From aeccae0a6181a9bfbf77b06daa19b96b97f40cd8 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 27 Mar 2026 22:45:25 +0700 Subject: [PATCH 01/42] feat: use deleteSurroundingText for app support --- src/lotus-engine.h | 14 ++++----- src/lotus-state.cpp | 74 +++++++++++++++++++++++++++++++-------------- src/lotus-state.h | 2 +- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/lotus-engine.h b/src/lotus-engine.h index 0b617e33..c73521e3 100644 --- a/src/lotus-engine.h +++ b/src/lotus-engine.h @@ -177,6 +177,13 @@ namespace fcitx { */ uintptr_t macroTable() const; + /** + * @brief Get name of current program + * @param ic Current input context. + * @return Name of current program + */ + std::string getProgramName(InputContext* ic); + /** * @brief Gets the emoji loader. * @return Reference to emoji loader instance. @@ -318,13 +325,6 @@ namespace fcitx { * @param ic Current input context. */ static void setMode(LotusMode mode, InputContext* ic); - - /** - * @brief Get name of current program - * @param ic Current input context. - * @return Name of current program - */ - static std::string getProgramName(InputContext* ic); }; /** diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 764ab813..829aca67 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -478,24 +478,49 @@ namespace fcitx { return false; } - void LotusState::performReplacement(const std::string& deletedPart, const std::string& addedPart) { + bool LotusState::performReplacement(const std::string& deletedPart, const std::string& addedPart) { LOTUS_INFO("Perform replacement: " + deletedPart + " -> " + addedPart); //NOLINT - int my_id = ++current_thread_id_; - current_backspace_count_ = 0; - pending_commit_string_ = addedPart; - const auto& surrounding = ic_->surroundingText(); - // Enable Autofill detection for all frontends (Wayland/IBus). - // This fixes the "toôi" duplication bug in Chromium-based search bars. - // The isAutofillCertain function has been optimized to differentiate - // between browser autofill and AI ghost text. - int autofillOffset = isAutofillCertain(surrounding) ? 1 : 0; - expected_backspaces_ = static_cast(utf8::length(deletedPart)) + 1 + autofillOffset; - replacement_thread_id_.store(my_id, std::memory_order_release); - replacement_start_ms_.store(now_ms(), std::memory_order_release); - is_deleting_.store(true, std::memory_order_release); - monitor_cv.notify_one(); - send_backspace_uinput(expected_backspaces_); - LOTUS_INFO("Send " + std::to_string(expected_backspaces_) + " backspaces"); + int my_id = ++current_thread_id_; + current_backspace_count_ = 0; + pending_commit_string_ = addedPart; + const auto& surrounding = ic_->surroundingText(); + int autofillOffset = isAutofillCertain(surrounding) ? 1 : 0; + expected_backspaces_ = static_cast(utf8::length(deletedPart)) + 1 + autofillOffset; + // Use deleteSurroundingText for apps that support it for smooth typing + if (engine_->getProgramName(ic_) == "soffice" && // Lmfao, only this work :> + surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) && + (surrounding.text()).back() != '\n' // firefox and discord insert '\n' into surrounding cause bug + && !(autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes + ) { + auto cur = static_cast(surrounding.cursor()); + const int bsCount = static_cast(utf8::length(deletedPart)); + if (autofillOffset) { + int surrLen = static_cast(utf8::length(surrounding.text())); + int realLen = static_cast(cur); + int suggestionLen = surrLen - realLen; + // delete suggestion tail + if (suggestionLen > 0) + ic_->deleteSurroundingText(0, 1); + // delete addedPart + if (bsCount > 0) + ic_->deleteSurroundingText(-bsCount, static_cast(bsCount)); + } else { + if (bsCount > 0) { + ic_->deleteSurroundingText(-bsCount, static_cast(bsCount)); + } + } + ic_->commitString(addedPart); + //clearAllBuffers(); + return true; + } else { + replacement_thread_id_.store(my_id, std::memory_order_release); + replacement_start_ms_.store(now_ms(), std::memory_order_release); + is_deleting_.store(true, std::memory_order_release); + monitor_cv.notify_one(); + send_backspace_uinput(expected_backspaces_); + LOTUS_INFO("Send " + std::to_string(expected_backspaces_) + " backspaces"); + } + return false; } bool LotusState::checkForwardSpecialKey(KeyEvent& keyEvent, KeySym& currentSym) { @@ -600,19 +625,20 @@ namespace fcitx { compareAndSplitStrings(oldPreBuffer_, commitStr, commonPrefix, deletedPart, addedPart); if (!deletedPart.empty()) { - performReplacement(deletedPart, addedPart); keyEvent.filterAndAccept(); + if (performReplacement(deletedPart, addedPart)) + keyEvent.forward(); } else { bool wasAutoCapitalized = (currentSym != keyEvent.rawKey().sym()); if (!addedPart.empty() && (keyUtf8 != addedPart || wasAutoCapitalized)) { // Prevent auto-capitalized character replacement from stripping out Vietnamese chars if (addedPart.size() > 1 && addedPart.back() == ' ') { // Stripping the trigger key (space) from addedPart -#if __cplusplus >= 202002L + #if __cplusplus >= 202002L addedPart.resize(addedPart.size() - 1); -#else + #else addedPart = addedPart.substr(0, addedPart.size() - 1); -#endif + #endif } ic_->commitString(addedPart); LOTUS_INFO("Commit: " + addedPart); @@ -629,7 +655,7 @@ namespace fcitx { return; } - if (!processed) { + if (!processed) { if (checkEmptyPreedit) { UniqueCPtr preeditC(EnginePullPreedit(lotusEngine_.handle())); if (!preeditC || (*preeditC.get() == 0)) { @@ -682,7 +708,8 @@ namespace fcitx { } keyEvent.filterAndAccept(); - performReplacement(deletedPart, addedPart); + if (performReplacement(deletedPart, addedPart)) + keyEvent.forward(); oldPreBuffer_ = preeditStr; } } @@ -1192,6 +1219,7 @@ namespace fcitx { } } performReplacement(deletedPart, addedPart); + oldPreBuffer_ = preeditStr; return; } diff --git a/src/lotus-state.h b/src/lotus-state.h index cdb2fc59..d68e7068 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -170,7 +170,7 @@ namespace fcitx { * @param deletedPart Text to delete. * @param addedPart Text to insert. */ - void performReplacement(const std::string& deletedPart, const std::string& addedPart); + bool performReplacement(const std::string& deletedPart, const std::string& addedPart); /** * @brief Handles the double space to period replacement. From 0097828a4e18ed09e8ac399e3203be3d2ff66a11 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 28 Mar 2026 22:37:46 +0700 Subject: [PATCH 02/42] Workaround for Chromium suggestions --- src/lotus-engine.cpp | 23 +++++++++++++++++------ src/lotus-state.cpp | 31 ++++++++++++++++++++----------- src/lotus-state.h | 1 + 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 5ab4c865..088ec485 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -366,21 +366,32 @@ namespace fcitx { auto* state = ic->propertyFor(&factory_); + // Workaround for chromium wayland issue where suggestions cause a doubled + // first character. Forwarding may prevent BS from being sent + // to the client. + // + // Note that with chromium x11 we can't do anything to fixes this because + // it not support surrounding text so can't know when it show suggestions + // + // TODO: Properly fixes instead ugly WA + state->wa_flag = false; + state->waitAck_ = false; if (*config_.fixUinputWithAck) { if (targetMode == LotusMode::Uinput || targetMode == LotusMode::UinputHC || targetMode == LotusMode::Smooth) { - if (is_dbus) { #if __cplusplus >= 202002L - std::ranges::transform(appName, appName.begin(), ::tolower); + std::ranges::transform(appName, appName.begin(), ::tolower); #else - std::transform(appName.begin(), appName.end(), appName.begin(), ::tolower); + std::transform(appName.begin(), appName.end(), appName.begin(), ::tolower); #endif - for (const auto& ackApp : ack_apps) { - if (appName.find(ackApp) != std::string::npos) { + for (const auto& ackApp : ack_apps) { + if (appName.find(ackApp) != std::string::npos) { + if (is_dbus) { state->waitAck_ = true; LOTUS_INFO(ackApp + " detected, waiting for ack"); - break; } + state->wa_flag = true; + break; } } } diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 829aca67..315d9fe9 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -11,6 +11,7 @@ #include "lotus-candidates.h" #include "lotus-utils.h" #include "lotus.h" +#include "ack-apps.h" #include #include @@ -677,22 +678,30 @@ namespace fcitx { std::string commonPrefix; std::string deletedPart; std::string addedPart; + + if (wa_flag) + keyEvent.filterAndAccept(); + if (compareAndSplitStrings(oldPreBuffer_, preeditStr, commonPrefix, deletedPart, addedPart) != 0) { if (deletedPart.empty()) { bool isCommit = false; bool wasAutoCapitalized = (currentSym != keyEvent.rawKey().sym()); if (!addedPart.empty()) { - oldPreBuffer_ = preeditStr; - if (addedPart != keyUtf8 || wasAutoCapitalized) { + if (wa_flag) ic_->commitString(addedPart); - LOTUS_INFO("Commit: " + addedPart); - keyEvent.filterAndAccept(); - isCommit = true; - } - } - if (!isCommit) { - keyEvent.forward(); + oldPreBuffer_ = preeditStr; + if (!wa_flag) + if (wasAutoCapitalized || addedPart != keyUtf8) { + LOTUS_INFO("Commit: " + addedPart); + ic_->commitString(addedPart); + keyEvent.filterAndAccept(); + isCommit = true; + } } + if (!wa_flag) + if (!isCommit) { + keyEvent.forward(); + } } else { if (uinput_client_fd_ < 0) { LOTUS_ERROR("Cannot connect to uinput server, commit rawkey"); @@ -706,8 +715,8 @@ namespace fcitx { if (is_deleting_.load()) { is_deleting_.store(false, std::memory_order_release); } - - keyEvent.filterAndAccept(); + if (!wa_flag) + keyEvent.filterAndAccept(); if (performReplacement(deletedPart, addedPart)) keyEvent.forward(); oldPreBuffer_ = preeditStr; diff --git a/src/lotus-state.h b/src/lotus-state.h index d68e7068..5b917665 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -106,6 +106,7 @@ namespace fcitx { bool shouldCapitalize_ = false; bool isPrevPunctuation_ = false; int64_t lastDeactivateTime_ = 0; + bool wa_flag = false; /** * @brief Connects to the uinput server. From a53bd910ff5dd1a70d1abbbe78c4c012e7223b02 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 29 Mar 2026 02:21:07 +0700 Subject: [PATCH 03/42] personal config --- src/ack-apps.h | 6 ++++++ src/lotus-engine.cpp | 10 ++++++++-- src/lotus-state.cpp | 4 ++-- src/lotus-state.h | 1 + 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/ack-apps.h b/src/ack-apps.h index 06ca67b7..e15a522d 100644 --- a/src/ack-apps.h +++ b/src/ack-apps.h @@ -21,3 +21,9 @@ * Chromium-based browsers that need special handling for text replacement. */ static std::vector ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex"}; + +/** + * @brief List of application names have goood support surrowding text + * + */ +static std::vector surrtp_apps = {"mullvad", "soffice"}; diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 088ec485..fe69d4c8 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -374,8 +374,8 @@ namespace fcitx { // it not support surrounding text so can't know when it show suggestions // // TODO: Properly fixes instead ugly WA - state->wa_flag = false; - + state->wa_flag = false; + state->surrtp = true; state->waitAck_ = false; if (*config_.fixUinputWithAck) { if (targetMode == LotusMode::Uinput || targetMode == LotusMode::UinputHC || targetMode == LotusMode::Smooth) { @@ -394,6 +394,12 @@ namespace fcitx { break; } } + for (const auto& _App : surrtp_app) { + if (appName.find(_App) != std::string::npos) { + state->surrtp = true; + break; + } + } } } if (event.type() == EventType::InputContextFocusIn && is_dbus && !surrvalid) { diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 315d9fe9..9ee02276 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -488,8 +488,8 @@ namespace fcitx { int autofillOffset = isAutofillCertain(surrounding) ? 1 : 0; expected_backspaces_ = static_cast(utf8::length(deletedPart)) + 1 + autofillOffset; // Use deleteSurroundingText for apps that support it for smooth typing - if (engine_->getProgramName(ic_) == "soffice" && // Lmfao, only this work :> - surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) && + if (surrtp // Lmfao, only this work :> + && surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) && (surrounding.text()).back() != '\n' // firefox and discord insert '\n' into surrounding cause bug && !(autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes ) { diff --git a/src/lotus-state.h b/src/lotus-state.h index 5b917665..aaa7257b 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -107,6 +107,7 @@ namespace fcitx { bool isPrevPunctuation_ = false; int64_t lastDeactivateTime_ = 0; bool wa_flag = false; + bool surrtp = false; /** * @brief Connects to the uinput server. From 6be0dd0ba2b26d469fc8e65ba26110ba7b4c8ef6 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 29 Mar 2026 02:22:10 +0700 Subject: [PATCH 04/42] typo --- src/lotus-engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index fe69d4c8..bec6d31c 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -394,7 +394,7 @@ namespace fcitx { break; } } - for (const auto& _App : surrtp_app) { + for (const auto& _App : surrtp_apps) { if (appName.find(_App) != std::string::npos) { state->surrtp = true; break; From 3bc5943d5e02ec7b2668b0603d9df5237cf9a8e2 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 29 Mar 2026 04:03:25 +0700 Subject: [PATCH 05/42] fix typo false :v, and reomve some func --- src/{ack-apps.h => app_quirks.h} | 9 +++++---- src/lotus-engine.cpp | 7 +++---- src/lotus-state.cpp | 26 ++++++++++++-------------- 3 files changed, 20 insertions(+), 22 deletions(-) rename src/{ack-apps.h => app_quirks.h} (59%) diff --git a/src/ack-apps.h b/src/app_quirks.h similarity index 59% rename from src/ack-apps.h rename to src/app_quirks.h index e15a522d..30d6ac1f 100644 --- a/src/ack-apps.h +++ b/src/app_quirks.h @@ -6,24 +6,25 @@ */ /** - * @file ack-apps.h + * @file app_quirks.h * @brief List of applications requiring acknowledgment workaround. * * These browsers need special handling for uinput mode to work correctly. */ #include -#include +#include /** * @brief List of application names requiring ACK workaround. * * Chromium-based browsers that need special handling for text replacement. */ -static std::vector ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex"}; +inline constexpr std::array ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", + "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex"}; /** * @brief List of application names have goood support surrowding text * */ -static std::vector surrtp_apps = {"mullvad", "soffice"}; +inline constexpr std::array surrtp_apps = {"soffice"}; diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index bec6d31c..dda32e53 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -12,7 +12,7 @@ #include "lotus-candidates.h" #include "lotus-monitor.h" #include "lotus-utils.h" -#include "ack-apps.h" +#include "app_quirks.h" #include #include #ifndef DISABLE_VERSION_ACTION @@ -375,7 +375,7 @@ namespace fcitx { // // TODO: Properly fixes instead ugly WA state->wa_flag = false; - state->surrtp = true; + state->surrtp = false; state->waitAck_ = false; if (*config_.fixUinputWithAck) { if (targetMode == LotusMode::Uinput || targetMode == LotusMode::UinputHC || targetMode == LotusMode::Smooth) { @@ -388,7 +388,7 @@ namespace fcitx { if (appName.find(ackApp) != std::string::npos) { if (is_dbus) { state->waitAck_ = true; - LOTUS_INFO(ackApp + " detected, waiting for ack"); + LOTUS_INFO(std::string(ackApp) + " detected, waiting for ack"); } state->wa_flag = true; break; @@ -621,7 +621,6 @@ namespace fcitx { auto* state = ic->propertyFor(&factory_); const bool surrvalid = ic->surroundingText().isValid(); const bool is_dbus = getFrontendName(ic) == "dbus"; - state->lastDeactivateTime_ = now_ms(); if (realMode == LotusMode::Preedit && event.type() != EventType::InputContextFocusOut) { state->commitBuffer(); } else { diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 9ee02276..9fa639b3 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -456,9 +456,9 @@ namespace fcitx { if (surr.isValid() && surr.cursor() == realtextLen.load(std::memory_order_acquire)) { LOTUS_INFO("Skip retry"); } else { - // Retry x3 (2 ms each), khi can (chromium,electron,...) - for (int retry = 0; retry < 3; ++retry) { - std::this_thread::sleep_for(std::chrono::milliseconds(2)); + // Retry x5 (1 ms each), khi can (chromium,electron,...) + for (int retry = 0; retry < 5; ++retry) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); const auto& surr2 = ic_->surroundingText(); if (surr2.isValid() && surr2.cursor() == realtextLen.load(std::memory_order_acquire)) { break; @@ -472,8 +472,8 @@ namespace fcitx { pending_commit_string_ = ""; event.filterAndAccept(); // Filter out the final trigger backspace. - if (getFrontendName(ic_) == "dbus" && !ic_->surroundingText().isValid()) - replayBufferedKeys(); // Does we need drop this? + //if (getFrontendName(ic_) == "dbus" && !ic_->surroundingText().isValid()) + // replayBufferedKeys(); // Does we need drop this? return true; } return false; @@ -932,8 +932,8 @@ namespace fcitx { } replacement_thread_id_.store(0, std::memory_order_release); replacement_start_ms_.store(0, std::memory_order_release); - if (getFrontendName(ic_) == "dbus" && !ic_->surroundingText().isValid()) - replayBufferedKeys(); // Does we need drop this? + //if (getFrontendName(ic_) == "dbus" && !ic_->surroundingText().isValid()) + // replayBufferedKeys(); // Does we need drop this? } KeySym currentSym = keyEvent.rawKey().sym(); if (*engine_->config().autoCapitalizeAfterPunctuation && realMode != LotusMode::Off) { @@ -1125,11 +1125,9 @@ namespace fcitx { } oldPreBuffer_.clear(); hasHistory_ = false; - if (!is_deleting_.load(std::memory_order_acquire)) { - expected_backspaces_ = 0; - current_backspace_count_ = 0; - pending_commit_string_.clear(); - } + expected_backspaces_ = 0; + current_backspace_count_ = 0; + pending_commit_string_.clear(); emojiBuffer_.clear(); emojiCandidates_.clear(); buffered_keys_.clear(); @@ -1142,14 +1140,13 @@ namespace fcitx { bool LotusState::isEmptyHistory() const { return !hasHistory_; } - + /* void LotusState::replayBufferedKeys() { LOTUS_INFO("Starting replay buffered keys"); if (buffered_keys_.empty()) { return; } auto keys = std::move(buffered_keys_); - buffered_keys_.clear(); for (size_t i = 0; i < keys.size(); ++i) { auto sym = static_cast(keys[i].sym); uint32_t state = keys[i].state; @@ -1236,4 +1233,5 @@ namespace fcitx { } LOTUS_INFO("Replay buffered keys done"); } +*/ } // namespace fcitx From 6d2a0a3888ab17a4aca8f58d3377a2b08c0af7a8 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 29 Mar 2026 22:37:11 +0700 Subject: [PATCH 06/42] only need retry for chromium,electron ack --- src/lotus-engine.cpp | 8 ++++---- src/lotus-state.cpp | 24 ++++++++++++++++++------ src/lotus-state.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index dda32e53..4d6e444d 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -617,10 +617,10 @@ namespace fcitx { } void LotusEngine::deactivate(const InputMethodEntry& /*entry*/, InputContextEvent& event) { - auto* ic = event.inputContext(); - auto* state = ic->propertyFor(&factory_); - const bool surrvalid = ic->surroundingText().isValid(); - const bool is_dbus = getFrontendName(ic) == "dbus"; + auto* ic = event.inputContext(); + auto* state = ic->propertyFor(&factory_); + const bool surrvalid = ic->surroundingText().isValid(); + const bool is_dbus = getFrontendName(ic) == "dbus"; if (realMode == LotusMode::Preedit && event.type() != EventType::InputContextFocusOut) { state->commitBuffer(); } else { diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 9fa639b3..8738fdad 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -139,6 +139,16 @@ namespace fcitx { } } + void LotusState::send_backspace_forward(int count) const { + if (count <= 0) + return; + for (int i = 0; i < count - 1; ++i) { + ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), false); + ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), true); + } + send_backspace_uinput(0); // trigger 1bs to make all bs prev release + } + bool LotusState::isAutofillCertain(const SurroundingText& s) { if (!s.isValid() || oldPreBuffer_.empty()) { return false; @@ -457,13 +467,14 @@ namespace fcitx { LOTUS_INFO("Skip retry"); } else { // Retry x5 (1 ms each), khi can (chromium,electron,...) - for (int retry = 0; retry < 5; ++retry) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - const auto& surr2 = ic_->surroundingText(); - if (surr2.isValid() && surr2.cursor() == realtextLen.load(std::memory_order_acquire)) { - break; + if (waitAck_) + for (int retry = 0; retry < 5; ++retry) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + const auto& surr2 = ic_->surroundingText(); + if (surr2.isValid() && surr2.cursor() == realtextLen.load(std::memory_order_acquire)) { + break; + } } - } } ic_->commitString(pending_commit_string_); LOTUS_INFO("Commit: " + pending_commit_string_); @@ -518,6 +529,7 @@ namespace fcitx { replacement_start_ms_.store(now_ms(), std::memory_order_release); is_deleting_.store(true, std::memory_order_release); monitor_cv.notify_one(); + //send_backspace_forward(expected_backspaces_ - 1); send_backspace_uinput(expected_backspaces_); LOTUS_INFO("Send " + std::to_string(expected_backspaces_) + " backspaces"); } diff --git a/src/lotus-state.h b/src/lotus-state.h index aaa7257b..ebb9849d 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -126,7 +126,7 @@ namespace fcitx { * @param count Number of backspaces to send. */ void send_backspace_uinput(int count) const; - + void send_backspace_forward(int count) const; /** * @brief Checks if autofill is certain for surrounding text. * @param s The surrounding text. From 3ecdc6dddac28d9b3ad46138649448e57169c0ae Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Mon, 30 Mar 2026 19:22:04 +0700 Subject: [PATCH 07/42] demorgan --- src/lotus-state.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 8738fdad..7202b008 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -462,12 +462,10 @@ namespace fcitx { replacement_thread_id_.store(0, std::memory_order_release); std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); // Validate surr cursor pos should match realtextLen after all BS applied - const auto& surr = ic_->surroundingText(); - if (surr.isValid() && surr.cursor() == realtextLen.load(std::memory_order_acquire)) { - LOTUS_INFO("Skip retry"); - } else { - // Retry x5 (1 ms each), khi can (chromium,electron,...) - if (waitAck_) + if (waitAck_) { + const auto& surr = ic_->surroundingText(); + if (!surr.isValid() || surr.cursor() != realtextLen.load(std::memory_order_acquire)) { + // Retry x5 (1 ms each), khi can (chromium,electron,...) for (int retry = 0; retry < 5; ++retry) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); const auto& surr2 = ic_->surroundingText(); @@ -475,6 +473,7 @@ namespace fcitx { break; } } + } } ic_->commitString(pending_commit_string_); LOTUS_INFO("Commit: " + pending_commit_string_); From adb26a0ee49fb94dceb988731478077d19e30400 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Mon, 30 Mar 2026 19:30:04 +0700 Subject: [PATCH 08/42] e --- src/lotus-state.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 7202b008..ec277388 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -11,7 +11,6 @@ #include "lotus-candidates.h" #include "lotus-utils.h" #include "lotus.h" -#include "ack-apps.h" #include #include @@ -646,11 +645,11 @@ namespace fcitx { // Prevent auto-capitalized character replacement from stripping out Vietnamese chars if (addedPart.size() > 1 && addedPart.back() == ' ') { // Stripping the trigger key (space) from addedPart - #if __cplusplus >= 202002L +#if __cplusplus >= 202002L addedPart.resize(addedPart.size() - 1); - #else +#else addedPart = addedPart.substr(0, addedPart.size() - 1); - #endif +#endif } ic_->commitString(addedPart); LOTUS_INFO("Commit: " + addedPart); @@ -667,7 +666,7 @@ namespace fcitx { return; } - if (!processed) { + if (!processed) { if (checkEmptyPreedit) { UniqueCPtr preeditC(EnginePullPreedit(lotusEngine_.handle())); if (!preeditC || (*preeditC.get() == 0)) { @@ -1135,7 +1134,7 @@ namespace fcitx { return; } oldPreBuffer_.clear(); - hasHistory_ = false; + hasHistory_ = false; expected_backspaces_ = 0; current_backspace_count_ = 0; pending_commit_string_.clear(); From eecf79595e4613aa2317fddd7fc8eb27d8333f08 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Mon, 30 Mar 2026 20:22:27 +0700 Subject: [PATCH 09/42] O(1) --- bamboo/bamboo-c.go | 3 ++- bamboo/bamboo-core | 2 +- bamboo/fcitxbambooengine.go | 16 ++++++++++++++-- src/lotus-engine.h | 14 +++++++------- src/lotus-state.cpp | 11 +++++++++++ 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/bamboo/bamboo-c.go b/bamboo/bamboo-c.go index 1965f7b6..2e75d4a1 100644 --- a/bamboo/bamboo-c.go +++ b/bamboo/bamboo-c.go @@ -158,6 +158,7 @@ func NewEngine(name *C.cchar, dictHandle uintptr, tableHandle uintptr) uintptr { timeFormat: "%H:%M", dateFormat: "%d/%m/%Y", } + engine.rebuildAppendingKeySet() return uintptr(cgo.NewHandle(engine)) } @@ -201,7 +202,7 @@ func NewCustomEngine(definition **C.char, dictHandle uintptr, tableHandle uintpt timeFormat: "%H:%M", dateFormat: "%d/%m/%Y", } - + engine.rebuildAppendingKeySet() return uintptr(cgo.NewHandle(engine)) } diff --git a/bamboo/bamboo-core b/bamboo/bamboo-core index de3d5b08..5f1974ac 160000 --- a/bamboo/bamboo-core +++ b/bamboo/bamboo-core @@ -1 +1 @@ -Subproject commit de3d5b08c52b8862b38b75fea67baf0631564df1 +Subproject commit 5f1974ac5eb6a540fdb05887dcd21131137f1605 diff --git a/bamboo/fcitxbambooengine.go b/bamboo/fcitxbambooengine.go index c0dcbf6c..b4ba24d5 100644 --- a/bamboo/fcitxbambooengine.go +++ b/bamboo/fcitxbambooengine.go @@ -17,6 +17,7 @@ import ( type FcitxBambooEngine struct { preeditor bamboo.IEngine + appendingKeySet map[rune]struct{} macroTable *MacroTable dictionary map[string]bool autoNonVnRestore bool @@ -208,6 +209,14 @@ func (e *FcitxBambooEngine) getBambooInputMode() bamboo.Mode { return bamboo.VietnameseMode } +func (e *FcitxBambooEngine) rebuildAppendingKeySet() { + keys := e.preeditor.GetInputMethod().AppendingKeys + e.appendingKeySet = make(map[rune]struct{}, len(keys)) + for _, k := range keys { + e.appendingKeySet[k] = struct{}{} + } +} + func inKeyList(list []rune, key rune) bool { for _, s := range list { if s == key { @@ -225,7 +234,10 @@ func (e *FcitxBambooEngine) toUpper(keyRune rune) rune { '}': ']', } - if upperSpecialKey, found := keyMapping[keyRune]; found && inKeyList(e.preeditor.GetInputMethod().AppendingKeys, keyRune) { + if upperSpecialKey, found := keyMapping[keyRune]; found { + if _, ok := e.appendingKeySet[keyRune]; !ok { + return keyRune + } keyRune = upperSpecialKey } return keyRune @@ -272,7 +284,7 @@ func (e *FcitxBambooEngine) getCommitText(keyVal, state uint32) (string, bool) { keyRune = e.toUpper(keyRune) } e.preeditor.ProcessKey(keyRune, e.getBambooInputMode()) - if inKeyList(e.preeditor.GetInputMethod().AppendingKeys, keyRune) { + if _, ok := e.appendingKeySet[keyRune]; ok { var newText string if e.shouldFallbackToEnglish(true) { newText = e.getProcessedString(bamboo.EnglishMode) diff --git a/src/lotus-engine.h b/src/lotus-engine.h index c73521e3..8f10cf22 100644 --- a/src/lotus-engine.h +++ b/src/lotus-engine.h @@ -177,13 +177,6 @@ namespace fcitx { */ uintptr_t macroTable() const; - /** - * @brief Get name of current program - * @param ic Current input context. - * @return Name of current program - */ - std::string getProgramName(InputContext* ic); - /** * @brief Gets the emoji loader. * @return Reference to emoji loader instance. @@ -325,6 +318,13 @@ namespace fcitx { * @param ic Current input context. */ static void setMode(LotusMode mode, InputContext* ic); + + /** + * @brief Get name of current program + * @param ic Current input context. + * @return Name of current program + */ + std::string getProgramName(InputContext* ic); }; /** diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index ec277388..1dc6808f 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -711,6 +711,17 @@ namespace fcitx { if (!wa_flag) if (!isCommit) { keyEvent.forward(); + bool hasMultibyte = false; + for (unsigned char c : oldPreBuffer_) + if (c > 0x7F) { + hasMultibyte = true; + break; + } + if (!hasMultibyte && utf8::length(oldPreBuffer_) > 8) { + ResetEngine(lotusEngine_.handle()); + hasHistory_ = false; + oldPreBuffer_.clear(); + } } } else { if (uinput_client_fd_ < 0) { From 6f85446b76b7ee9a8b61fc7a98c20470b2fdaa7a Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 2 May 2026 11:00:06 +0700 Subject: [PATCH 10/42] e Signed-off-by: Zebra2711 --- CMakeLists.txt | 6 +- bamboo/bamboo-c.go | 93 +- bamboo/fcitxbambooengine.go | 2 +- src/lotus-state.cpp | 63 +- unikey/CMakeLists.txt | 27 + unikey/LotusUnikeyEngine.cpp | 82 + unikey/LotusUnikeyEngine.hpp | 63 + unikey/core/byteio.cpp | 479 +++++ unikey/core/byteio.h | 179 ++ unikey/core/charset.cpp | 1247 ++++++++++++ unikey/core/charset.h | 293 +++ unikey/core/convert.cpp | 231 +++ unikey/core/data.cpp | 1792 +++++++++++++++++ unikey/core/data.h | 18 + unikey/core/inputproc.cpp | 304 +++ unikey/core/inputproc.h | 113 ++ unikey/core/keycons.h | 69 + unikey/core/mactab.cpp | 319 +++ unikey/core/mactab.h | 60 + unikey/core/pattern.cpp | 80 + unikey/core/pattern.h | 52 + unikey/core/ukengine.cpp | 3002 ++++++++++++++++++++++++++++ unikey/core/ukengine.h | 147 ++ unikey/core/unikeyinputcontext.cpp | 128 ++ unikey/core/unikeyinputcontext.h | 87 + unikey/core/usrkeymap.cpp | 180 ++ unikey/core/usrkeymap.h | 19 + unikey/core/vnconv.h | 109 + unikey/core/vnlexi.h | 310 +++ 29 files changed, 9519 insertions(+), 35 deletions(-) create mode 100644 unikey/CMakeLists.txt create mode 100644 unikey/LotusUnikeyEngine.cpp create mode 100644 unikey/LotusUnikeyEngine.hpp create mode 100644 unikey/core/byteio.cpp create mode 100644 unikey/core/byteio.h create mode 100644 unikey/core/charset.cpp create mode 100644 unikey/core/charset.h create mode 100644 unikey/core/convert.cpp create mode 100644 unikey/core/data.cpp create mode 100644 unikey/core/data.h create mode 100644 unikey/core/inputproc.cpp create mode 100644 unikey/core/inputproc.h create mode 100644 unikey/core/keycons.h create mode 100644 unikey/core/mactab.cpp create mode 100644 unikey/core/mactab.h create mode 100644 unikey/core/pattern.cpp create mode 100644 unikey/core/pattern.h create mode 100644 unikey/core/ukengine.cpp create mode 100644 unikey/core/ukengine.h create mode 100644 unikey/core/unikeyinputcontext.cpp create mode 100644 unikey/core/unikeyinputcontext.h create mode 100644 unikey/core/usrkeymap.cpp create mode 100644 unikey/core/usrkeymap.h create mode 100644 unikey/core/vnconv.h create mode 100644 unikey/core/vnlexi.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c7adcacd..5acfbb37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ include(GNUInstallDirs) include(ECMUninstallTarget) option(ENABLE_QT "Enable Qt based GUI" On) +option(ENABLE_LOTUS_UNIKEY_ENGINE "Build Unikey core + LotusUnikeyEngine bridge in unikey/ (optional Bamboo replacement)" Off) find_package(Fcitx5Core ${REQUIRED_FCITX_VERSION} REQUIRED) find_package(Fcitx5ModuleEmoji REQUIRED) @@ -38,6 +39,9 @@ else() endif() fcitx5_add_i18n_definition() +if(ENABLE_LOTUS_UNIKEY_ENGINE) + add_subdirectory(unikey) +endif() add_subdirectory(po) add_subdirectory(bamboo) add_subdirectory(src) @@ -70,4 +74,4 @@ install(FILES DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/${PROJECT_NAME} ) -feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) \ No newline at end of file +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/bamboo/bamboo-c.go b/bamboo/bamboo-c.go index 2e75d4a1..0846fe17 100644 --- a/bamboo/bamboo-c.go +++ b/bamboo/bamboo-c.go @@ -35,17 +35,55 @@ import ( import ( "bufio" "os" + "runtime" + "runtime/debug" "sort" "strings" + "sync/atomic" ) +// If FCITX_LOTUS_LOCK_OSTHREAD is set (any non-empty value), bind each CGO callback +// goroutine to its OS thread to reduce scheduler/TLS churn at the C↔Go boundary. +var lockOsThreadEnabled uint32 + +func lockOSThreadForCgo() { + if atomic.LoadUint32(&lockOsThreadEnabled) != 0 { + runtime.LockOSThread() + } +} + //export Init func Init() { signal.Ignore(syscall.SIGPIPE) + debug.SetGCPercent(200) + if os.Getenv("FCITX_LOTUS_LOCK_OSTHREAD") != "" { + atomic.StoreUint32(&lockOsThreadEnabled, 1) + } +} + +func enginePullCommitCString(e *FcitxBambooEngine) *C.char { + commitText := e.commitText + e.commitText = "" + if commitText == "" { + return nil + } + encodedText := bamboo.Encode(e.outputCharset, commitText) + if encodedText == "" { + return nil + } + return C.CString(encodedText) +} + +func enginePullPreeditCString(e *FcitxBambooEngine) *C.char { + if e.preeditText == "" { + return nil + } + return C.CString(e.preeditText) } //export EngineProcessKeyEvent func EngineProcessKeyEvent(engine uintptr, keyVal, state uint32) bool { + lockOSThreadForCgo() bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) if !ok { return false @@ -53,6 +91,52 @@ func EngineProcessKeyEvent(engine uintptr, keyVal, state uint32) bool { return bambooEngine.preeditProcessKeyEvent(keyVal, state) } +//export EngineProcessKeyEventAndPull +// Runs one key event then returns commit and/or preedit in one CGO transition. +// Pass nil for commitOut or preeditOut if that string is not needed. +func EngineProcessKeyEventAndPull(engine uintptr, keyVal, state uint32, commitOut, preeditOut **C.char) bool { + lockOSThreadForCgo() + if commitOut != nil { + *commitOut = nil + } + if preeditOut != nil { + *preeditOut = nil + } + bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) + if !ok { + return false + } + processed := bambooEngine.preeditProcessKeyEvent(keyVal, state) + if commitOut != nil { + *commitOut = enginePullCommitCString(bambooEngine) + } + if preeditOut != nil { + *preeditOut = enginePullPreeditCString(bambooEngine) + } + return processed +} + +//export EnginePullCommitAndPreedit +func EnginePullCommitAndPreedit(engine uintptr, commitOut, preeditOut **C.char) { + lockOSThreadForCgo() + if commitOut != nil { + *commitOut = nil + } + if preeditOut != nil { + *preeditOut = nil + } + bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) + if !ok { + return + } + if commitOut != nil { + *commitOut = enginePullCommitCString(bambooEngine) + } + if preeditOut != nil { + *preeditOut = enginePullPreeditCString(bambooEngine) + } +} + //export EngineSetRestoreKeyStroke func EngineSetRestoreKeyStroke(engine uintptr) { bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) @@ -64,11 +148,12 @@ func EngineSetRestoreKeyStroke(engine uintptr) { //export EnginePullPreedit func EnginePullPreedit(engine uintptr) *C.char { + lockOSThreadForCgo() bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) if !ok { return nil } - return C.CString(bambooEngine.preeditText) + return enginePullPreeditCString(bambooEngine) } //export EngineCommitPreedit @@ -82,14 +167,12 @@ func EngineCommitPreedit(engine uintptr) { //export EnginePullCommit func EnginePullCommit(engine uintptr) *C.char { + lockOSThreadForCgo() bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) if !ok { return nil } - var commitText = bambooEngine.commitText - bambooEngine.commitText = "" - encodedText := bamboo.Encode(bambooEngine.outputCharset, commitText) - return C.CString(encodedText) + return enginePullCommitCString(bambooEngine) } //export EngineSetOption diff --git a/bamboo/fcitxbambooengine.go b/bamboo/fcitxbambooengine.go index b4ba24d5..77a79660 100644 --- a/bamboo/fcitxbambooengine.go +++ b/bamboo/fcitxbambooengine.go @@ -352,7 +352,7 @@ func (e *FcitxBambooEngine) commitPreeditAndReset(s string) { func (e *FcitxBambooEngine) updatePreedit(processedStr string) { var encodedStr = e.encodeText(processedStr) - var preeditLen = uint32(len([]rune(encodedStr))) + var preeditLen = uint32(utf8.RuneCountInString(encodedStr)) if preeditLen == 0 { e.preeditText = "" e.commitText = "" diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 1dc6808f..96c9e8cf 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -197,16 +197,18 @@ namespace fcitx { } void LotusState::handlePreeditMode(KeyEvent& keyEvent, KeySym currentSym) { - if (EngineProcessKeyEvent(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states()) != 0U) + char* commitRaw = nullptr; + char* preeditRaw = nullptr; + bool processed = EngineProcessKeyEventAndPull(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states(), &commitRaw, &preeditRaw) != 0U; + UniqueCPtr commit(commitRaw); + UniqueCPtr preedit(preeditRaw); + if (processed) keyEvent.filterAndAccept(); - if (auto commit = UniqueCPtr(EnginePullCommit(lotusEngine_.handle()))) { - if (commit && (*commit.get() != 0)) { - LOTUS_INFO("Commit: " + std::string(commit.get())); - ic_->commitString(commit.get()); - } + if (commit && (*commit.get() != 0)) { + LOTUS_INFO("Commit: " + std::string(commit.get())); + ic_->commitString(commit.get()); } ic_->inputPanel().reset(); - UniqueCPtr preedit(EnginePullPreedit(lotusEngine_.handle())); if (preedit && (*preedit.get() != 0)) { std::string_view view = preedit.get(); Text text; @@ -606,9 +608,10 @@ namespace fcitx { if (isBackspace(currentSym) || currentSym == FcitxKey_Return) { if (isBackspace(currentSym)) { - hasHistory_ = true; - EngineProcessKeyEvent(lotusEngine_.handle(), FcitxKey_BackSpace, 0); - UniqueCPtr preeditC(EnginePullPreedit(lotusEngine_.handle())); + hasHistory_ = true; + char* preeditBs = nullptr; + EngineProcessKeyEventAndPull(lotusEngine_.handle(), FcitxKey_BackSpace, 0, nullptr, &preeditBs); + UniqueCPtr preeditC(preeditBs); oldPreBuffer_ = (preeditC && (*preeditC.get() != 0)) ? preeditC.get() : ""; } else { hasHistory_ = false; @@ -625,9 +628,12 @@ namespace fcitx { return; } - bool processed = EngineProcessKeyEvent(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states()) != 0U; + char* commitRaw = nullptr; + char* preeditRaw = nullptr; + bool processed = EngineProcessKeyEventAndPull(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states(), &commitRaw, &preeditRaw) != 0U; + UniqueCPtr commitF(commitRaw); + UniqueCPtr preeditC(preeditRaw); - auto commitF = UniqueCPtr(EnginePullCommit(lotusEngine_.handle())); if (commitF && (*commitF.get() != 0)) { std::string commitStr = commitF.get(); std::string commonPrefix; @@ -637,8 +643,7 @@ namespace fcitx { if (!deletedPart.empty()) { keyEvent.filterAndAccept(); - if (performReplacement(deletedPart, addedPart)) - keyEvent.forward(); + performReplacement(deletedPart, addedPart); } else { bool wasAutoCapitalized = (currentSym != keyEvent.rawKey().sym()); if (!addedPart.empty() && (keyUtf8 != addedPart || wasAutoCapitalized)) { @@ -668,7 +673,6 @@ namespace fcitx { if (!processed) { if (checkEmptyPreedit) { - UniqueCPtr preeditC(EnginePullPreedit(lotusEngine_.handle())); if (!preeditC || (*preeditC.get() == 0)) { hasHistory_ = false; ResetEngine(lotusEngine_.handle()); @@ -682,12 +686,11 @@ namespace fcitx { hasHistory_ = true; realtextLen.fetch_add(1, std::memory_order_acq_rel); - UniqueCPtr preeditC(EnginePullPreedit(lotusEngine_.handle())); - std::string preeditStr = (preeditC && (*preeditC.get() != 0)) ? preeditC.get() : ""; + std::string preeditStr = (preeditC && (*preeditC.get() != 0)) ? preeditC.get() : ""; - std::string commonPrefix; - std::string deletedPart; - std::string addedPart; + std::string commonPrefix; + std::string deletedPart; + std::string addedPart; if (wa_flag) keyEvent.filterAndAccept(); @@ -738,8 +741,7 @@ namespace fcitx { } if (!wa_flag) keyEvent.filterAndAccept(); - if (performReplacement(deletedPart, addedPart)) - keyEvent.forward(); + performReplacement(deletedPart, addedPart); oldPreBuffer_ = preeditStr; } } @@ -824,10 +826,13 @@ namespace fcitx { return; } - auto commitPtr = UniqueCPtr(EnginePullCommit(lotusEngine_.handle())); - auto preeditPtr = UniqueCPtr(EnginePullPreedit(lotusEngine_.handle())); + char* commitP = nullptr; + char* preeditP = nullptr; + EnginePullCommitAndPreedit(lotusEngine_.handle(), &commitP, &preeditP); + UniqueCPtr commitPtr(commitP); + UniqueCPtr preeditPtr(preeditP); - std::string newWord; + std::string newWord; if (commitPtr && (*commitPtr.get() != 0)) newWord += commitPtr.get(); if (preeditPtr && (*preeditPtr.get() != 0)) @@ -869,10 +874,12 @@ namespace fcitx { void LotusState::processNormalKey(KeyEvent& keyEvent, KeySym currentSym) { auto* ic = keyEvent.inputContext(); ResetEngine(lotusEngine_.handle()); - bool processed = EngineProcessKeyEvent(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states()) != 0U; + char* commitP = nullptr; + char* preeditP = nullptr; + bool processed = EngineProcessKeyEventAndPull(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states(), &commitP, &preeditP) != 0U; + UniqueCPtr commitPtr(commitP); + UniqueCPtr preeditPtr(preeditP); if (processed) { - auto commitPtr = UniqueCPtr(EnginePullCommit(lotusEngine_.handle())); - auto preeditPtr = UniqueCPtr(EnginePullPreedit(lotusEngine_.handle())); std::string out; if (commitPtr && (*commitPtr.get() != 0)) out += commitPtr.get(); diff --git a/unikey/CMakeLists.txt b/unikey/CMakeLists.txt new file mode 100644 index 00000000..8e0cefcb --- /dev/null +++ b/unikey/CMakeLists.txt @@ -0,0 +1,27 @@ +# Vendored Unikey engine (core/) + Lotus wrapper. Optional Bamboo replacement. +# SPDX-FileCopyrightText: Unikey authors (LGPL/GPL); Lotus wrapper GPL-3.0-or-later +set(_UK_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") +set(LOTUS_UNIKEY_CORE_SRCS + ${_UK_CORE}/byteio.cpp + ${_UK_CORE}/charset.cpp + ${_UK_CORE}/convert.cpp + ${_UK_CORE}/data.cpp + ${_UK_CORE}/inputproc.cpp + ${_UK_CORE}/mactab.cpp + ${_UK_CORE}/pattern.cpp + ${_UK_CORE}/ukengine.cpp + ${_UK_CORE}/usrkeymap.cpp + ${_UK_CORE}/unikeyinputcontext.cpp +) +add_library(lotus-unikey-core STATIC ${LOTUS_UNIKEY_CORE_SRCS}) +set_target_properties(lotus-unikey-core PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_link_libraries(lotus-unikey-core PUBLIC Fcitx5::Utils) +target_include_directories(lotus-unikey-core PUBLIC "${_UK_CORE}") +add_library(lotus-unikey-bridge STATIC + "${CMAKE_CURRENT_SOURCE_DIR}/LotusUnikeyEngine.cpp" +) +target_link_libraries(lotus-unikey-bridge PUBLIC lotus-unikey-core) +target_include_directories(lotus-unikey-bridge PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${_UK_CORE}" +) diff --git a/unikey/LotusUnikeyEngine.cpp b/unikey/LotusUnikeyEngine.cpp new file mode 100644 index 00000000..ed0a77da --- /dev/null +++ b/unikey/LotusUnikeyEngine.cpp @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "LotusUnikeyEngine.hpp" +#include "unikeyinputcontext.h" + +namespace fcitx::lotus { + +LotusUnikeyEngine::LotusUnikeyEngine() + : im_(std::make_unique()) + , uic_(std::make_unique(im_.get())) {} + +LotusUnikeyEngine::~LotusUnikeyEngine() = default; + +void LotusUnikeyEngine::setInputMethod(UkInputMethod im) { + im_->setInputMethod(im); +} + +void LotusUnikeyEngine::setOutputCharset(int charsetId) { + im_->setOutputCharset(charsetId); +} + +void LotusUnikeyEngine::setOptions(UnikeyOptions* opt) { + im_->setOptions(opt); +} + +void LotusUnikeyEngine::resetBuf() { + uic_->resetBuf(); +} + +void LotusUnikeyEngine::setCapsState(int shiftPressed, int capsLockOn) { + uic_->setCapsState(shiftPressed, capsLockOn); +} + +void LotusUnikeyEngine::filter(std::uint32_t unikeyKeyCode) { + uic_->filter(unikeyKeyCode); +} + +void LotusUnikeyEngine::putChar(std::uint32_t ch) { + uic_->putChar(ch); +} + +void LotusUnikeyEngine::rebuildChar(VnLexiName ch) { + uic_->rebuildChar(ch); +} + +void LotusUnikeyEngine::backspacePress() { + uic_->backspacePress(); +} + +void LotusUnikeyEngine::restoreKeyStrokes() { + uic_->restoreKeyStrokes(); +} + +bool LotusUnikeyEngine::isAtWordBeginning() const { + return uic_->isAtWordBeginning(); +} + +int LotusUnikeyEngine::backspaces() const { + return uic_->backspaces(); +} + +int LotusUnikeyEngine::bufChars() const { + return uic_->bufChars(); +} + +const unsigned char* LotusUnikeyEngine::buf() const { + return uic_->buf(); +} + +UnikeyInputMethod* LotusUnikeyEngine::inputMethod() { + return im_.get(); +} + +UnikeyInputContext* LotusUnikeyEngine::context() { + return uic_.get(); +} + +} // namespace fcitx::lotus diff --git a/unikey/LotusUnikeyEngine.hpp b/unikey/LotusUnikeyEngine.hpp new file mode 100644 index 00000000..c1249361 --- /dev/null +++ b/unikey/LotusUnikeyEngine.hpp @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Thin wrapper around fcitx5-unikey's UkEngine stack (UnikeyInputMethod + + * UnikeyInputContext). Intended to replace the Go/Bamboo engine when + * LOTUS_USE_UNIKEY is wired through LotusState. + */ +#ifndef FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP +#define FCITX5_LOTUS_LOTUS_ENGINE_HPP + +#include "keycons.h" +#include "vnlexi.h" +#include +#include +#include +#include + +class UnikeyInputMethod; +class UnikeyInputContext; + +namespace fcitx::lotus { + +class LotusUnikeyEngine { +public: + LotusUnikeyEngine(); + ~LotusUnikeyEngine(); + + LotusUnikeyEngine(const LotusUnikeyEngine&) = delete; + LotusUnikeyEngine& operator=(const LotusUnikeyEngine&) = delete; + LotusUnikeyEngine(LotusUnikeyEngine&&) = delete; + LotusUnikeyEngine& operator=(LotusUnikeyEngine&&) = delete; + + void setInputMethod(UkInputMethod im); + void setOutputCharset(int charsetId); + void setOptions(UnikeyOptions* opt); + + void resetBuf(); + void setCapsState(int shiftPressed, int capsLockOn); + void filter(std::uint32_t unikeyKeyCode); + void putChar(std::uint32_t ch); + void rebuildChar(VnLexiName ch); + void backspacePress(); + void restoreKeyStrokes(); + + bool isAtWordBeginning() const; + + int backspaces() const; + int bufChars() const; + const unsigned char* buf() const; + + UnikeyInputMethod* inputMethod(); + UnikeyInputContext* context(); + +private: + std::unique_ptr im_; + std::unique_ptr uic_; +}; + +} // namespace fcitx::lotus + +#endif diff --git a/unikey/core/byteio.cpp b/unikey/core/byteio.cpp new file mode 100644 index 00000000..81fb773c --- /dev/null +++ b/unikey/core/byteio.cpp @@ -0,0 +1,479 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "byteio.h" +#include + +//------------------------------------------------ +StringBIStream::StringBIStream(UKBYTE *data, int len, int elementSize) { + m_data = m_current = data; + m_len = m_left = len; + if (len == -1) { + if (elementSize == 2) + m_eos = (*(UKWORD *)data == 0); + else if (elementSize == 4) + m_eos = (*(UKDWORD *)data == 4); + else + m_eos = (*data == 0); + } else + m_eos = (len <= 0); + m_didBookmark = 0; +} + +//------------------------------------------------ +int StringBIStream::eos() { return m_eos; } + +//------------------------------------------------ +int StringBIStream::getNext(UKBYTE &b) { + if (m_eos) + return 0; + b = *m_current++; + if (m_len == -1) { + m_eos = (b == 0); + } else { + m_left--; + m_eos = (m_left <= 0); + } + return 1; +} + +//------------------------------------------------ +int StringBIStream::unget(UKBYTE b) { + if (m_current != m_data) { + *--m_current = b; + m_eos = 0; + if (m_len != -1) + m_left++; + } + return 1; +} + +//------------------------------------------------ +int StringBIStream::getNextW(UKWORD &w) { + if (m_eos) + return 0; + w = *((UKWORD *)m_current); + m_current += 2; + if (m_len == -1) + m_eos = (w == 0); + else { + m_left -= 2; + m_eos = (m_left <= 0); + } + return 1; +} + +//------------------------------------------------ +int StringBIStream::getNextDW(UKDWORD &dw) { + if (m_eos) + return 0; + + dw = *((UKDWORD *)m_current); + m_current += 4; + if (m_len == -1) + m_eos = (dw == 0); + else { + m_left -= 4; + m_eos = (m_left <= 0); + } + return 1; +} + +//------------------------------------------------ +int StringBIStream::peekNext(UKBYTE &b) { + if (m_eos) + return 0; + b = *m_current; + return 1; +} + +//------------------------------------------------ +int StringBIStream::peekNextW(UKWORD &w) { + if (m_eos) + return 0; + w = *((UKWORD *)m_current); + return 1; +} + +/* +//------------------------------------------------ +int StringBIStream::peekNextDW(UKDWORD & dw) +{ + if (m_eos) + return 0; + dw = *((UKDWORD *)m_current); + return 1; +} +*/ + +//------------------------------------------------ +void StringBIStream::reopen() { + m_current = m_data; + m_left = m_len; + if (m_len == -1) + m_eos = (m_data == 0); + else + m_eos = (m_len <= 0); + m_didBookmark = 0; +} + +//------------------------------------------------ +int StringBIStream::bookmark() { + m_didBookmark = 1; + m_bookmark.current = m_current; + m_bookmark.data = m_data; + m_bookmark.eos = m_eos; + m_bookmark.left = m_left; + m_bookmark.len = m_len; + return 1; +} + +//------------------------------------------------ +int StringBIStream::gotoBookmark() { + if (!m_didBookmark) + return 0; + m_current = m_bookmark.current; + m_data = m_bookmark.data; + m_eos = m_bookmark.eos; + m_left = m_bookmark.left; + m_len = m_bookmark.len; + return 1; +} + +//------------------------------------------------ +int StringBIStream::close() { return 1; }; + +////////////////////////////////////////////////// +// Class StringBOStream +////////////////////////////////////////////////// + +//------------------------------------------------ +StringBOStream::StringBOStream(UKBYTE *buf, int len) { + m_current = m_buf = buf; + m_len = len; + m_out = 0; + m_bad = 0; +} + +//------------------------------------------------ +int StringBOStream::putB(UKBYTE b) { + m_out++; + /* + if (m_out >= 2147483647) { + int err; + err = 1; + } + */ + if (m_bad) + return 0; + /* + if (m_out < 0) { + int i; + i = 1; + } + */ + if (m_out <= m_len) { + *m_current++ = b; + return 1; + } + m_bad = 1; + return 0; +} + +//------------------------------------------------ +int StringBOStream::putW(UKWORD w) { + m_out += 2; + if (m_bad) + return 0; + if (m_out <= m_len) { + *((UKWORD *)m_current) = w; + m_current += 2; + return 1; + } + m_bad = 1; + return 0; +} + +//------------------------------------------------ +int StringBOStream::puts(const char *s, int size) { + if (size == -1) { + while (*s) { + m_out++; + if (m_out <= m_len) + *m_current++ = *s; + s++; + } + if (!m_bad && m_out > m_len) + m_bad = 1; + return (!m_bad); + } + + int n; + if (!m_bad && m_out <= m_len) { + n = m_len - m_out; + if (n > size) + n = size; + memcpy(m_current, s, n); + m_current += n; + } + + m_out += size; + if (!m_bad && m_out > m_len) + m_bad = 1; + return (!m_bad); +} + +//------------------------------------------------ +void StringBOStream::reopen() { + m_current = m_buf; + m_out = 0; + m_bad = 0; +} + +//------------------------------------------------ +int StringBOStream::isOK() { return !m_bad; } + +//////////////////////////////////////////////////// +// Class FileBIStream // +//////////////////////////////////////////////////// + +//---------------------------------------------------- +FileBIStream::FileBIStream(int bufSize, char *buf) { + m_file = NULL; + m_buf = buf; + m_bufSize = bufSize; + m_own = 1; + m_didBookmark = 0; + + m_readAhead = 0; + m_lastIsAhead = 0; +} + +//---------------------------------------------------- +FileBIStream::~FileBIStream() { + if (m_own) + close(); +} + +//---------------------------------------------------- +int FileBIStream::open(const char *fileName) { + m_file = fopen(fileName, "rb"); + if (m_file == NULL) + return 0; + setvbuf(m_file, m_buf, _IOFBF, m_bufSize); + m_own = 0; + m_readAhead = 0; + m_lastIsAhead = 0; + return 1; +} + +//---------------------------------------------------- +int FileBIStream::close() { + if (m_file != NULL) { + fclose(m_file); + m_file = NULL; + } + return 1; +} + +//---------------------------------------------------- +void FileBIStream::attach(FILE *f) { + m_file = f; + m_own = 0; + m_readAhead = 0; + m_lastIsAhead = 0; +} + +//---------------------------------------------------- +int FileBIStream::eos() { + if (m_readAhead) + return 0; + return feof(m_file); +} + +//---------------------------------------------------- +int FileBIStream::getNext(UKBYTE &b) { + if (m_readAhead) { + m_readAhead = 0; + b = m_readByte; + m_lastIsAhead = 1; + return 1; + } + + m_lastIsAhead = 0; + b = fgetc(m_file); + return (!feof(m_file)); +} + +//---------------------------------------------------- +int FileBIStream::peekNext(UKBYTE &b) { + if (m_readAhead) { + b = m_readByte; + return 1; + } + + b = fgetc(m_file); + if (feof(m_file)) + return 0; + ungetc(b, m_file); + return 1; +} + +//---------------------------------------------------- +int FileBIStream::unget(UKBYTE b) { + if (m_lastIsAhead) { + m_lastIsAhead = 0; + m_readAhead = 1; + m_readByte = b; + return 1; + } + + ungetc(b, m_file); + return 1; +} + +//---------------------------------------------------- +int FileBIStream::getNextW(UKWORD &w) { + UKBYTE b1, b2; + + if (getNext(b1)) { + if (getNext(b2)) { + *((UKBYTE *)&w) = b1; + *(((UKBYTE *)&w) + 1) = b2; + return 1; + } + } + return 0; +} + +//---------------------------------------------------- +int FileBIStream::getNextDW(UKDWORD &dw) { + UKWORD w1, w2; + if (getNextW(w1)) { + if (getNextW(w2)) { + *((UKWORD *)&dw) = w1; + *(((UKWORD *)&dw) + 1) = w2; + return 1; + } + } + return 0; +} +//---------------------------------------------------- +int FileBIStream::peekNextW(UKWORD &w) { + UKBYTE hi, low; + if (getNext(low)) { + if (getNext(hi)) { + unget(hi); + w = hi; + w = (w << 8) + low; + m_readAhead = 1; + m_readByte = low; + m_lastIsAhead = 0; + return 1; + } + + m_readAhead = 1; + m_readByte = low; + m_lastIsAhead = 0; + return 0; + } + return 0; +} + +//---------------------------------------------------- +int FileBIStream::bookmark() { + m_didBookmark = 1; + m_bookmark.pos = ftell(m_file); + return 1; +} + +//---------------------------------------------------- +int FileBIStream::gotoBookmark() { + if (!m_didBookmark) + return 0; + fseek(m_file, m_bookmark.pos, SEEK_SET); + return 1; +} + +//////////////////////////////////////////////////// +// Class FileBOStream // +//////////////////////////////////////////////////// +//---------------------------------------------------- +FileBOStream::FileBOStream(int bufSize, char *buf) { + m_file = NULL; + m_buf = buf; + m_bufSize = bufSize; + m_own = 1; + m_bad = 1; +} + +//---------------------------------------------------- +FileBOStream::~FileBOStream() { + if (m_own) + close(); +} + +//---------------------------------------------------- +int FileBOStream::open(const char *fileName) { + m_file = fopen(fileName, "wb"); + if (m_file == NULL) + return 0; + m_bad = 0; + setvbuf(m_file, m_buf, _IOFBF, m_bufSize); + m_own = 1; + return 1; +} + +//---------------------------------------------------- +void FileBOStream::attach(FILE *f) { + m_file = f; + m_own = 0; + m_bad = 0; +} + +//---------------------------------------------------- +int FileBOStream::close() { + if (m_file != NULL) { + fclose(m_file); + m_file = NULL; + } + return 1; +} + +//---------------------------------------------------- +int FileBOStream::putB(UKBYTE b) { + if (m_bad) + return 0; + m_bad = (fputc(b, m_file) == EOF); + return (!m_bad); +} + +//---------------------------------------------------- +int FileBOStream::putW(UKWORD w) { + if (m_bad) + return 0; + // m_bad = (fputwc(w, m_file) == WEOF); + m_bad = (fputc((UKBYTE)w, m_file) == EOF); + if (m_bad) + return 0; + m_bad = (fputc((UKBYTE)(w >> 8), m_file) == EOF); + return (!m_bad); +} + +//---------------------------------------------------- +int FileBOStream::puts(const char *s, int size) { + if (m_bad) + return 0; + if (size == -1) { + m_bad = (fputs(s, m_file) == EOF); + return (!m_bad); + } + int out = fwrite(s, 1, size, m_file); + m_bad = (out != size); + return (!m_bad); +} + +//---------------------------------------------------- +int FileBOStream::isOK() { return !m_bad; } diff --git a/unikey/core/byteio.h b/unikey/core/byteio.h new file mode 100644 index 00000000..9b3582d7 --- /dev/null +++ b/unikey/core/byteio.h @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef BYTE_IO_STREAM_H +#define BYTE_IO_STREAM_H + +// #include "vnconv.h" +#include + +typedef unsigned char UKBYTE; +typedef unsigned short UKWORD; +typedef unsigned int UKDWORD; + +//---------------------------------------------------- +class ByteStream { +public: + virtual ~ByteStream() {} +}; + +//---------------------------------------------------- +class ByteInStream : public ByteStream { +public: + virtual int getNext(UKBYTE &b) = 0; + virtual int peekNext(UKBYTE &b) = 0; + virtual int unget(UKBYTE b) = 0; + + virtual int getNextW(UKWORD &w) = 0; + virtual int peekNextW(UKWORD &w) = 0; + + virtual int getNextDW(UKDWORD &dw) = 0; + + virtual int bookmark() // no support for bookmark by default + { + return 0; + } + + virtual int gotoBookmark() { return 0; } + + virtual int eos() = 0; // end of stream + virtual int close() = 0; +}; + +//---------------------------------------------------- +class ByteOutStream : public ByteStream { +public: + virtual int putB(UKBYTE b) = 0; + virtual int putW(UKWORD w) = 0; + virtual int puts(const char *s, int size = -1) = 0; // write an 8-bit string + virtual int isOK() = 0; // get current stream state +}; + +//---------------------------------------------------- +class StringBIStream : public ByteInStream { +protected: + int m_eos; + UKBYTE *m_data, *m_current; + int m_len, m_left; + + struct { + int eos; + UKBYTE *data, *current; + int len, left; + } m_bookmark; + + int m_didBookmark; + +public: + StringBIStream(UKBYTE *data, int len, int elementSize = 1); + virtual int getNext(UKBYTE &b); + virtual int peekNext(UKBYTE &b); + virtual int unget(UKBYTE b); + + virtual int getNextW(UKWORD &w); + virtual int peekNextW(UKWORD &w); + + virtual int getNextDW(UKDWORD &dw); + + virtual int eos(); // end of stream + virtual int close(); + + virtual int bookmark(); + virtual int gotoBookmark(); + + void reopen(); + int left() { return m_left; } +}; + +//---------------------------------------------------- +class FileBIStream : public ByteInStream { +protected: + FILE *m_file; + int m_bufSize; + char *m_buf; + int m_own; + int m_didBookmark; + + struct { + long pos; + } m_bookmark; + + // some systems don't have wide char IO functions + // we have to use this variables to implement that + UKBYTE m_readByte; + int m_readAhead; + int m_lastIsAhead; + +public: + FileBIStream(int bufsize = 8192, char *buf = NULL); + // FileBIStream(char *fileName, int bufsize = 8192, void *buf = NULL); + + int open(const char *fileName); + void attach(FILE *f); + virtual int close(); + + virtual int getNext(UKBYTE &b); + virtual int peekNext(UKBYTE &b); + virtual int unget(UKBYTE b); + + virtual int getNextW(UKWORD &w); + virtual int peekNextW(UKWORD &w); + + virtual int getNextDW(UKDWORD &dw); + + virtual int eos(); // end of stream + + virtual int bookmark(); + virtual int gotoBookmark(); + + virtual ~FileBIStream(); +}; + +//---------------------------------------------------- +class StringBOStream : public ByteOutStream { +protected: + UKBYTE *m_buf, *m_current; + int m_out; + int m_len; + int m_bad; + +public: + StringBOStream(UKBYTE *buf, int len); + virtual int putB(UKBYTE b); + virtual int putW(UKWORD w); + virtual int puts(const char *s, int size = -1); + virtual int isOK(); // get current stream state + + virtual int close() { return 1; }; + + void reopen(); + int getOutBytes() { return m_out; } +}; + +//---------------------------------------------------- +class FileBOStream : public ByteOutStream { +protected: + FILE *m_file; + int m_bufSize; + char *m_buf; + int m_own; + int m_bad; + +public: + FileBOStream(int bufsize = 8192, char *buf = NULL); + // FileBOStream(char *fileName, int bufsize = 8192, void *buf = NULL); + + int open(const char *fileName); + void attach(FILE *); + virtual int close(); + + virtual int putB(UKBYTE b); + virtual int putW(UKWORD w); + virtual int puts(const char *s, int size = -1); + virtual int isOK(); // get current stream state + virtual ~FileBOStream(); +}; + +#endif diff --git a/unikey/core/charset.cpp b/unikey/core/charset.cpp new file mode 100644 index 00000000..e6719a4b --- /dev/null +++ b/unikey/core/charset.cpp @@ -0,0 +1,1247 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include + +#include "charset.h" +#include "data.h" + +int LoVowel['z' - 'a' + 1]; +int HiVowel['Z' - 'A' + 1]; + +#define IS_VOWEL(x) \ + ((x >= 'a' && x <= 'z' && LoVowel[x - 'a']) || \ + (x >= 'A' && x <= 'Z' && HiVowel[x - 'A'])) + +SingleByteCharset *SgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; +DoubleByteCharset *DbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; + +DllExport CVnCharsetLib VnCharsetLibObj; + +////////////////////////////////////////////////////// +// Generic VnCharset class +////////////////////////////////////////////////////// +int VnCharset::elementSize() { return 1; } + +//------------------------------------------- +int VnInternalCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + if (!is.getNextDW(stdChar)) { + bytesRead = 0; + return 0; + } + bytesRead = sizeof(UKDWORD); + return 1; +} + +//------------------------------------------- +int VnInternalCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + outLen = sizeof(StdVnChar); + os.putW((UKWORD)stdChar); + return os.putW((UKWORD)(stdChar >> (sizeof(UKWORD) * 8))); +} + +//------------------------------------------- +int VnInternalCharset::elementSize() { return 4; } + +//------------------------------------------- +SingleByteCharset::SingleByteCharset(unsigned char *vnChars) { + int i; + m_vnChars = vnChars; + memset(m_stdMap, 0, 256 * sizeof(UKWORD)); + for (i = 0; i < TOTAL_VNCHARS; i++) { + if (vnChars[i] != 0 && + (i == TOTAL_VNCHARS - 1 || vnChars[i] != vnChars[i + 1])) + m_stdMap[vnChars[i]] = i + 1; + } +} + +//------------------------------------------- +int SingleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + unsigned char ch; + if (!is.getNext(ch)) { + bytesRead = 0; + return 0; + } + + stdChar = (m_stdMap[ch]) ? (VnStdCharOffset + m_stdMap[ch] - 1) : ch; + bytesRead = 1; + return 1; +} + +//------------------------------------------- +int SingleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + int ret; + unsigned char ch; + if (stdChar >= VnStdCharOffset) { + outLen = 1; + ch = m_vnChars[stdChar - VnStdCharOffset]; + if (ch == 0) + ch = (stdChar == StdStartQuote) + ? PadStartQuote + : ((stdChar == StdEndQuote) + ? PadEndQuote + : ((stdChar == StdEllipsis) ? PadEllipsis + : PadChar)); + ret = os.putB(ch); + } else { + if (stdChar > 255 || m_stdMap[stdChar]) { + // this character is missing in the charset + // output padding character + outLen = 1; + ret = os.putB(PadChar); + } else { + outLen = 1; + ret = os.putB((UKBYTE)stdChar); + } + } + return ret; +} + +//------------------------------------------- +int wideCharCompare(const void *ele1, const void *ele2) { + UKWORD ch1 = LOWORD(*((UKDWORD *)ele1)); + UKWORD ch2 = LOWORD(*((UKDWORD *)ele2)); + return (ch1 == ch2) ? 0 : ((ch1 > ch2) ? 1 : -1); +} + +//------------------------------------------- +UnicodeCharset::UnicodeCharset(UnicodeChar *vnChars) { + UKDWORD i; + m_toUnicode = vnChars; + for (i = 0; i < TOTAL_VNCHARS; i++) + m_vnChars[i] = (i << 16) + vnChars[i]; // high word is used for index + qsort(m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); +} + +//------------------------------------------- +int UnicodeCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + UnicodeChar uniCh; + if (!is.getNextW(uniCh)) { + bytesRead = 0; + return 0; + } + bytesRead = sizeof(UnicodeChar); + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); + if (pChar) + stdChar = VnStdCharOffset + HIWORD(*pChar); + else + stdChar = uniCh; + return 1; +} + +//------------------------------------------- +int UnicodeCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { + outLen = sizeof(UnicodeChar); + return os.putW((stdChar >= VnStdCharOffset) + ? m_toUnicode[stdChar - VnStdCharOffset] + : (UnicodeChar)stdChar); +} + +//------------------------------------------- +int UnicodeCharset::elementSize() { return 2; } + +//////////////////////////////////////// +// Unicode decomposed +//////////////////////////////////////// +//------------------------------------------- +int uniCompInfoCompare(const void *ele1, const void *ele2) { + UKDWORD ch1 = ((UniCompCharInfo *)ele1)->compChar; + UKDWORD ch2 = ((UniCompCharInfo *)ele2)->compChar; + return (ch1 == ch2) ? 0 : ((ch1 > ch2) ? 1 : -1); +} + +UnicodeCompCharset::UnicodeCompCharset(UnicodeChar *uniChars, + UKDWORD *uniCompChars) { + int i, k; + m_uniCompChars = uniCompChars; + m_totalChars = 0; + for (i = 0; i < TOTAL_VNCHARS; i++) { + m_info[i].compChar = uniCompChars[i]; + m_info[i].stdIndex = i; + m_totalChars++; + } + + for (k = 0, i = TOTAL_VNCHARS; k < TOTAL_VNCHARS; k++) + if (uniChars[k] != uniCompChars[k]) { + m_info[i].compChar = uniChars[k]; + m_info[i].stdIndex = k; + m_totalChars++; + i++; + } + + qsort(m_info, m_totalChars, sizeof(UniCompCharInfo), uniCompInfoCompare); +} + +//--------------------------------------------- +int UnicodeCompCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + // read first char + + UniCompCharInfo key; + UKWORD w; + if (!is.getNextW(w)) { + bytesRead = 0; + return 0; + } + key.compChar = w; + bytesRead = 2; + + UniCompCharInfo *pInfo = + (UniCompCharInfo *)bsearch(&key, m_info, m_totalChars, + sizeof(UniCompCharInfo), uniCompInfoCompare); + if (!pInfo) + stdChar = key.compChar; + else { + stdChar = pInfo->stdIndex + VnStdCharOffset; + if (is.peekNextW(w)) { + UKDWORD hi = w; + if (hi > 0) { + key.compChar += hi << 16; + pInfo = (UniCompCharInfo *)bsearch(&key, m_info, m_totalChars, + sizeof(UniCompCharInfo), + uniCompInfoCompare); + if (pInfo) { + stdChar = pInfo->stdIndex + VnStdCharOffset; + bytesRead += 2; + is.getNextW(w); + } + } + } + } + return 1; +} + +//--------------------------------------------- +int UnicodeCompCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + int ret; + if (stdChar >= VnStdCharOffset) { + UKDWORD uniCompCh = m_uniCompChars[stdChar - VnStdCharOffset]; + UKWORD lo = LOWORD(uniCompCh); + UKWORD hi = HIWORD(uniCompCh); + outLen = 2; + ret = os.putW(lo); + if (hi > 0) { + outLen += 2; + ret = os.putW(hi); + } + } else { + outLen = 2; + ret = os.putW((UKWORD)stdChar); + } + return ret; +} + +//------------------------------------------- +int UnicodeCompCharset::elementSize() { return 2; } + +//////////////////////////////// +// Unicode UTF-8 // +//////////////////////////////// +int UnicodeUTF8Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + UKWORD w1, w2, w3; + UKBYTE first, second, third; + UnicodeChar uniCh; + + bytesRead = 0; + if (!is.getNext(first)) + return 0; + bytesRead = 1; + + if (first < 0x80) + uniCh = first; // 1-byte sequence + else if ((first & 0xE0) == 0xC0) { + // 2-byte sequence + if (!is.peekNext(second)) + return 0; + if ((second & 0xC0) != 0x80) { + stdChar = INVALID_STD_CHAR; + return 1; + } + is.getNext(second); + bytesRead = 2; + w1 = first; + w2 = second; + uniCh = ((w1 & 0x001F) << 6) | (w2 & 0x3F); + } else if ((first & 0xF0) == 0xE0) { + // 3-byte sequence + if (!is.peekNext(second)) + return 0; + if ((second & 0xC0) != 0x80) { + stdChar = INVALID_STD_CHAR; + return 1; + } + is.getNext(second); + bytesRead = 2; + if (!is.peekNext(third)) + return 0; + if ((third & 0xC0) != 0x80) { + stdChar = INVALID_STD_CHAR; + return 1; + } + is.getNext(third); + bytesRead = 3; + w1 = first; + w2 = second; + w3 = third; + uniCh = ((w1 & 0x000F) << 12) | ((w2 & 0x003F) << 6) | (w3 & 0x003F); + } else { + stdChar = INVALID_STD_CHAR; + return 1; + } + + // translate to StdVnChar + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); + if (pChar) + stdChar = VnStdCharOffset + HIWORD(*pChar); + else + stdChar = uniCh; + return 1; +} + +//------------------------------------------- +int UnicodeUTF8Charset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; + if (uChar < 0x0080) { + outLen = 1; + ret = os.putB((UKBYTE)uChar); + } else if (uChar < 0x0800) { + outLen = 2; + os.putB(0xC0 | (UKBYTE)(uChar >> 6)); + ret = os.putB(0x80 | (UKBYTE)(uChar & 0x003F)); + } else { + outLen = 3; + os.putB(0xE0 | (UKBYTE)(uChar >> 12)); + os.putB(0x80 | (UKBYTE)((uChar >> 6) & 0x003F)); + ret = os.putB(0x80 | (UKBYTE)(uChar & 0x003F)); + } + return ret; +} + +//////////////////////////////////////// +// Unicode character reference &#D; // +//////////////////////////////////////// +int hexDigitValue(unsigned char digit) { + if (digit >= 'a' && digit <= 'f') + return digit - 'a' + 10; + if (digit >= 'A' && digit <= 'F') + return digit - 'A' + 10; + if (digit >= '0' && digit <= '9') + return digit - '0'; + return 0; +} + +//-------------------------------------- +int UnicodeRefCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + unsigned char ch; + UnicodeChar uniCh; + bytesRead = 0; + if (!is.getNext(ch)) + return 0; + bytesRead = 1; + uniCh = ch; + if (ch == '&') { + if (is.peekNext(ch) && ch == '#') { + is.getNext(ch); + bytesRead++; + if (!is.eos()) { + is.peekNext(ch); + if (ch != 'x' && ch != 'X') { + UKWORD code = 0; + int digits = 0; + while (is.peekNext(ch) && isdigit(ch) && digits < 5) { + is.getNext(ch); + bytesRead++; + code = code * 10 + (ch - '0'); + digits++; + } + if (is.peekNext(ch) && ch == ';') { + is.getNext(ch); + bytesRead++; + uniCh = code; + } + } else { + is.getNext(ch); + bytesRead++; + UKWORD code = 0; + int digits = 0; + while (is.peekNext(ch) && isxdigit(ch) && digits < 4) { + is.getNext(ch); + bytesRead++; + code = (code << 4) + hexDigitValue(ch); + digits++; + } + if (is.peekNext(ch) && ch == ';') { + is.getNext(ch); + bytesRead++; + uniCh = code; + } + } // hex digits + } + } + } + + // translate to StdVnChar + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); + if (pChar) + stdChar = VnStdCharOffset + HIWORD(*pChar); + else + stdChar = uniCh; + return 1; +} + +//-------------------------------- +int UnicodeRefCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; + if (uChar < 128) { + outLen = 1; + ret = os.putB((UKBYTE)uChar); + } else { + outLen = 2; + os.putB((UKBYTE)'&'); + os.putB((UKBYTE)'#'); + + int i, digit, prev, base; + prev = 0; + base = 10000; + for (i = 0; i < 5; i++) { + digit = uChar / base; + if (digit || prev) { + prev = 1; + outLen++; + os.putB('0' + (unsigned char)digit); + } + uChar %= base; + base /= 10; + } + ret = os.putB((UKBYTE)';'); + outLen++; + } + return ret; +} + +#define HEX_DIGIT(x) ((x < 10) ? ('0' + x) : ('A' + x - 10)) + +//-------------------------------- +int UnicodeHexCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; + if (uChar < 256) { + outLen = 1; + ret = os.putB((UKBYTE)uChar); + } else { + outLen = 3; + os.putB('&'); + os.putB('#'); + os.putB('x'); + + int i, digit; + int prev = 0; + int shifts = 12; + + for (i = 0; i < 4; i++) { + digit = ((uChar >> shifts) & 0x000F); + if (digit > 0 || prev) { + prev = 1; + outLen++; + os.putB((UKBYTE)HEX_DIGIT(digit)); + } + shifts -= 4; + } + ret = os.putB(';'); + outLen++; + } + return ret; +} + +///////////////////////////////// +// Class UnicodeCStringCharset / +///////////////////////////////// +void UnicodeCStringCharset::startInput() { m_prevIsHex = 0; } + +//---------------------------------------- +int UnicodeCStringCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + unsigned char ch; + UnicodeChar uniCh; + bytesRead = 0; + if (!is.getNext(ch)) + return 0; + bytesRead = 1; + uniCh = ch; + if (ch == '\\') { + if (is.peekNext(ch) && (ch == 'x' || ch == 'X')) { + is.getNext(ch); + bytesRead++; + UKWORD code = 0; + int digits = 0; + while (is.peekNext(ch) && isxdigit(ch) && digits < 4) { + is.getNext(ch); + bytesRead++; + code = (code << 4) + hexDigitValue(ch); + digits++; + } + uniCh = code; + } + } + + // translate to StdVnChar + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); + if (pChar) + stdChar = VnStdCharOffset + HIWORD(*pChar); + else + stdChar = uniCh; + return 1; +} + +//------------------------------------ +int UnicodeCStringCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; + if (uChar < 128 && !isxdigit(uChar) && uChar != 'x' && uChar != 'X') { + outLen = 1; + ret = os.putB((UKBYTE)uChar); + } else { + outLen = 2; + os.putB('\\'); + os.putB('x'); + + int i, digit; + int prev = 0; + int shifts = 12; + + for (i = 0; i < 4; i++) { + digit = ((uChar >> shifts) & 0x000F); + if (digit > 0 || prev) { + prev = 1; + outLen++; + os.putB((UKBYTE)HEX_DIGIT(digit)); + } + shifts -= 4; + } + ret = os.isOK(); + m_prevIsHex = 1; + } + return ret; +} + +///////////////////////////////// +// Double-byte charsets // +///////////////////////////////// +DoubleByteCharset::DoubleByteCharset(UKWORD *vnChars) { + m_toDoubleChar = vnChars; + memset(m_stdMap, 0, 256 * sizeof(UKWORD)); + for (int i = 0; i < TOTAL_VNCHARS; i++) { + if (vnChars[i] >> 8) // a 2-byte character + m_stdMap[vnChars[i] >> 8] = 0xFFFF; // INVALID_STD_CHAR; + else if (m_stdMap[vnChars[i]] == 0) + m_stdMap[vnChars[i]] = i + 1; + m_vnChars[i] = + (i << 16) + vnChars[i]; // high word is used for StdChar index + } + qsort(m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); +} + +//--------------------------------------------- +int DoubleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + unsigned char ch; + + // read first byte + bytesRead = 0; + if (!is.getNext(ch)) + return 0; + bytesRead = 1; + stdChar = m_stdMap[ch]; + if (stdChar == 0) + stdChar = ch; + else if (stdChar == 0xFFFF) + stdChar = INVALID_STD_CHAR; + else { + stdChar += VnStdCharOffset - 1; + UKBYTE hi; + if (is.peekNext(hi) && hi > 0) { + // test if a double-byte character is encountered + UKDWORD key = MAKEWORD(ch, hi); + UKDWORD *pChar = + (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); + if (pChar) { + stdChar = VnStdCharOffset + HIWORD(*pChar); + bytesRead = 2; + is.getNext(hi); + } + } + } + return 1; +} + +//--------------------------------------------- +int DoubleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + int ret; + if (stdChar >= VnStdCharOffset) { + UKWORD wCh = m_toDoubleChar[stdChar - VnStdCharOffset]; + + if (wCh & 0xFF00) { + outLen = 2; + os.putB((UKBYTE)(wCh & 0x00FF)); + ret = os.putB((UKBYTE)(wCh >> 8)); + } else { + unsigned char b = (unsigned char)wCh; + if (m_stdMap[b] == 0xFFFF) + b = PadChar; + outLen = 1; + ret = os.putB(b); + } + /* + outLen = 1; + ret = os.putB((UKBYTE)(wCh & 0x00FF)); + if (wCh & 0xFF00) { + outLen = 2; + ret = os.putB((UKBYTE)(wCh >> 8)); + } + */ + } else { + if (stdChar > 255 || m_stdMap[stdChar]) { + outLen = 1; + ret = os.putB((UKBYTE)PadChar); + } else { + outLen = 1; + ret = os.putB((UKBYTE)stdChar); + } + } + return ret; +} + +///////////////////////////////////////////// +// Class: VIQRCharset // +///////////////////////////////////////////// + +unsigned char VIQRTones[] = {'\'', '`', '?', '~', '.'}; + +const char *VIQREscapes[] = { + "://", "/", "@", "mailto:", "email:", "news:", "www", "ftp"}; + +const int VIQREscCount = sizeof(VIQREscapes) / sizeof(char *); + +VIQRCharset::VIQRCharset(UKDWORD *vnChars) { + memset(m_stdMap, 0, 256 * sizeof(UKWORD)); + int i; + UKDWORD dw; + m_vnChars = vnChars; + for (i = 0; i < TOTAL_VNCHARS; i++) { + dw = m_vnChars[i]; + if (!(dw & 0xffffff00)) { // single byte + // ch = (unsigned char)(dw & 0xff); + m_stdMap[dw] = i + 256; + } + } + + // set offset from base characters according to tone marks + m_stdMap[(unsigned char)'\''] = 2; + m_stdMap[(unsigned char)'`'] = 4; + m_stdMap[(unsigned char)'?'] = 6; + m_stdMap[(unsigned char)'~'] = 8; + m_stdMap[(unsigned char)'.'] = 10; + m_stdMap[(unsigned char)'^'] = 12; + + m_stdMap[(unsigned char)'('] = 24; + m_stdMap[(unsigned char)'+'] = 26; + m_stdMap[(unsigned char)'*'] = 26; +} + +//--------------------------------------------------- +void VIQRCharset::startInput() { + m_suspicious = 0; + m_atWordBeginning = 1; + m_gotTone = 0; + m_escAll = 0; + if (VnCharsetLibObj.m_options.viqrEsc) + VnCharsetLibObj.m_VIQREscPatterns.reset(); +} + +//--------------------------------------------------- +int VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + unsigned char ch1; + bytesRead = 0; + + if (!is.getNext(ch1)) + return 0; + bytesRead = 1; + stdChar = m_stdMap[ch1]; + + if (VnCharsetLibObj.m_options.viqrEsc) { + if (VnCharsetLibObj.m_VIQREscPatterns.foundAtNextChar(ch1) != -1) { + m_escAll = 1; + } + } + + if (m_escAll && (ch1 == ' ' || ch1 == '\t' || ch1 == '\r' || ch1 == '\n')) + m_escAll = 0; + + if (ch1 == '\\') { + // ecape character , try to read next + if (!is.getNext(ch1)) { + bytesRead++; + stdChar = m_stdMap[ch1]; + } + } + + if (stdChar < 256) { + stdChar = ch1; + } else if (!m_escAll && !is.eos()) { + // try to read the next byte + unsigned char ch2; + is.peekNext(ch2); + unsigned char upper = toupper(ch1); + if ((!VnCharsetLibObj.m_options.smartViqr || m_atWordBeginning) && + upper == 'D' && (ch2 == 'd' || ch2 == 'D')) { + is.getNext(ch2); + bytesRead++; + stdChar += 2; // dd is 2 positions after d. + } else { + StdVnChar index = m_stdMap[ch2]; + + int cond; + if (m_suspicious) { + cond = + IS_VOWEL(ch1) && + (index == 2 || index == 4 || + index == 8 || // not accepting ? . in suspicious mode + (index == 12 && + (upper == 'A' || upper == 'E' || upper == 'O')) || + (m_stdMap[ch2] == 24 && upper == 'A') || + (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); + if (cond) + m_suspicious = 0; + } else + cond = + IS_VOWEL(ch1) && + ((index <= 10 && index > 0 && + (!m_gotTone || (index != 6 && index != 10))) || + (index == 12 && + (upper == 'A' || upper == 'E' || upper == 'O')) || + (m_stdMap[ch2] == 24 && upper == 'A') || + (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); + + if (cond) { + if (index > 0) + m_gotTone = + 1; // we have a tone/breve/hook in the current word + + // ok, take this byte + is.getNext(ch2); + bytesRead++; + int offset = m_stdMap[ch2]; + if (offset == 26) + offset = 24; + if (offset == 24 && (ch1 == 'u' || ch1 == 'U')) + offset = 12; + stdChar += offset; + // check next byte + if (is.peekNext(ch2)) { + if (index > 10 && m_stdMap[ch2] > 0 && + m_stdMap[ch2] <= 10) { + // ok, take one more byte + is.getNext(ch2); + bytesRead++; + stdChar += m_stdMap[ch2]; + } + } + } + } + } + m_atWordBeginning = (stdChar < 256); + if (stdChar < 256) { + m_gotTone = + 0; // reset this flag because we are at the beginning of a new word + } + + // adjust stdChar + if (stdChar >= 256) + stdChar += VnStdCharOffset - 256; + return 1; +} + +//--------------------------------------------------- +void VIQRCharset::startOutput() { + m_escapeBowl = 0; + m_escapeRoof = 0; + m_escapeHook = 0; + m_escapeTone = 0; + m_noOutEsc = 0; + VnCharsetLibObj.m_VIQROutEscPatterns.reset(); +} + +//--------------------------------------------------- +int VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { + int ret; + UKBYTE b; + if (stdChar >= VnStdCharOffset) { + outLen = 1; + UKDWORD dw = m_vnChars[stdChar - VnStdCharOffset]; + + unsigned char first = (unsigned char)dw; + unsigned char firstUpper = toupper(first); + + b = (UKBYTE)dw; + ret = os.putB(b); + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar(b) != -1) + m_noOutEsc = 1; + + if (m_noOutEsc && (b == ' ' || b == '\t' || b == '\r' || b == '\n')) + m_noOutEsc = 0; + + if (dw & 0x0000FF00) { + // second byte is present + unsigned char second = (UKBYTE)(dw >> 8); + outLen++; + ret = os.putB(second); + + if (dw & 0x00FF0000) { + // third byte is present + outLen++; + ret = os.putB((UKBYTE)(dw >> 16)); + m_escapeTone = 0; + } else { + UKWORD index = m_stdMap[second]; + m_escapeTone = (index == 12 || index == 24 || index == 26); + } + + VnCharsetLibObj.m_VIQROutEscPatterns.reset(); + + m_escapeBowl = 0; + m_escapeHook = 0; + m_escapeRoof = 0; + } else { + m_escapeTone = IS_VOWEL(first); + m_escapeBowl = (firstUpper == 'A'); + m_escapeHook = (firstUpper == 'U' || firstUpper == 'O'); + m_escapeRoof = + (firstUpper == 'A' || firstUpper == 'E' || firstUpper == 'O'); + } + } else { + if (stdChar > 255) { + outLen = 1; + ret = os.putB((UKBYTE)PadChar); + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar( + (UKBYTE)PadChar) != -1) + m_noOutEsc = 1; + } else { + outLen = 1; + UKWORD index = m_stdMap[stdChar]; + if (!VnCharsetLibObj.m_options.viqrMixed && !m_noOutEsc && + (stdChar == '\\' || + (index > 0 && index <= 10 && m_escapeTone) || + (index == 12 && m_escapeRoof) || + (index == 24 && m_escapeBowl) || + (index == 26 && m_escapeHook))) { + //(m_stdMap[stdChar] > 0 && m_stdMap[stdChar] <= 26)) { + // tone mark, needs an escape character + outLen++; + ret = os.putB('\\'); + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar( + '\\') != -1) + m_noOutEsc = 1; + } + b = (UKBYTE)stdChar; + ret = os.putB(b); + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar(b) != -1) + m_noOutEsc = 1; + if (m_noOutEsc && (b == ' ' || b == '\t' || b == '\r' || b == '\n')) + m_noOutEsc = 0; + } + // reset escape marks + m_escapeBowl = 0; + m_escapeRoof = 0; + m_escapeHook = 0; + m_escapeTone = 0; + } + return ret; +} + +///////////////////////////////////////////// +// Class: UTF8VIQRCharset // +///////////////////////////////////////////// + +//----------------------------------------- +UTF8VIQRCharset::UTF8VIQRCharset(UnicodeUTF8Charset *pUtf, VIQRCharset *pViqr) { + m_pUtf = pUtf; + m_pViqr = pViqr; +} + +//----------------------------------------- +void UTF8VIQRCharset::startInput() { + m_pUtf->startInput(); + m_pViqr->startInput(); +} + +//----------------------------------------- +void UTF8VIQRCharset::startOutput() { + m_pUtf->startOutput(); + m_pViqr->startOutput(); +} + +//----------------------------------------- +int UTF8VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + UKBYTE ch; + + if (!is.peekNext(ch)) + return 0; + + if (ch > 0xBF && ch < 0xFE) { + m_pViqr->startInput(); // just to reset the VIQR object state + m_pViqr->m_suspicious = 1; + return m_pUtf->nextInput(is, stdChar, bytesRead); + } + + return m_pViqr->nextInput(is, stdChar, bytesRead); +} + +//----------------------------------------- +int UTF8VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + return m_pViqr->putChar(os, stdChar, outLen); +} + +//----------------------------------------- +CVnCharsetLib::CVnCharsetLib() { + unsigned char ch; + for (ch = 'a'; ch < 'z'; ch++) + LoVowel[ch - 'a'] = 0; + LoVowel['a' - 'a'] = 1; + LoVowel['e' - 'a'] = 1; + LoVowel['i' - 'a'] = 1; + LoVowel['o' - 'a'] = 1; + LoVowel['u' - 'a'] = 1; + LoVowel['y' - 'a'] = 1; + + for (ch = 'A'; ch < 'Z'; ch++) + HiVowel[ch - 'A'] = 0; + HiVowel['A' - 'A'] = 1; + HiVowel['E' - 'A'] = 1; + HiVowel['I' - 'A'] = 1; + HiVowel['O' - 'A'] = 1; + HiVowel['U' - 'A'] = 1; + HiVowel['Y' - 'A'] = 1; + + m_pUniCharset = NULL; + m_pUniCompCharset = NULL; + m_pUniUTF8 = NULL; + m_pUniRef = NULL; + m_pUniHex = NULL; + m_pVIQRCharObj = NULL; + m_pUVIQRCharObj = NULL; + m_pWinCP1258 = NULL; + m_pVnIntCharset = NULL; + + int i; + for (i = 0; i < CONV_TOTAL_SINGLE_CHARSETS; i++) + m_sgCharsets[i] = NULL; + + for (i = 0; i < CONV_TOTAL_DOUBLE_CHARSETS; i++) + m_dbCharsets[i] = NULL; + + VnConvResetOptions(&m_options); + m_VIQREscPatterns.init((char **)VIQREscapes, VIQREscCount); + m_VIQROutEscPatterns.init((char **)VIQREscapes, VIQREscCount); +} + +//----------------------------------------- +CVnCharsetLib::~CVnCharsetLib() { + if (m_pUniCharset) + delete m_pUniCharset; + if (m_pUniUTF8) + delete m_pUniUTF8; + if (m_pUniRef) + delete m_pUniRef; + if (m_pUniHex) + delete m_pUniHex; + if (m_pVIQRCharObj) + delete m_pVIQRCharObj; + if (m_pUVIQRCharObj) + delete m_pUVIQRCharObj; + if (m_pWinCP1258) + delete m_pWinCP1258; + if (m_pUniCString) + delete m_pUniCString; + if (m_pVnIntCharset) + delete m_pVnIntCharset; + + int i; + for (i = 0; i < CONV_TOTAL_SINGLE_CHARSETS; i++) + if (m_sgCharsets[i]) + delete m_sgCharsets[i]; + + for (i = 0; i < CONV_TOTAL_DOUBLE_CHARSETS; i++) + if (m_dbCharsets[i]) + delete m_dbCharsets[i]; +} + +//----------------------------------------- +VnCharset *CVnCharsetLib::getVnCharset(int charsetIdx) { + switch (charsetIdx) { + + case CONV_CHARSET_UNICODE: + if (m_pUniCharset == NULL) + m_pUniCharset = new UnicodeCharset(UnicodeTable); + return m_pUniCharset; + case CONV_CHARSET_UNIDECOMPOSED: + if (m_pUniCompCharset == NULL) + m_pUniCompCharset = + new UnicodeCompCharset(UnicodeTable, UnicodeComposite); + return m_pUniCompCharset; + case CONV_CHARSET_UNIUTF8: + case CONV_CHARSET_XUTF8: + if (m_pUniUTF8 == NULL) + m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); + return m_pUniUTF8; + + case CONV_CHARSET_UNIREF: + if (m_pUniRef == NULL) + m_pUniRef = new UnicodeRefCharset(UnicodeTable); + return m_pUniRef; + + case CONV_CHARSET_UNIREF_HEX: + if (m_pUniHex == NULL) + m_pUniHex = new UnicodeHexCharset(UnicodeTable); + return m_pUniHex; + + case CONV_CHARSET_UNI_CSTRING: + if (m_pUniCString == NULL) + m_pUniCString = new UnicodeCStringCharset(UnicodeTable); + return m_pUniCString; + + case CONV_CHARSET_WINCP1258: + if (m_pWinCP1258 == NULL) + m_pWinCP1258 = new WinCP1258Charset(WinCP1258, WinCP1258Pre); + return m_pWinCP1258; + + case CONV_CHARSET_VIQR: + if (m_pVIQRCharObj == NULL) + m_pVIQRCharObj = new VIQRCharset(VIQRTable); + return m_pVIQRCharObj; + + case CONV_CHARSET_VNSTANDARD: + if (m_pVnIntCharset == NULL) + m_pVnIntCharset = new VnInternalCharset(); + return m_pVnIntCharset; + + case CONV_CHARSET_UTF8VIQR: + if (m_pUVIQRCharObj == NULL) { + if (m_pVIQRCharObj == NULL) + m_pVIQRCharObj = new VIQRCharset(VIQRTable); + + if (m_pUniUTF8 == NULL) + m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); + m_pUVIQRCharObj = new UTF8VIQRCharset(m_pUniUTF8, m_pVIQRCharObj); + } + return m_pUVIQRCharObj; + + default: + if (IS_SINGLE_BYTE_CHARSET(charsetIdx)) { + int i = charsetIdx - CONV_CHARSET_TCVN3; + if (m_sgCharsets[i] == NULL) + m_sgCharsets[i] = new SingleByteCharset(SingleByteTables[i]); + return m_sgCharsets[i]; + } else if (IS_DOUBLE_BYTE_CHARSET(charsetIdx)) { + int i = charsetIdx - CONV_CHARSET_VNIWIN; + if (m_dbCharsets[i] == NULL) + m_dbCharsets[i] = new DoubleByteCharset(DoubleByteTables[i]); + return m_dbCharsets[i]; + } + } + return NULL; +} + +//------------------------------------------------- +DllExport void VnConvSetOptions(VnConvOptions *pOptions) { + VnCharsetLibObj.m_options = *pOptions; +} + +//------------------------------------------------- +DllExport void VnConvGetOptions(VnConvOptions *pOptions) { + *pOptions = VnCharsetLibObj.m_options; +} + +//------------------------------------------------- +DllExport void VnConvResetOptions(VnConvOptions *pOptions) { + pOptions->viqrEsc = 1; + pOptions->viqrMixed = 0; + pOptions->toUpper = 0; + pOptions->toLower = 0; + pOptions->removeTone = 0; + pOptions->smartViqr = 1; +} + +///////////////////////////////////////////// +// Class WinCP1258Charset +///////////////////////////////////////////// +WinCP1258Charset::WinCP1258Charset(UKWORD *compositeChars, + UKWORD *precomposedChars) { + int i, k; + m_toDoubleChar = compositeChars; + memset(m_stdMap, 0, 256 * sizeof(UKWORD)); + + // encode composite chars + for (i = 0; i < TOTAL_VNCHARS; i++) { + if (compositeChars[i] >> 8) // a 2-byte character + m_stdMap[compositeChars[i] >> 8] = 0xFFFF; // INVALID_STD_CHAR; + else if (m_stdMap[compositeChars[i]] == 0) + m_stdMap[compositeChars[i]] = i + 1; + + m_vnChars[i] = (i << 16) + + compositeChars[i]; // high word is used for StdChar index + } + + m_totalChars = TOTAL_VNCHARS; + + // add precomposed chars to the table + for (k = 0, i = TOTAL_VNCHARS; k < TOTAL_VNCHARS; k++) + if (precomposedChars[k] != compositeChars[k]) { + if (precomposedChars[k] >> 8) // a 2-byte character + m_stdMap[precomposedChars[k] >> 8] = + 0xFFFF; // INVALID_STD_CHAR; + else if (m_stdMap[precomposedChars[k]] == 0) + m_stdMap[precomposedChars[k]] = k + 1; + + m_vnChars[i] = (k << 16) + precomposedChars[k]; + m_totalChars++; + i++; + } + + qsort(m_vnChars, m_totalChars, sizeof(UKDWORD), wideCharCompare); +} + +//--------------------------------------------------------------------- +// This fuction is basically the same as that of DoubleByteCharset +// with m_totalChars is used instead of constant TOTAL_VNCHARS +//--------------------------------------------------------------------- +int WinCP1258Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + unsigned char ch; + + // read first byte + bytesRead = 0; + if (!is.getNext(ch)) + return 0; + bytesRead = 1; + stdChar = m_stdMap[ch]; + if (stdChar == 0) + stdChar = ch; + else if (stdChar == 0xFFFF) + stdChar = INVALID_STD_CHAR; + else { + stdChar += VnStdCharOffset - 1; + UKBYTE hi; + if (is.peekNext(hi) && hi > 0) { + // test if a double-byte character is encountered + UKDWORD key = MAKEWORD(ch, hi); + UKDWORD *pChar = + (UKDWORD *)bsearch(&key, m_vnChars, m_totalChars, + sizeof(UKDWORD), wideCharCompare); + if (pChar) { + stdChar = VnStdCharOffset + HIWORD(*pChar); + bytesRead = 2; + is.getNext(hi); + } + } + } + return 1; +} + +//--------------------------------------------------------------------- +// This fuction is exactly the same as that of DoubleByteCharset +//--------------------------------------------------------------------- +int WinCP1258Charset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + int ret; + if (stdChar >= VnStdCharOffset) { + UKWORD wCh = m_toDoubleChar[stdChar - VnStdCharOffset]; + + if (wCh & 0xFF00) { + outLen = 2; + os.putB((UKBYTE)(wCh & 0x00FF)); + ret = os.putB((UKBYTE)(wCh >> 8)); + } else { + unsigned char b = (unsigned char)wCh; + if (m_stdMap[b] == 0xFFFF) + b = PadChar; + outLen = 1; + ret = os.putB(b); + } + } else { + if (stdChar > 255 || m_stdMap[stdChar]) { + outLen = 1; + ret = os.putB((UKBYTE)PadChar); + } else { + outLen = 1; + ret = os.putB((UKBYTE)stdChar); + } + } + return ret; +} + +#define IS_ODD(x) (x & 1) +#define IS_EVEN(x) (!(x & 1)) + +StdVnChar StdVnToUpper(StdVnChar ch) { + if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && + IS_ODD(ch)) + ch -= 1; + return ch; +} + +//---------------------------------------- +StdVnChar StdVnToLower(StdVnChar ch) { + if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && + IS_EVEN(ch)) + ch += 1; + return ch; +} + +//---------------------------------------- +StdVnChar StdVnGetRoot(StdVnChar ch) { + if (ch >= VnStdCharOffset && ch < VnStdCharOffset + TOTAL_VNCHARS) + ch = VnStdCharOffset + StdVnRootChar[ch - VnStdCharOffset]; + return ch; +} diff --git a/unikey/core/charset.h b/unikey/core/charset.h new file mode 100644 index 00000000..f40d32b6 --- /dev/null +++ b/unikey/core/charset.h @@ -0,0 +1,293 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __CHARSET_CONVERT_H +#define __CHARSET_CONVERT_H + +#if !defined(_WIN32) +#include +#endif + +#if defined(_WIN32) +#if defined(UNIKEYHOOK) +#define DllInterface __declspec(dllexport) +#else +#define DllInterface __declspec(dllimport) +#endif +#else +#define DllInterface // not used +#define DllExport +#define DllImport +#endif + +#include "byteio.h" +#include "pattern.h" +#include "vnconv.h" + +#define TOTAL_VNCHARS 213 +#define TOTAL_ALPHA_VNCHARS 186 + +#if defined(_WIN32) +typedef unsigned __int32 StdVnChar; +typedef unsigned __int16 UnicodeChar; +typedef unsigned __int16 UKWORD; +typedef unsigned __int32 UKDWORD; +#else +// typedef unsigned int StdVnChar; //the size should be more specific +typedef uint32_t StdVnChar; +typedef uint16_t UnicodeChar; +typedef uint16_t UKWORD; +typedef uint32_t UKDWORD; +#endif + +// typedef unsigned short UnicodeChar; +// typedef unsigned short UKWORD; + +// typedef unsigned int UKDWORD; //the size should be more specific + +#ifndef LOWORD +#define LOWORD(l) ((UKWORD)(l)) +#endif + +#ifndef HIWORD +#define HIWORD(l) ((UKWORD)(((UKDWORD)(l) >> 16) & 0xFFFF)) +#endif + +#ifndef MAKEWORD +#define MAKEWORD(a, b) ((UKWORD)(((UKBYTE)(a)) | ((UKWORD)((UKBYTE)(b))) << 8)) +#endif + +const StdVnChar VnStdCharOffset = 0x10000; +const StdVnChar INVALID_STD_CHAR = 0xFFFFFFFF; +// const unsigned char PadChar = '?'; //? is used for VIQR charset +const unsigned char PadChar = '#'; +const unsigned char PadStartQuote = '\"'; +const unsigned char PadEndQuote = '\"'; +const unsigned char PadEllipsis = '.'; + +class DllInterface VnCharset { +public: + virtual void startInput() {} + virtual void startOutput() {} + // virtual UKBYTE *nextInput(UKBYTE *input, int inLen, StdVnChar & stdChar, + // int & bytesRead) = 0; + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) = 0; + + //------------------------------------------------------------------------ + // put a character to the output after converting it + // Arguments: + // output[in]: output buffer + // stdChar[in]: character in standard charset + // outLen[out]: length of converted sequence + // maxAvail[in]: max length available. + // Returns: next position in output + //------------------------------------------------------------------------ + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) = 0; + virtual int elementSize(); + virtual ~VnCharset() {} +}; + +//-------------------------------------------------- +class SingleByteCharset : public VnCharset { +protected: + UKWORD m_stdMap[256]; + unsigned char *m_vnChars; + +public: + SingleByteCharset(unsigned char *vnChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +class VnInternalCharset : public VnCharset { +public: + VnInternalCharset() {} + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int elementSize(); +}; + +//-------------------------------------------------- +class UnicodeCharset : public VnCharset { +protected: + UKDWORD m_vnChars[TOTAL_VNCHARS]; + UnicodeChar *m_toUnicode; + +public: + UnicodeCharset(UnicodeChar *vnChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int elementSize(); +}; + +//-------------------------------------------------- +class DoubleByteCharset : public VnCharset { +protected: + UKWORD m_stdMap[256]; + UKDWORD m_vnChars[TOTAL_VNCHARS]; + UKWORD *m_toDoubleChar; + +public: + DoubleByteCharset(UKWORD *vnChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +class UnicodeUTF8Charset : public UnicodeCharset { +public: + UnicodeUTF8Charset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} + + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +class UnicodeRefCharset : public UnicodeCharset { +public: + UnicodeRefCharset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} + + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +class UnicodeHexCharset : public UnicodeRefCharset { +public: + UnicodeHexCharset(UnicodeChar *vnChars) : UnicodeRefCharset(vnChars) {} + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +class UnicodeCStringCharset : public UnicodeCharset { +protected: + int m_prevIsHex; + +public: + UnicodeCStringCharset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual void startInput(); +}; + +//-------------------------------------------------- +class WinCP1258Charset : public VnCharset { +protected: + UKWORD m_stdMap[256]; + UKDWORD m_vnChars[TOTAL_VNCHARS * 2]; + UKWORD *m_toDoubleChar; + int m_totalChars; + +public: + WinCP1258Charset(UKWORD *compositeChars, UKWORD *precomposedChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +struct UniCompCharInfo { + UKDWORD compChar; + int stdIndex; +}; + +class UnicodeCompCharset : public VnCharset { +protected: + UniCompCharInfo m_info[TOTAL_VNCHARS * 2]; + UKDWORD *m_uniCompChars; + int m_totalChars; + +public: + UnicodeCompCharset(UnicodeChar *uniChars, UKDWORD *uniCompChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int elementSize(); +}; + +//-------------------------------------------------- +class VIQRCharset : public VnCharset { +protected: + UKDWORD *m_vnChars; + UKWORD m_stdMap[256]; + int m_atWordBeginning; + int m_escapeBowl; + int m_escapeRoof; + int m_escapeHook; + int m_escapeTone; + int m_gotTone; + int m_escAll; + int m_noOutEsc; + +public: + int m_suspicious; + VIQRCharset(UKDWORD *vnChars); + virtual void startInput(); + virtual void startOutput(); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +class UTF8VIQRCharset : public VnCharset { + +protected: + VIQRCharset *m_pViqr; + UnicodeUTF8Charset *m_pUtf; + +public: + UTF8VIQRCharset(UnicodeUTF8Charset *pUtf, VIQRCharset *pViqr); + virtual void startInput(); + virtual void startOutput(); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); +}; + +//-------------------------------------------------- +class DllInterface CVnCharsetLib { +protected: + SingleByteCharset *m_sgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; + DoubleByteCharset *m_dbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; + UnicodeCharset *m_pUniCharset; + UnicodeCompCharset *m_pUniCompCharset; + UnicodeUTF8Charset *m_pUniUTF8; + UnicodeRefCharset *m_pUniRef; + UnicodeHexCharset *m_pUniHex; + VIQRCharset *m_pVIQRCharObj; + UTF8VIQRCharset *m_pUVIQRCharObj; + WinCP1258Charset *m_pWinCP1258; + UnicodeCStringCharset *m_pUniCString; + VnInternalCharset *m_pVnIntCharset; + +public: + PatternList m_VIQREscPatterns, m_VIQROutEscPatterns; + VnConvOptions m_options; + CVnCharsetLib(); + ~CVnCharsetLib(); + VnCharset *getVnCharset(int charsetIdx); +}; + +extern unsigned char SingleByteTables[][TOTAL_VNCHARS]; +extern UKWORD DoubleByteTables[][TOTAL_VNCHARS]; +extern UnicodeChar UnicodeTable[TOTAL_VNCHARS]; +extern UKDWORD VIQRTable[TOTAL_VNCHARS]; +extern UKDWORD UnicodeComposite[TOTAL_VNCHARS]; +extern UKWORD WinCP1258[TOTAL_VNCHARS]; +extern UKWORD WinCP1258Pre[TOTAL_VNCHARS]; + +extern DllInterface CVnCharsetLib VnCharsetLibObj; +extern VnConvOptions VnConvGlobalOptions; +extern int StdVnNoTone[TOTAL_VNCHARS]; +extern int StdVnRootChar[TOTAL_VNCHARS]; + +DllInterface int genConvert(VnCharset &incs, VnCharset &outcs, + ByteInStream &input, ByteOutStream &output); + +StdVnChar StdVnToUpper(StdVnChar ch); +StdVnChar StdVnToLower(StdVnChar ch); +StdVnChar StdVnGetRoot(StdVnChar ch); + +#endif diff --git a/unikey/core/convert.cpp b/unikey/core/convert.cpp new file mode 100644 index 00000000..79ca8623 --- /dev/null +++ b/unikey/core/convert.cpp @@ -0,0 +1,231 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "charset.h" +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#endif + +#include "vnconv.h" + +int vnFileStreamConvert(int inCharset, int outCharset, FILE *inf, FILE *outf); + +DllExport int genConvert(VnCharset &incs, VnCharset &outcs, ByteInStream &input, + ByteOutStream &output) { + StdVnChar stdChar; + int bytesRead, bytesWritten; + + incs.startInput(); + outcs.startOutput(); + + int ret = 1; + while (!input.eos()) { + stdChar = 0; + if (incs.nextInput(input, stdChar, bytesRead)) { + if (stdChar != INVALID_STD_CHAR) { + if (VnCharsetLibObj.m_options.toLower) + stdChar = StdVnToLower(stdChar); + else if (VnCharsetLibObj.m_options.toUpper) + stdChar = StdVnToUpper(stdChar); + if (VnCharsetLibObj.m_options.removeTone) + stdChar = StdVnGetRoot(stdChar); + ret = outcs.putChar(output, stdChar, bytesWritten); + } + } else + break; + } + return (ret ? 0 : VNCONV_OUT_OF_MEMORY); +} + +//---------------------------------------------- +// Arguments: +// inCharset: charset of input +// outCharset: charset of output +// input: input data +// output: output data +// inLen: [in] size of input. if inLen = -1, input data is +// null-terminated. +// [out] if input inLen != -1, output iLen is the numbers of byte +// left in input. +// maxOutLen: [in] size of output. +// [out] number of bytes output, if enough memory +// number of bytes needed for output, if not enough +// memory +// Returns: 0 if successful +// error code: if failed +//---------------------------------------------- +// int VnConvert(int inCharset, int outCharset, UKBYTE *input, UKBYTE *output, +// int & inLen, int & maxOutLen) + +DllExport int VnConvert(int inCharset, int outCharset, UKBYTE *input, + UKBYTE *output, int *pInLen, int *pMaxOutLen) { + int inLen, maxOutLen; + int ret = -1; + + inLen = *pInLen; + maxOutLen = *pMaxOutLen; + + if (inLen != -1 && inLen < 0) // invalid inLen + return ret; + + VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset); + VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); + + if (!pInCharset || !pOutCharset) + return VNCONV_INVALID_CHARSET; + + StringBIStream is(input, inLen, pInCharset->elementSize()); + StringBOStream os(output, maxOutLen); + + ret = genConvert(*pInCharset, *pOutCharset, is, os); + *pMaxOutLen = os.getOutBytes(); + *pInLen = is.left(); + return ret; +} + +//--------------------------------------- +// Arguments: +// inFile: input file name. NULL if STDIN is used +// outFile: output file name, NULL if STDOUT is used +// Returns: +// 0: successful +// errCode: if failed +//--------------------------------------- +DllExport int VnFileConvert(int inCharset, int outCharset, const char *inFile, + const char *outFile) { + FILE *inf = NULL; + FILE *outf = NULL; + int ret = 0; + char tmpName[32]; + + if (inFile == NULL) { + inf = stdin; +#if defined(_WIN32) + _setmode(_fileno(stdin), _O_BINARY); +#endif + } else { + inf = fopen(inFile, "rb"); + if (inf == NULL) { + ret = VNCONV_ERR_INPUT_FILE; + goto end; + } + } + + if (outFile == NULL) + outf = stdout; + else { + // setup temporary output file (because real output file may be the same + // as input file + char outDir[256]; + strcpy(outDir, outFile); + +#if defined(_WIN32) + char *p = strrchr(outDir, '\\'); +#else + char *p = strrchr(outDir, '/'); +#endif + + if (p == NULL) + outDir[0] = 0; + else + *p = 0; + + strcpy(tmpName, outDir); + strcat(tmpName, "XXXXXX"); + + if (mkstemp(tmpName) == -1) { + fclose(inf); + ret = VNCONV_ERR_OUTPUT_FILE; + goto end; + } + outf = fopen(tmpName, "wb"); + + if (outf == NULL) { + fclose(inf); + ret = VNCONV_ERR_OUTPUT_FILE; + goto end; + } + } + + ret = vnFileStreamConvert(inCharset, outCharset, inf, outf); + if (inf != stdin) + fclose(inf); + if (outf != stdout) { + fclose(outf); + + // delete output file if exisits + if (ret == 0) { + remove(outFile); +#if !defined(_WIN32) + char cmd[256]; + sprintf(cmd, "mv %s %s", tmpName, outFile); + cmd[0] = system(cmd); +#else + if (rename(tmpName, outFile) != 0) { + remove(tmpName); + ret = VNCONV_ERR_OUTPUT_FILE; + goto end; + } +#endif + } else + remove(tmpName); + } + +end: +#if defined(_WIN32) + if (inf == stdin) { + _setmode(_fileno(stdin), _O_BINARY); + } +#endif + return ret; +} + +//------------------------------------------------ +// Returns: +// 0: successful +// errCode: if failed +//--------------------------------------- +int vnFileStreamConvert(int inCharset, int outCharset, FILE *inf, FILE *outf) { + VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset); + VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); + + if (!pInCharset || !pOutCharset) + return VNCONV_INVALID_CHARSET; + + if (outCharset == CONV_CHARSET_UNICODE) { + UKWORD sign = 0xFEFF; + fwrite(&sign, sizeof(UKWORD), 1, outf); + } + + FileBIStream is; + FileBOStream os; + + is.attach(inf); + os.attach(outf); + + return genConvert(*pInCharset, *pOutCharset, is, os); +} + +const char *ErrTable[VNCONV_LAST_ERROR] = { + "No error", + "Unknown error", + "Invalid charset", + "Error opening input file", + "Error opening output file", + "Error writing to output stream", + "Not enough memory", +}; + +DllExport const char *VnConvErrMsg(int errCode) { + if (errCode < 0 || errCode >= VNCONV_LAST_ERROR) + errCode = VNCONV_UNKNOWN_ERROR; + return ErrTable[errCode]; +} diff --git a/unikey/core/data.cpp b/unikey/core/data.cpp new file mode 100644 index 00000000..2e3539d5 --- /dev/null +++ b/unikey/core/data.cpp @@ -0,0 +1,1792 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "charset.h" + +/* +Instructions on how to add more charset supports + +Each charset enumerates all its characters according to a fixed order. +To understand this order, copy the TCVN3 charset bellow to some application +and view it with some TCVN3 font. + +Steps to add an 1-byte charset: + +- Determine the Id for your charset. See "vnconv.h". The Id + for your charset is equal to the id of the last 1-byte charset PLUS 1. + Then define a constant for that Id (e.g. #define MY_NEW_CHARSET 25) +- In "vnconv.h": Increase the variable CONV_TOTAL_SINGLE_CHARSETS by 1. +- Add an entry for your charset to the end of CharsetIdMap (in this file) +- Add your charset to the end of SingleBytesTable +- Note that and the end of each charset (after character z) there's a section + for the symbols in western charsets (see TCVN3). Just copy + this section for your charset, and set zero for each code point + that is occupied by your charset (for representing Vietnamese characters). + +Steps to add a 2-byte charset: +- Determine the Id for your charset. See "vnconv.h". The Id + for your charset is equal to the id of the last 2-byte charset PLUS 1. + Then define a constant for that Id (e.g. #define MY_NEW_CHARSET 44) +- In "vnconv.h": Increase the variable CONV_TOTAL_DOUBLE_CHARSETS by 1. +- Add an entry for your charset to the end of CharsetIdMap (in this file) +- Add your charset to the end of DoubleByteTables +- Note that and the end of each charset (after character z) there's a section + for the symbols in western charsets. Just copy this section from + VNI-WIN charset to your charset +- Double-byte characters are represented as a word in which the + low byte is base character, high byte is tone mark (if present). +*/ +extern CharsetNameId CharsetIdMap[]; +extern const int CharsetCount; + +CharsetNameId CharsetIdMap[] = {{"BKHCM1", CONV_CHARSET_BKHCM1}, + {"BKHCM2", CONV_CHARSET_BKHCM2}, + {"ISC", CONV_CHARSET_ISC}, + {"NCR-DEC", CONV_CHARSET_UNIREF}, + {"NCR-HEX", CONV_CHARSET_UNIREF_HEX}, + {"TCVN3", CONV_CHARSET_TCVN3}, + {"UNI-COMP", CONV_CHARSET_UNIDECOMPOSED}, + {"UNICODE", CONV_CHARSET_UNICODE}, + {"UTF-8", CONV_CHARSET_UNIUTF8}, + {"UTF8", CONV_CHARSET_UNIUTF8}, + {"UVIQR", CONV_CHARSET_UTF8VIQR}, + {"VIETWARE-F", CONV_CHARSET_VIETWAREF}, + {"VIETWARE-X", CONV_CHARSET_VIETWAREX}, + {"VIQR", CONV_CHARSET_VIQR}, + {"VISCII", CONV_CHARSET_VISCII}, + {"VNI-MAC", CONV_CHARSET_VNIMAC}, + {"VNI-WIN", CONV_CHARSET_VNIWIN}, + {"VPS", CONV_CHARSET_VPS}, + {"WINCP-1258", CONV_CHARSET_WINCP1258}}; + +const int CharsetCount = sizeof(CharsetIdMap) / sizeof(CharsetNameId); + +/* Western symbols that need to be mapped + 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, + 0x9C, 0x9E, 0x9F, + +If a single byte charset occupies a certain symbol, +its code point in the charset must be set to zero. +See TCVN3 & VPS below for examples +*/ + +unsigned char SingleByteTables[][TOTAL_VNCHARS] = + + // TCVN3 + {{static_cast('A'), + static_cast('a'), + static_cast('\xb8'), + static_cast('\xb8'), + static_cast('\xb5'), + static_cast('\xb5'), + static_cast('\xb6'), + static_cast('\xb6'), + static_cast('\xb7'), + static_cast('\xb7'), + static_cast('\xb9'), + static_cast('\xb9'), // 0: a + static_cast('\xa2'), + static_cast('\xa9'), + static_cast('\xca'), + static_cast('\xca'), + static_cast('\xc7'), + static_cast('\xc7'), + static_cast('\xc8'), + static_cast('\xc8'), + static_cast('\xc9'), + static_cast('\xc9'), + static_cast('\xcb'), + static_cast('\xcb'), // 1: a^ + static_cast('\xa1'), + static_cast('\xa8'), + static_cast('\xbe'), + static_cast('\xbe'), + static_cast('\xbb'), + static_cast('\xbb'), + static_cast('\xbc'), + static_cast('\xbc'), + static_cast('\xbd'), + static_cast('\xbd'), + static_cast('\xc6'), + static_cast('\xc6'), // 2: a( + static_cast('B'), + static_cast('b'), + static_cast('C'), + static_cast('c'), + static_cast('D'), + static_cast('d'), + static_cast('\xa7'), + static_cast('\xae'), + static_cast('E'), + static_cast('e'), + static_cast('\xd0'), + static_cast('\xd0'), + static_cast('\xcc'), + static_cast('\xcc'), + static_cast('\xce'), + static_cast('\xce'), + static_cast('\xcf'), + static_cast('\xcf'), + static_cast('\xd1'), + static_cast('\xd1'), // 3: e + static_cast('\xa3'), + static_cast('\xaa'), + static_cast('\xd5'), + static_cast('\xd5'), + static_cast('\xd2'), + static_cast('\xd2'), + static_cast('\xd3'), + static_cast('\xd3'), + static_cast('\xd4'), + static_cast('\xd4'), + static_cast('\xd6'), + static_cast('\xd6'), // 4: e^ + static_cast('F'), + static_cast('f'), + static_cast('G'), + static_cast('g'), + static_cast('H'), + static_cast('h'), + static_cast('I'), + static_cast('i'), + static_cast('\xdd'), + static_cast('\xdd'), + static_cast('\xd7'), + static_cast('\xd7'), + static_cast('\xd8'), + static_cast('\xd8'), + static_cast('\xdc'), + static_cast('\xdc'), + static_cast('\xde'), + static_cast('\xde'), // 5: i + static_cast('J'), + static_cast('j'), + static_cast('K'), + static_cast('k'), + static_cast('L'), + static_cast('l'), + static_cast('M'), + static_cast('m'), + static_cast('N'), + static_cast('n'), + static_cast('O'), + static_cast('o'), + static_cast('\xe3'), + static_cast('\xe3'), + static_cast('\xdf'), + static_cast('\xdf'), + static_cast('\xe1'), + static_cast('\xe1'), + static_cast('\xe2'), + static_cast('\xe2'), + static_cast('\xe4'), + static_cast('\xe4'), // 6: o + static_cast('\xa4'), + static_cast('\xab'), + static_cast('\xe8'), + static_cast('\xe8'), + static_cast('\xe5'), + static_cast('\xe5'), + static_cast('\xe6'), + static_cast('\xe6'), + static_cast('\xe7'), + static_cast('\xe7'), + static_cast('\xe9'), + static_cast('\xe9'), // 7: o^ + static_cast('\xa5'), + static_cast('\xac'), + static_cast('\xed'), + static_cast('\xed'), + static_cast('\xea'), + static_cast('\xea'), + static_cast('\xeb'), + static_cast('\xeb'), + static_cast('\xec'), + static_cast('\xec'), + static_cast('\xee'), + static_cast('\xee'), // 8: o+ + static_cast('P'), + static_cast('p'), + static_cast('Q'), + static_cast('q'), + static_cast('R'), + static_cast('r'), + static_cast('S'), + static_cast('s'), + static_cast('T'), + static_cast('t'), + static_cast('U'), + static_cast('u'), + static_cast('\xf3'), + static_cast('\xf3'), + static_cast('\xef'), + static_cast('\xef'), + static_cast('\xf1'), + static_cast('\xf1'), + static_cast('\xf2'), + static_cast('\xf2'), + static_cast('\xf4'), + static_cast('\xf4'), // 9: u + static_cast('\xa6'), + static_cast('\xad'), + static_cast('\xf8'), + static_cast('\xf8'), + static_cast('\xf5'), + static_cast('\xf5'), + static_cast('\xf6'), + static_cast('\xf6'), + static_cast('\xf7'), + static_cast('\xf7'), + static_cast('\xf9'), + static_cast('\xf9'), // 10: u+ + static_cast('V'), + static_cast('v'), + static_cast('W'), + static_cast('w'), + static_cast('X'), + static_cast('x'), + static_cast('Y'), + static_cast('y'), + static_cast('\xfd'), + static_cast('\xfd'), + static_cast('\xfa'), + static_cast('\xfa'), + static_cast('\xfb'), + static_cast('\xfb'), + static_cast('\xfc'), + static_cast('\xfc'), + static_cast('\xfe'), + static_cast('\xfe'), // 11: y + static_cast('Z'), + static_cast('z'), + 0x80, + 0x82, + 0x83, + 0x84, + 0x85, + 0x86, + 0x87, + 0x88, + 0x89, + 0x8A, + 0x8B, + 0x8C, + 0x8E, + 0x91, + 0x92, + 0x93, + 0x94, + 0x95, + 0x96, + 0x97, + 0x98, + 0x99, + 0x9A, + 0x9B, + 0x9C, + 0x9E, + 0x9F}, + // VPS + {static_cast('A'), + static_cast('a'), + static_cast('\xc1'), + static_cast('\xe1'), + static_cast('\x80'), + static_cast('\xe0'), + static_cast('\x81'), + static_cast('\xe4'), + static_cast('\x82'), + static_cast('\xe3'), + static_cast('\xe5'), + static_cast('\xe5'), + static_cast('\xc2'), + static_cast('\xe2'), + static_cast('\x83'), + static_cast('\xc3'), + static_cast('\x84'), + static_cast('\xc0'), + static_cast('\x85'), + static_cast('\xc4'), + static_cast('\xc5'), + static_cast('\xc5'), + static_cast('\xc6'), + static_cast('\xc6'), + static_cast('\x88'), + static_cast('\xe6'), + static_cast('\x8d'), + static_cast('\xa1'), + static_cast('\x8e'), + static_cast('\xa2'), + static_cast('\x8f'), + static_cast('\xa3'), + static_cast('\xf0'), + static_cast('\xa4'), + static_cast('\xa5'), + static_cast('\xa5'), + static_cast('B'), + static_cast('b'), + static_cast('C'), + static_cast('c'), + static_cast('D'), + static_cast('d'), + static_cast('\xf1'), + static_cast('\xc7'), + static_cast('E'), + static_cast('e'), + static_cast('\xc9'), + static_cast('\xe9'), + static_cast('\xd7'), + static_cast('\xe8'), + static_cast('\xde'), + static_cast('\xc8'), + static_cast('\xfe'), + static_cast('\xeb'), + static_cast('\xcb'), + static_cast('\xcb'), + static_cast('\xca'), + static_cast('\xea'), + static_cast('\x90'), + static_cast('\x89'), + static_cast('\x93'), + static_cast('\x8a'), + static_cast('\x94'), + static_cast('\x8b'), + static_cast('\x95'), + static_cast('\xcd'), + static_cast('\x8c'), + static_cast('\x8c'), + static_cast('F'), + static_cast('f'), + static_cast('G'), + static_cast('g'), + static_cast('H'), + static_cast('h'), + static_cast('I'), + static_cast('i'), + static_cast('\xb4'), + static_cast('\xed'), + static_cast('\xb5'), + static_cast('\xec'), + static_cast('\xb7'), + static_cast('\xcc'), + static_cast('\xb8'), + static_cast('\xef'), + static_cast('\xce'), + static_cast('\xce'), + static_cast('J'), + static_cast('j'), + static_cast('K'), + static_cast('k'), + static_cast('L'), + static_cast('l'), + static_cast('M'), + static_cast('m'), + static_cast('N'), + static_cast('n'), + static_cast('O'), + static_cast('o'), + static_cast('\xb9'), + static_cast('\xf3'), + static_cast('\xbc'), + static_cast('\xf2'), + static_cast('\xbd'), + static_cast('\xd5'), + static_cast('\xbe'), + static_cast('\xf5'), + static_cast('\x86'), + static_cast('\x86'), + static_cast('\xd4'), + static_cast('\xf4'), + static_cast('\x96'), + static_cast('\xd3'), + static_cast('\x97'), + static_cast('\xd2'), + static_cast('\x98'), + static_cast('\xb0'), + static_cast('\x99'), + static_cast('\x87'), + static_cast('\xb6'), + static_cast('\xb6'), + static_cast('\xf7'), + static_cast('\xd6'), + static_cast('\x9d'), + static_cast('\xa7'), + static_cast('\x9e'), + static_cast('\xa9'), + static_cast('\x9f'), + static_cast('\xaa'), + static_cast('\xa6'), + static_cast('\xab'), + static_cast('\xae'), + static_cast('\xae'), + static_cast('P'), + static_cast('p'), + static_cast('Q'), + static_cast('q'), + static_cast('R'), + static_cast('r'), + static_cast('S'), + static_cast('s'), + static_cast('T'), + static_cast('t'), + static_cast('U'), + static_cast('u'), + static_cast('\xda'), + static_cast('\xfa'), + static_cast('\xa8'), + static_cast('\xf9'), + static_cast('\xd1'), + static_cast('\xfb'), + static_cast('\xac'), + static_cast('\xdb'), + static_cast('\xf8'), + static_cast('\xf8'), + static_cast('\xd0'), + static_cast('\xdc'), + static_cast('\xad'), + static_cast('\xd9'), + static_cast('\xaf'), + static_cast('\xd8'), + static_cast('\xb1'), + static_cast('\xba'), + static_cast('\xbb'), + static_cast('\xbb'), + static_cast('\xbf'), + static_cast('\xbf'), + static_cast('V'), + static_cast('v'), + static_cast('W'), + static_cast('w'), + static_cast('X'), + static_cast('x'), + static_cast('Y'), + static_cast('y'), + static_cast('\xdd'), + static_cast('\x9a'), + static_cast('\xb2'), + static_cast('\xff'), + static_cast('\xfd'), + static_cast('\x9b'), + static_cast('\xb3'), + static_cast('\xcf'), + static_cast('\x9c'), + static_cast('\x9c'), + static_cast('Z'), + static_cast('z'), + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x8E, + 0x91, + 0x92, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x9E, + 0x00}, + // VISCII + {static_cast('A'), + static_cast('a'), + static_cast('\xc1'), + static_cast('\xe1'), + static_cast('\xc0'), + static_cast('\xe0'), + static_cast('\xc4'), + static_cast('\xe4'), + static_cast('\xc3'), + static_cast('\xe3'), + static_cast('\x80'), + static_cast('\xd5'), + static_cast('\xc2'), + static_cast('\xe2'), + static_cast('\x84'), + static_cast('\xa4'), + static_cast('\x85'), + static_cast('\xa5'), + static_cast('\x86'), + static_cast('\xa6'), + static_cast('\xe7'), + static_cast('\xe7'), + static_cast('\x87'), + static_cast('\xa7'), + static_cast('\xc5'), + static_cast('\xe5'), + static_cast('\x81'), + static_cast('\xa1'), + static_cast('\x82'), + static_cast('\xa2'), + static_cast('\xc6'), + static_cast('\xc6'), + static_cast('\xc7'), + static_cast('\xc7'), + static_cast('\x83'), + static_cast('\xa3'), + static_cast('B'), + static_cast('b'), + static_cast('C'), + static_cast('c'), + static_cast('D'), + static_cast('d'), + static_cast('\xd0'), + static_cast('\xf0'), + static_cast('E'), + static_cast('e'), + static_cast('\xc9'), + static_cast('\xe9'), + static_cast('\xc8'), + static_cast('\xe8'), + static_cast('\xcb'), + static_cast('\xeb'), + static_cast('\x88'), + static_cast('\xa8'), + static_cast('\x89'), + static_cast('\xa9'), + static_cast('\xca'), + static_cast('\xea'), + static_cast('\x8a'), + static_cast('\xaa'), + static_cast('\x8b'), + static_cast('\xab'), + static_cast('\x8c'), + static_cast('\xac'), + static_cast('\x8d'), + static_cast('\xad'), + static_cast('\x8e'), + static_cast('\xae'), + static_cast('F'), + static_cast('f'), + static_cast('G'), + static_cast('g'), + static_cast('H'), + static_cast('h'), + static_cast('I'), + static_cast('i'), + static_cast('\xcd'), + static_cast('\xed'), + static_cast('\xcc'), + static_cast('\xec'), + static_cast('\x9b'), + static_cast('\xef'), + static_cast('\xce'), + static_cast('\xee'), + static_cast('\x98'), + static_cast('\xb8'), + static_cast('J'), + static_cast('j'), + static_cast('K'), + static_cast('k'), + static_cast('L'), + static_cast('l'), + static_cast('M'), + static_cast('m'), + static_cast('N'), + static_cast('n'), + static_cast('O'), + static_cast('o'), + static_cast('\xd3'), + static_cast('\xf3'), + static_cast('\xd2'), + static_cast('\xf2'), + static_cast('\x99'), + static_cast('\xf6'), + static_cast('\xf5'), + static_cast('\xf5'), + static_cast('\x9a'), + static_cast('\xf7'), + static_cast('\xd4'), + static_cast('\xf4'), + static_cast('\x8f'), + static_cast('\xaf'), + static_cast('\x90'), + static_cast('\xb0'), + static_cast('\x91'), + static_cast('\xb1'), + static_cast('\x92'), + static_cast('\xb2'), + static_cast('\x93'), + static_cast('\xb5'), + static_cast('\xb4'), + static_cast('\xbd'), + static_cast('\x95'), + static_cast('\xbe'), + static_cast('\x96'), + static_cast('\xb6'), + static_cast('\x97'), + static_cast('\xb7'), + static_cast('\xb3'), + static_cast('\xde'), + static_cast('\x94'), + static_cast('\xfe'), + static_cast('P'), + static_cast('p'), + static_cast('Q'), + static_cast('q'), + static_cast('R'), + static_cast('r'), + static_cast('S'), + static_cast('s'), + static_cast('T'), + static_cast('t'), + static_cast('U'), + static_cast('u'), + static_cast('\xda'), + static_cast('\xfa'), + static_cast('\xd9'), + static_cast('\xf9'), + static_cast('\x9c'), + static_cast('\xfc'), + static_cast('\x9d'), + static_cast('\xfb'), + static_cast('\x9e'), + static_cast('\xf8'), + static_cast('\xbf'), + static_cast('\xdf'), + static_cast('\xba'), + static_cast('\xd1'), + static_cast('\xbb'), + static_cast('\xd7'), + static_cast('\xbc'), + static_cast('\xd8'), + static_cast('\xff'), + static_cast('\xe6'), + static_cast('\xb9'), + static_cast('\xf1'), + static_cast('V'), + static_cast('v'), + static_cast('W'), + static_cast('w'), + static_cast('X'), + static_cast('x'), + static_cast('Y'), + static_cast('y'), + static_cast('\xdd'), + static_cast('\xfd'), + static_cast('\x9f'), + static_cast('\xcf'), + static_cast('\xd6'), + static_cast('\xd6'), + static_cast('\xdb'), + static_cast('\xdb'), + static_cast('\xdc'), + static_cast('\xdc'), + static_cast('Z'), + static_cast('z'), + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x8E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x9E, + 0x00}, + // BKHCM1 + {static_cast('A'), + static_cast('a'), + static_cast('\x80'), + static_cast('\xbe'), + static_cast('\x81'), + static_cast('\xbf'), + static_cast('\x82'), + static_cast('\xc0'), + static_cast('\x83'), + static_cast('\xc1'), + static_cast('\x84'), + static_cast('\xc2'), + static_cast('\x9f'), + static_cast('\xdd'), + static_cast('~'), + static_cast('\xde'), + static_cast('\xa1'), + static_cast('\xdf'), + static_cast('\xa2'), + static_cast('\xe0'), + static_cast('\xa3'), + static_cast('\xe1'), + static_cast('\xa4'), + static_cast('\xe2'), + static_cast('\x99'), + static_cast('\xd7'), + static_cast('\x9a'), + static_cast('\xd8'), + static_cast('\x9b'), + static_cast('\xd9'), + static_cast('\x9c'), + static_cast('\xda'), + static_cast('\x9d'), + static_cast('\xdb'), + static_cast('\x98'), + static_cast('\xdc'), + static_cast('B'), + static_cast('b'), + static_cast('C'), + static_cast('c'), + static_cast('D'), + static_cast('d'), + static_cast('}'), + static_cast('\xbd'), + static_cast('E'), + static_cast('e'), + static_cast('\x85'), + static_cast('\xc3'), + static_cast('\x86'), + static_cast('\xc4'), + static_cast('\x87'), + static_cast('\xc5'), + static_cast('\x88'), + static_cast('\xc6'), + static_cast('\x89'), + static_cast('\xc7'), + static_cast('\xa5'), + static_cast('\xe3'), + static_cast('\xa6'), + static_cast('\xe4'), + static_cast('\xa7'), + static_cast('\xe5'), + static_cast('\xa8'), + static_cast('\xe6'), + static_cast('\xa9'), + static_cast('\xe7'), + static_cast('\xaa'), + static_cast('\xe8'), + static_cast('F'), + static_cast('f'), + static_cast('G'), + static_cast('g'), + static_cast('H'), + static_cast('h'), + static_cast('I'), + static_cast('i'), + static_cast('\x8a'), + static_cast('\xc8'), + static_cast('\x8b'), + static_cast('\xc9'), + static_cast('\x8c'), + static_cast('\xca'), + static_cast('\x8d'), + static_cast('\xcb'), + static_cast('\x8e'), + static_cast('\xcc'), + static_cast('J'), + static_cast('j'), + static_cast('K'), + static_cast('k'), + static_cast('L'), + static_cast('l'), + static_cast('M'), + static_cast('m'), + static_cast('N'), + static_cast('n'), + static_cast('O'), + static_cast('o'), + static_cast('\x8f'), + static_cast('\xcd'), + static_cast('\x90'), + static_cast('\xce'), + static_cast('\x91'), + static_cast('\xcf'), + static_cast('\x92'), + static_cast('\xd0'), + static_cast('\x93'), + static_cast('\xd1'), + static_cast('\xab'), + static_cast('\xe9'), + static_cast('\xac'), + static_cast('\xea'), + static_cast('\xad'), + static_cast('\xeb'), + static_cast('\xae'), + static_cast('\xec'), + static_cast('\xaf'), + static_cast('\xed'), + static_cast('\xb0'), + static_cast('\xee'), + static_cast('\xb1'), + static_cast('\xef'), + static_cast('\xb2'), + static_cast('\xf0'), + static_cast('\xb3'), + static_cast('\xf1'), + static_cast('\xb4'), + static_cast('\xf2'), + static_cast('\xb5'), + static_cast('\xf3'), + static_cast('\xb6'), + static_cast('\xf4'), + static_cast('P'), + static_cast('p'), + static_cast('Q'), + static_cast('q'), + static_cast('R'), + static_cast('r'), + static_cast('S'), + static_cast('s'), + static_cast('T'), + static_cast('t'), + static_cast('U'), + static_cast('u'), + static_cast('\x94'), + static_cast('\xd2'), + static_cast('\x95'), + static_cast('\xd3'), + static_cast('\x96'), + static_cast('\xd4'), + static_cast('\x97'), + static_cast('\xd5'), + static_cast('\x98'), + static_cast('\xd6'), + static_cast('\xb7'), + static_cast('\xf5'), + static_cast('\xb8'), + static_cast('\xf6'), + static_cast('\xb9'), + static_cast('\xf7'), + static_cast('\xba'), + static_cast('\xf8'), + static_cast('\xbb'), + static_cast('\xf9'), + static_cast('\xbc'), + static_cast('\xfa'), + static_cast('V'), + static_cast('v'), + static_cast('W'), + static_cast('w'), + static_cast('X'), + static_cast('x'), + static_cast('Y'), + static_cast('y'), + static_cast('{'), + static_cast('\xfb'), + static_cast('^'), + static_cast('\xfc'), + static_cast('`'), + static_cast('\xfd'), + static_cast('|'), + static_cast('\xfe'), + static_cast('\x8e'), + static_cast('\xff'), + static_cast('Z'), + static_cast('z'), + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x8E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x9E, + 0x00}, + // Vietware-F + {static_cast('A'), + static_cast('a'), + static_cast('\xc0'), + static_cast('\xc0'), + static_cast('\xaa'), + static_cast('\xaa'), + static_cast('\xb6'), + static_cast('\xb6'), + static_cast('\xba'), + static_cast('\xba'), + static_cast('\xc1'), + static_cast('\xc1'), + static_cast('\x97'), + static_cast('\xa1'), + static_cast('\xca'), + static_cast('\xca'), + static_cast('\xc7'), + static_cast('\xc7'), + static_cast('\xc8'), + static_cast('\xc8'), + static_cast('\xc9'), + static_cast('\xc9'), + static_cast('\xcb'), + static_cast('\xcb'), + static_cast('\x96'), + static_cast('\x9f'), + static_cast('\xc5'), + static_cast('\xc5'), + static_cast('\xc2'), + static_cast('\xc2'), + static_cast('\xc3'), + static_cast('\xc3'), + static_cast('\xc4'), + static_cast('\xc4'), + static_cast('\xc6'), + static_cast('\xc6'), + static_cast('B'), + static_cast('b'), + static_cast('C'), + static_cast('c'), + static_cast('D'), + static_cast('d'), + static_cast('\x98'), + static_cast('\xa2'), + static_cast('E'), + static_cast('e'), + static_cast('\xcf'), + static_cast('\xcf'), + static_cast('\xcc'), + static_cast('\xcc'), + static_cast('\xcd'), + static_cast('\xcd'), + static_cast('\xce'), + static_cast('\xce'), + static_cast('\xd1'), + static_cast('\xd1'), + static_cast('\x99'), + static_cast('\xa3'), + static_cast('\xd5'), + static_cast('\xd5'), + static_cast('\xd2'), + static_cast('\xd2'), + static_cast('\xd3'), + static_cast('\xd3'), + static_cast('\xd4'), + static_cast('\xd4'), + static_cast('\xd6'), + static_cast('\xd6'), + static_cast('F'), + static_cast('f'), + static_cast('G'), + static_cast('g'), + static_cast('H'), + static_cast('h'), + static_cast('I'), + static_cast('i'), + static_cast('\xdb'), + static_cast('\xdb'), + static_cast('\xd8'), + static_cast('\xd8'), + static_cast('\xd9'), + static_cast('\xd9'), + static_cast('\xda'), + static_cast('\xda'), + static_cast('\xdc'), + static_cast('\xdc'), + static_cast('J'), + static_cast('j'), + static_cast('K'), + static_cast('k'), + static_cast('L'), + static_cast('l'), + static_cast('M'), + static_cast('m'), + static_cast('N'), + static_cast('n'), + static_cast('O'), + static_cast('o'), + static_cast('\xe2'), + static_cast('\xe2'), + static_cast('\xdf'), + static_cast('\xdf'), + static_cast('\xe0'), + static_cast('\xe0'), + static_cast('\xe1'), + static_cast('\xe1'), + static_cast('\xe3'), + static_cast('\xe3'), + static_cast('\x9a'), + static_cast('\xa4'), + static_cast('\xe7'), + static_cast('\xe7'), + static_cast('\xe4'), + static_cast('\xe4'), + static_cast('\xe5'), + static_cast('\xe5'), + static_cast('\xe6'), + static_cast('\xe6'), + static_cast('\xe8'), + static_cast('\xe8'), + static_cast('\x9b'), + static_cast('\xa5'), + static_cast('\xec'), + static_cast('\xec'), + static_cast('\xe9'), + static_cast('\xe9'), + static_cast('\xea'), + static_cast('\xea'), + static_cast('\xeb'), + static_cast('\xeb'), + static_cast('\xed'), + static_cast('\xed'), + static_cast('P'), + static_cast('p'), + static_cast('Q'), + static_cast('q'), + static_cast('R'), + static_cast('r'), + static_cast('S'), + static_cast('s'), + static_cast('T'), + static_cast('t'), + static_cast('U'), + static_cast('u'), + static_cast('\xf2'), + static_cast('\xf2'), + static_cast('\xee'), + static_cast('\xee'), + static_cast('\xef'), + static_cast('\xef'), + static_cast('\xf1'), + static_cast('\xf1'), + static_cast('\xf3'), + static_cast('\xf3'), + static_cast('\x9c'), + static_cast('\xa7'), + static_cast('\xf7'), + static_cast('\xf7'), + static_cast('\xf4'), + static_cast('\xf4'), + static_cast('\xf5'), + static_cast('\xf5'), + static_cast('\xf6'), + static_cast('\xf6'), + static_cast('\xf8'), + static_cast('\xf8'), + static_cast('V'), + static_cast('v'), + static_cast('W'), + static_cast('w'), + static_cast('X'), + static_cast('x'), + static_cast('Y'), + static_cast('y'), + static_cast('\xfc'), + static_cast('\xfc'), + static_cast('\xf9'), + static_cast('\xf9'), + static_cast('\xfa'), + static_cast('\xfa'), + static_cast('\xfb'), + static_cast('\xfb'), + static_cast('\xff'), + static_cast('\xff'), + static_cast('Z'), + static_cast('z'), + 0x80, + 0x82, + 0x83, + 0x84, + 0x85, + 0x86, + 0x87, + 0x88, + 0x89, + 0x8A, + 0x8B, + 0x8C, + 0x8E, + 0x91, + 0x92, + 0x93, + 0x94, + 0x95, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x9E, + 0x00}, + // ISC + {static_cast('A'), + static_cast('a'), + static_cast('\x83'), + static_cast('\xb8'), + static_cast('\x80'), + static_cast('\xb5'), + static_cast('\x81'), + static_cast('\xb6'), + static_cast('\x82'), + static_cast('\xb7'), + static_cast('\x84'), + static_cast('\xb9'), + static_cast('\xa2'), + static_cast('\xa9'), + static_cast('\xc4'), + static_cast('\xa0'), + static_cast('\xc1'), + static_cast('\xc7'), + static_cast('\xc2'), + static_cast('\xc8'), + static_cast('\xc3'), + static_cast('\xc9'), + static_cast('\x86'), + static_cast('\xcb'), + static_cast('\xa1'), + static_cast('\xa8'), + static_cast('\xc0'), + static_cast('\xbe'), + static_cast('\xaf'), + static_cast('\xbb'), + static_cast('\xba'), + static_cast('\xbc'), + static_cast('\xbf'), + static_cast('\xbd'), + static_cast('\x85'), + static_cast('\xc6'), + static_cast('B'), + static_cast('b'), + static_cast('C'), + static_cast('c'), + static_cast('D'), + static_cast('d'), + static_cast('\xa7'), + static_cast('\xae'), + static_cast('E'), + static_cast('e'), + static_cast('\xd0'), + static_cast('\x8a'), + static_cast('\x87'), + static_cast('\xcc'), + static_cast('\x88'), + static_cast('\xce'), + static_cast('\x89'), + static_cast('\xcf'), + static_cast('\xd1'), + static_cast('\x8b'), + static_cast('\xa3'), + static_cast('\xaa'), + static_cast('\xda'), + static_cast('\xd5'), + static_cast('\xc5'), + static_cast('\xd2'), + static_cast('\xcd'), + static_cast('\xd3'), + static_cast('\xd9'), + static_cast('\xd4'), + static_cast('\x8c'), + static_cast('\xd6'), + static_cast('F'), + static_cast('f'), + static_cast('G'), + static_cast('g'), + static_cast('H'), + static_cast('h'), + static_cast('I'), + static_cast('i'), + static_cast('\x90'), + static_cast('\xdd'), + static_cast('\x8d'), + static_cast('\xd7'), + static_cast('\x8e'), + static_cast('\xd8'), + static_cast('\x8f'), + static_cast('\xdc'), + static_cast('\x91'), + static_cast('\xde'), + static_cast('J'), + static_cast('j'), + static_cast('K'), + static_cast('k'), + static_cast('L'), + static_cast('l'), + static_cast('M'), + static_cast('m'), + static_cast('N'), + static_cast('n'), + static_cast('O'), + static_cast('o'), + static_cast('\x95'), + static_cast('\xe3'), + static_cast('\x92'), + static_cast('\xdf'), + static_cast('\x93'), + static_cast('\xe1'), + static_cast('\x94'), + static_cast('\xe2'), + static_cast('\x96'), + static_cast('\xe4'), + static_cast('\xa4'), + static_cast('\xab'), + static_cast('\xff'), + static_cast('\xe8'), + static_cast('\xdb'), + static_cast('\xe5'), + static_cast('\xe0'), + static_cast('\xe6'), + static_cast('\xf0'), + static_cast('\xe7'), + static_cast('\x97'), + static_cast('\xe9'), + static_cast('\xa5'), + static_cast('\xac'), + static_cast('\x9b'), + static_cast('\xed'), + static_cast('\x98'), + static_cast('\xea'), + static_cast('\x99'), + static_cast('\xeb'), + static_cast('\x9a'), + static_cast('\xec'), + static_cast('\x9c'), + static_cast('\xee'), + static_cast('P'), + static_cast('p'), + static_cast('Q'), + static_cast('q'), + static_cast('R'), + static_cast('r'), + static_cast('S'), + static_cast('s'), + static_cast('T'), + static_cast('t'), + static_cast('U'), + static_cast('u'), + static_cast('@'), + static_cast('\xf3'), + static_cast('\x9d'), + static_cast('\xef'), + static_cast('\x9e'), + static_cast('\xf1'), + static_cast('\x9f'), + static_cast('\xf2'), + static_cast('|'), + static_cast('\xf4'), + static_cast('\xa6'), + static_cast('\xad'), + static_cast('`'), + static_cast('\xf8'), + 0x5C, + static_cast('\xf5'), + static_cast('^'), + static_cast('\xf6'), + static_cast('~'), + static_cast('\xf7'), + static_cast('#'), + static_cast('\xf9'), + static_cast('V'), + static_cast('v'), + static_cast('W'), + static_cast('w'), + static_cast('X'), + static_cast('x'), + static_cast('Y'), + static_cast('y'), + static_cast('\xb3'), + static_cast('\xfd'), + static_cast('\xb0'), + static_cast('\xfa'), + static_cast('\xb1'), + static_cast('\xfb'), + static_cast('\xb2'), + static_cast('\xfc'), + static_cast('\xb4'), + static_cast('\xfe'), + static_cast('Z'), + static_cast('z'), + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x11, + 0x12, + 0x13, + 0x14, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00}}; + +UKWORD DoubleByteTables[][TOTAL_VNCHARS] = { + // VNI-WIN + {0x0041, 0x0061, 0xd941, 0xf961, 0xd841, 0xf861, 0xdb41, 0xfb61, 0xd541, + 0xf561, 0xcf41, 0xef61, // a + 0xc241, 0xe261, 0xc141, 0xe161, 0xc041, 0xe061, 0xc541, 0xe561, 0xc341, + 0xe361, 0xc441, 0xe461, // a^ + 0xca41, 0xea61, 0xc941, 0xe961, 0xc841, 0xe861, 0xda41, 0xfa61, 0xdc41, + 0xfc61, 0xcb41, 0xeb61, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d1, 0x00f1, // DD, dd + 0x0045, 0x0065, 0xd945, 0xf965, 0xd845, 0xf865, 0xdb45, 0xfb65, 0xd545, + 0xf565, 0xcf45, 0xef65, // e + 0xc245, 0xe265, 0xc145, 0xe165, 0xc045, 0xe065, 0xc545, 0xe565, 0xc345, + 0xe365, 0xc445, 0xe465, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x00c6, 0x00e6, 0x00d3, + 0x00f3, 0x00d2, 0x00f2, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xd94f, 0xf96f, 0xd84f, 0xf86f, 0xdb4f, 0xfb6f, 0xd54f, + 0xf56f, 0xcf4f, 0xef6f, // o + 0xc24f, 0xe26f, 0xc14f, 0xe16f, 0xc04f, 0xe06f, 0xc54f, 0xe56f, 0xc34f, + 0xe36f, 0xc44f, 0xe46f, // o^ + 0x00d4, 0x00f4, 0xd9d4, 0xf9f4, 0xd8d4, 0xf8f4, 0xdbd4, 0xfbf4, 0xd5d4, + 0xf5f4, 0xcfd4, 0xeff4, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xd955, 0xf975, 0xd855, 0xf875, 0xdb55, 0xfb75, 0xd555, + 0xf575, 0xcf55, 0xef75, // u + 0x00d6, 0x00f6, 0xd9d6, 0xf9f6, 0xd8d6, 0xf8f6, 0xdbd6, 0xfbf6, 0xd5d6, + 0xf5f6, 0xcfd6, 0xeff6, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xd959, 0xf979, 0xd859, 0xf879, 0xdb59, 0xfb79, 0xd559, + 0xf579, 0x00ce, 0x00ee, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + // BKHCM2 + {0x0041, 0x0061, 0xC141, 0xe161, 0xC241, 0xe261, 0xC341, + 0xe361, 0xC441, 0xe461, 0xC541, 0xe561, // a + 0x00CA, 0x00EA, 0xCBCA, 0xEBEA, 0xCCCA, 0xECEA, 0xCDCA, + 0xEDEA, 0xCECA, 0xEEEA, 0xC5CA, 0xE5EA, // a^ + 0x00D9, 0x00F9, 0xC6D9, 0xE6F9, 0xC7D9, 0xE7F9, 0xC8D9, + 0xE8F9, 0xC9D9, 0xE9F9, 0xC5D9, 0xE5F9, 0x0042, 0x0062, + 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00C0, 0x00E0, 0x0045, 0x0065, 0xC145, 0xE165, 0xC245, + 0xE265, 0xC345, 0xE365, 0xC445, 0xE465, 0xC545, 0xE565, // e + 0x00CF, 0x00EF, 0xCBCF, 0xEBEF, 0xCCCF, 0xECEF, 0xCDCF, + 0xEDEF, 0xCECF, 0xEEEF, 0xE5CF, 0xE5EF, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00D1, 0x00F1, 0x00D2, 0x00F2, 0x00D3, + 0x00F3, 0x00D4, 0x00F4, 0x00D5, 0x00F5, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, + 0x006d, 0x004e, 0x006e, // J j K k L l M m N n + 0x004F, 0x006F, 0xC14F, 0xE16F, 0xC24F, 0xE26F, 0xC34F, + 0xE36F, 0xC44F, 0xE46F, 0xC54F, 0xE56F, // o + 0x00D6, 0x00F6, 0xCBD6, 0xEBF6, 0xCCD6, 0xECF6, 0xCDD6, + 0xEDF6, 0xCED6, 0xEEF6, 0xC5D6, 0xE5F6, // o^ + 0x00DA, 0x00FA, 0xC1DA, 0xE1FA, 0xC2DA, 0xE2FA, 0xC3DA, + 0xE3FA, 0xC4DA, 0xE4FA, 0xC5DA, 0xE5FA, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, + 0x0073, 0x0054, 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xC155, 0xE175, 0xC255, 0xE275, 0xC355, + 0xE375, 0xC455, 0xE475, 0xC555, 0xE575, // u + 0x00DB, 0x00FB, 0xC1DB, 0xE1FB, 0xC2DB, 0xE2FB, 0xC3DB, + 0xE3FB, 0xC4DB, 0xE4FB, 0xC5DB, 0xE5FB, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xC159, 0xE179, 0xC259, 0xE279, 0xC359, + 0xE379, 0xC459, 0xE479, 0xC559, 0xE579, 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, + 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, + 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + // VIETWARE-X + {0x0041, 0x0061, 0xCF41, 0xEF61, 0xCC41, 0xEC61, 0xCD41, 0xED61, 0xCE41, + 0xEE61, 0xDB41, 0xFB61, // a + 0x00C1, 0x00E1, 0xDAC1, 0xFAE1, 0xD6C1, 0xF6E1, 0xD8C1, 0xF8E1, 0xD9C1, + 0xF9E1, 0xDBC1, 0xFBE1, // a^ + 0x00C0, 0x00E0, 0xD5C0, 0xF5E0, 0xD2C0, 0xF2E0, 0xD3C0, 0xF3E0, 0xD4C0, + 0xF4E0, 0xDBC0, 0xFBE0, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00C2, 0x00E2, 0x0045, 0x0065, 0xCF45, 0xEF65, 0xCC45, 0xEC65, 0xCD45, + 0xED65, 0xCE45, 0xEE65, 0xDB45, 0xFB65, // e + 0x00C3, 0x00E3, 0xDAC3, 0xFAE3, 0xD6C3, 0xF6E3, 0xD8C3, 0xF8E3, 0xD9C3, + 0xF9E3, 0xDBC3, 0xFBE3, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00CA, 0x00EA, 0x00C7, 0x00E7, 0x00C8, 0x00E8, 0x00C9, + 0x00E9, 0x00CB, 0x00EB, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004F, 0x006F, 0xCF4F, 0xEF6F, 0xCC4F, 0xEC6F, 0xCD4F, 0xED6F, 0xCE4F, + 0xEE6F, 0xDC4F, 0xFC6F, // o + 0x00C4, 0x00E4, 0xDAC4, 0xFAE4, 0xD6C4, 0xF6E4, 0xD8C4, 0xF8E4, 0xD9C4, + 0xF9E4, 0xDCC4, 0xFCE4, // o^ + 0x00C5, 0x00E5, 0xCFC5, 0xEFE5, 0xCCC5, 0xECE5, 0xCDC5, 0xEDE5, 0xCEC5, + 0xEEE5, 0xDCC5, 0xFCE5, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xCF55, 0xEF75, 0xCC55, 0xEC75, 0xCD55, 0xED75, 0xCE55, + 0xEE75, 0xDB55, 0xFB75, // u + 0x00C6, 0x00E6, 0xCFC6, 0xEFE6, 0xCCC6, 0xECE6, 0xCDC6, 0xEDE6, 0xCEC6, + 0xEEE6, 0xDBC6, 0xFBE6, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xCF59, 0xEF79, 0xCC59, 0xEC79, 0xCD59, 0xED79, 0xCE59, + 0xEE79, 0xD159, 0xF179, // Y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + // VNI-MAC + {0x0041, 0x0061, 0xf441, 0x9d61, 0xaf41, 0xbf61, 0xf341, 0x9e61, 0xcd41, + 0x9b61, 0xec41, 0x9561, // a + 0xe541, 0x8961, 0xe741, 0x8761, 0xcb41, 0x8861, 0x8141, 0x8c61, 0xcc41, + 0x8b61, 0x8041, 0x8a61, // a^ + 0xe641, 0x9061, 0x8341, 0x8e61, 0xe941, 0x8f61, 0xf241, 0x9c61, 0x8641, + 0x9f61, 0xe841, 0x9161, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x0084, 0x0096, // DD, dd + 0x0045, 0x0065, 0xf445, 0x9d65, 0xaf45, 0xbf65, 0xf345, 0x9e65, 0xcd45, + 0x9b65, 0xec45, 0x9565, // e + 0xe545, 0x8965, 0xe745, 0x8765, 0xcb45, 0x8865, 0x8145, 0x8c65, 0xcc45, + 0x8b65, 0x8045, 0x8a65, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00ea, 0x0092, 0x00ed, 0x0093, 0x00ae, 0x00be, 0x00ee, + 0x0097, 0x00f1, 0x0098, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xf44f, 0x9d6f, 0xaf4f, 0xbf6f, 0xf34f, 0x9e6f, 0xcd4f, + 0x9b6f, 0xec4f, 0x956f, // o + 0xe54f, 0x896f, 0xe74f, 0x876f, 0xcb4f, 0x886f, 0x814f, 0x8c6f, 0xcc4f, + 0x8b6f, 0x804f, 0x8a6f, // o^ + 0x00ef, 0x0099, 0xf4ef, 0x9d99, 0xafef, 0xbf99, 0xf3ef, 0x9e99, 0xcdef, + 0x9b99, 0xecef, 0x9599, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xf455, 0x9d75, 0xaf55, 0xbf75, 0xf355, 0x9e75, 0xcd55, + 0x9b75, 0xec55, 0x9575, // u + 0x0085, 0x009a, 0xf485, 0x9d9a, 0xaf85, 0xbf9a, 0xf385, 0x9e9a, 0xcd85, + 0x9b9a, 0xec85, 0x959a, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xf459, 0x9d79, 0xaf59, 0xbf79, 0xf359, 0x9e79, 0xcd59, + 0x9b79, 0x00eb, 0x0094, // y + 0x005a, 0x007a, // Z z + 0x00db, 0x00e2, 0x00c4, 0x00e3, 0x00c9, 0x00a0, 0x00e0, 0x00f6, 0x00e4, + 0x003f, 0x00dc, 0x00ce, 0x003f, 0x00d4, 0x00d5, 0x00d2, 0x00d3, 0x00a5, + 0x00d0, 0x00d1, 0x00f7, 0x00aa, 0x003f, 0x00dd, 0x00cf, 0x003f, 0x00d9}}; + +UKWORD WinCP1258[TOTAL_VNCHARS] = + // Windows CP 1258 + {0x0041, 0x0061, 0xec41, 0xec61, 0xcc41, 0xcc61, 0xd241, 0xd261, 0xde41, + 0xde61, 0xf241, 0xf261, // a + 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, + 0xdee2, 0xf2c2, 0xf2e2, // a^ + 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, + 0xdee3, 0xf2c3, 0xf2e3, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d0, 0x00f0, // DD, dd + 0x0045, 0x0065, 0xec45, 0xec65, 0xcc45, 0xcc65, 0xd245, 0xd265, 0xde45, + 0xde65, 0xf245, 0xf265, // e + 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, + 0xdeea, 0xf2ca, 0xf2ea, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0xec49, 0xec69, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, + 0xde69, 0xf249, 0xf269, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xec4f, 0xec6f, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, + 0xde6f, 0xf24f, 0xf26f, // o + 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, + 0xdef4, 0xf2d4, 0xf2f4, // o^ + 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, + 0xdef5, 0xf2d5, 0xf2f5, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xec55, 0xec75, 0xcc55, 0xcc75, 0xd255, 0xd275, 0xde55, + 0xde75, 0xf255, 0xf275, // u + 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, + 0xdefd, 0xf2dd, 0xf2fd, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, + 0xde79, 0xf259, 0xf279, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; + +UKWORD WinCP1258Pre[TOTAL_VNCHARS] = + // Windows CP1258 - with some more precomposed characters + {0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0xd241, 0xd261, 0xde41, + 0xde61, 0xf241, 0xf261, // a + 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, + 0xdee2, 0xf2c2, 0xf2e2, // a^ + 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, + 0xdee3, 0xf2c3, 0xf2e3, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d0, 0x00f0, // DD, dd + 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0xd245, 0xd265, 0xde45, + 0xde65, 0xf245, 0xf265, // e + 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, + 0xdeea, 0xf2ca, 0xf2ea, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, + 0xde69, 0xf249, 0xf269, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0x00d3, 0x00f3, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, + 0xde6f, 0xf24f, 0xf26f, // o + 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, + 0xdef4, 0xf2d4, 0xf2f4, // o^ + 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, + 0xdef5, 0xf2d5, 0xf2f5, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0xd255, 0xd275, 0xde55, + 0xde75, 0xf255, 0xf275, // u + 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, + 0xdefd, 0xf2dd, 0xf2fd, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, + 0xde79, 0xf259, 0xf279, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; + +UnicodeChar UnicodeTable[TOTAL_VNCHARS] = { + 0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0x1ea2, 0x1ea3, 0x00c3, + 0x00e3, 0x1ea0, 0x1ea1, // a + 0x00c2, 0x00e2, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7, 0x1ea8, 0x1ea9, 0x1eaa, + 0x1eab, 0x1eac, 0x1ead, // a^ + 0x0102, 0x0103, 0x1eae, 0x1eaf, 0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, + 0x1eb5, 0x1eb6, 0x1eb7, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x0110, 0x0111, // DD, dd + 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0x1eba, 0x1ebb, 0x1ebc, + 0x1ebd, 0x1eb8, 0x1eb9, // e + 0x00ca, 0x00ea, 0x1ebe, 0x1ebf, 0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, + 0x1ec5, 0x1ec6, 0x1ec7, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x1ec8, 0x1ec9, 0x0128, + 0x0129, 0x1eca, 0x1ecb, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0x00d3, 0x00f3, 0x00d2, 0x00f2, 0x1ece, 0x1ecf, 0x00d5, + 0x00f5, 0x1ecc, 0x1ecd, // o + 0x00d4, 0x00f4, 0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, + 0x1ed7, 0x1ed8, 0x1ed9, // o^ + 0x01a0, 0x01a1, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf, 0x1ee0, + 0x1ee1, 0x1ee2, 0x1ee3, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0x1ee6, 0x1ee7, 0x0168, + 0x0169, 0x1ee4, 0x1ee5, // u + 0x01af, 0x01b0, 0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, + 0x1eef, 0x1ef0, 0x1ef1, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0x00dd, 0x00fd, 0x1ef2, 0x1ef3, 0x1ef6, 0x1ef7, 0x1ef8, + 0x1ef9, 0x1ef4, 0x1ef5, // y + 0x005a, 0x007a, // Z z + // Symbols that have different code points in Unicode and Western charsets + 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, + 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, + 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; + +/* +unsigned char WesternSymbols[] = + {0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, + 0x9C, 0x9E, 0x9F}; +*/ + +/* +' 0x27 +` 0x60 +? 0x3f +~ 0x7e +. 0x2e + +^ 0x5e +( 0x28 ++ 0x2b + +*/ +UKDWORD VIQRTable[TOTAL_VNCHARS] = { + 0x41, 0x61, 0x2741, 0x2761, 0x6041, 0x6061, 0x3f41, + 0x3f61, 0x7e41, 0x7e61, 0x2e41, 0x2e61, // a + 0x5e41, 0x5e61, 0x275e41, 0x275e61, 0x605e41, 0x605e61, 0x3f5e41, + 0x3f5e61, 0x7e5e41, 0x7e5e61, 0x2e5e41, 0x2e5e61, // a^ + 0x2841, 0x2861, 0x272841, 0x272861, 0x602841, 0x602861, 0x3f2841, + 0x3f2861, 0x7e2841, 0x7e2861, 0x2e2841, 0x2e2861, // a( + 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, // B b C c D d + 0x4444, 0x6464, // DD, dd + 0x45, 0x65, 0x2745, 0x2765, 0x6045, 0x6065, 0x3f45, + 0x3f65, 0x7e45, 0x7e65, 0x2e45, 0x2e65, // e + 0x5e45, 0x5e65, 0x275e45, 0x275e65, 0x605e45, 0x605e65, 0x3f5e45, + 0x3f5e65, 0x7e5e45, 0x7e5e65, 0x2e5e45, 0x2e5e65, // e^ + 0x46, 0x66, 0x47, 0x67, 0x48, 0x68, // F f G g H h + 0x49, 0x69, 0x2749, 0x2769, 0x6049, 0x6069, 0x3f49, + 0x3f69, 0x7e49, 0x7e69, 0x2e49, 0x2e69, // i + 0x4a, 0x6a, 0x4b, 0x6b, 0x4c, 0x6c, 0x4d, + 0x6d, 0x4e, 0x6e, // J j K k L l M m N n + 0x4f, 0x6f, 0x274f, 0x276f, 0x604f, 0x606f, 0x3f4f, + 0x3f6f, 0x7e4f, 0x7e6f, 0x2e4f, 0x2e6f, // o + 0x5e4f, 0x5e6f, 0x275e4f, 0x275e6f, 0x605e4f, 0x605e6f, 0x3f5e4f, + 0x3f5e6f, 0x7e5e4f, 0x7e5e6f, 0x2e5e4f, 0x2e5e6f, // o^ + 0x2b4f, 0x2b6f, 0x272b4f, 0x272b6f, 0x602b4f, 0x602b6f, 0x3f2b4f, + 0x3f2b6f, 0x7e2b4f, 0x7e2b6f, 0x2e2b4f, 0x2e2b6f, // o+ + 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, + 0x73, 0x54, 0x74, // P p Q q R r S s T t + 0x55, 0x75, 0x2755, 0x2775, 0x6055, 0x6075, 0x3f55, + 0x3f75, 0x7e55, 0x7e75, 0x2e55, 0x2e75, // u + 0x2b55, 0x2b75, 0x272b55, 0x272b75, 0x602b55, 0x602b75, 0x3f2b55, + 0x3f2b75, 0x7e2b55, 0x7e2b75, 0x2e2b55, 0x2e2b75, // u+ + 0x56, 0x76, 0x57, 0x77, 0x58, 0x78, // V v W w X x + 0x59, 0x79, 0x2759, 0x2779, 0x6059, 0x6079, 0x3f59, + 0x3f79, 0x7e59, 0x7e79, 0x2e59, 0x2e79, 0x5a, 0x7a, // Z z + 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F}; + +UKDWORD UnicodeComposite[TOTAL_VNCHARS] = { + 0x00000041, 0x00000061, 0x03010041, 0x03010061, 0x03000041, 0x03000061, // a + 0x03090041, 0x03090061, 0x03030041, 0x03030061, 0x03230041, 0x03230061, // a + + 0x000000c2, 0x000000e2, 0x030100c2, 0x030100e2, 0x030000c2, 0x030000e2, + 0x030900c2, 0x030900e2, 0x030300c2, 0x030300e2, 0x032300c2, + 0x032300e2, // a^ + + 0x00000102, 0x00000103, 0x03010102, 0x03010103, 0x03000102, 0x03000103, + 0x03090102, 0x03090103, 0x03030102, 0x03030103, 0x03230102, + 0x03230103, // a( + + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x0110, 0x0111, // 0x00d1, 0x00f1, //DD, dd + + 0x00000045, 0x00000065, 0x03010045, 0x03010065, 0x03000045, 0x03000065, + 0x03090045, 0x03090065, 0x03030045, 0x03030065, 0x03230045, 0x03230065, // e + + 0x000000ca, 0x000000ea, 0x030100ca, 0x030100ea, 0x030000ca, 0x030000ea, + 0x030900ca, 0x030900ea, 0x030300ca, 0x030300ea, 0x032300ca, + 0x032300ea, // e^ + + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + + 0x00000049, 0x00000069, 0x03010049, 0x03010069, 0x03000049, 0x03000069, + 0x03090049, 0x03090069, 0x03030049, 0x03030069, 0x03230049, 0x03230069, // i + + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + + 0x0000004f, 0x0000006f, 0x0301004f, 0x0301006f, 0x0300004f, 0x0300006f, + 0x0309004f, 0x0309006f, 0x0303004f, 0x0303006f, 0x0323004f, 0x0323006f, // o + + 0x000000d4, 0x000000f4, 0x030100d4, 0x030100f4, 0x030000d4, 0x030000f4, + 0x030900d4, 0x030900f4, 0x030300d4, 0x030300f4, 0x032300d4, + 0x032300f4, // o^ + + 0x000001a0, 0x000001a1, 0x030101a0, 0x030101a1, 0x030001a0, 0x030001a1, + 0x030901a0, 0x030901a1, 0x030301a0, 0x030301a1, 0x032301a0, + 0x032301a1, // o+ + + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + + 0x00000055, 0x00000075, 0x03010055, 0x03010075, 0x03000055, 0x03000075, + 0x03090055, 0x03090075, 0x03030055, 0x03030075, 0x03230055, 0x03230075, // u + + 0x000001af, 0x000001b0, 0x030101af, 0x030101b0, 0x030001af, 0x030001b0, + 0x030901af, 0x030901b0, 0x030301af, 0x030301b0, 0x032301af, + 0x032301b0, // u+ + + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + + 0x00000059, 0x00000079, 0x03010059, 0x03010079, 0x03000059, 0x03000079, + 0x03090059, 0x03090079, 0x03030059, 0x03030079, 0x03230059, 0x03230079, // y + 0x005a, 0x007a, // Z z + // Symbols that have different code points in Unicode and Western charsets + 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, + 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, + 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; + +int StdVnRootChar[TOTAL_VNCHARS] = { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a^ -> a + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a( -> a + 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] + 40, 41, // DD dd [mapped to D, d] + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 4: e^ -> e + 68, 69, 70, 71, 72, 73, // fgh + 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 7: o^ -> o + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 8: o+ -> o + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 10: u+ -> u + 166, 167, 168, 169, 170, 171, // vwx + 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] + 184, 185, // z + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; + +int StdVnNoTone[TOTAL_VNCHARS] = { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, // a^ + 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, // a( + 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] + 42, 43, // DD dd + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] + 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, // 4: e^ + 68, 69, 70, 71, 72, 73, // fgh + 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] + 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, // 7: o^ + 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, // 8: o+ + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] + 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, // 10: u+ + 166, 167, 168, 169, 170, 171, // vwx + 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] + 184, 185, // z + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; diff --git a/unikey/core/data.h b/unikey/core/data.h new file mode 100644 index 00000000..fa8e97b3 --- /dev/null +++ b/unikey/core/data.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef VIETNAMESE_CHARSET_DATA_H +#define VIETNAMESE_CHARSET_DATA_H + +// This header defines some special characters +const StdVnChar StdStartQuote = + (VnStdCharOffset + 201); // 0x93 in the Western charset +// 201 is the offset of character 0x93 (start quote) in Vn charsets +const StdVnChar StdEndQuote = + (VnStdCharOffset + 202); // 0x94 in the Western charset +const StdVnChar StdEllipsis = + (VnStdCharOffset + 190); // 0x85 in Western charet. + +#endif diff --git a/unikey/core/inputproc.cpp b/unikey/core/inputproc.cpp new file mode 100644 index 00000000..cafa4199 --- /dev/null +++ b/unikey/core/inputproc.cpp @@ -0,0 +1,304 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ +#include "inputproc.h" +#include +#include +#include + +using namespace std; + +/* +unsigned char WordBreakSyms[] = { + ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', + '<', '>', '=', '+', '-', '*', '/', '\\', + '_', '~', '`', '@', '#', '$', '%', '^', '&', '(', ')', '{', '}', '[', ']'}; +*/ + +const std::unordered_set WordBreakSyms = { + ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', '<', + '>', '=', '+', '-', '*', '/', '\\', '_', '@', '#', + '$', '%', '&', '(', ')', '{', '}', '[', ']', '|'}; // we excluded ~, `, ^ + +VnLexiName AZLexiUpper[] = {vnl_A, vnl_B, vnl_C, vnl_D, vnl_E, vnl_F, vnl_G, + vnl_H, vnl_I, vnl_J, vnl_K, vnl_L, vnl_M, vnl_N, + vnl_O, vnl_P, vnl_Q, vnl_R, vnl_S, vnl_T, vnl_U, + vnl_V, vnl_W, vnl_X, vnl_Y, vnl_Z}; + +VnLexiName AZLexiLower[] = {vnl_a, vnl_b, vnl_c, vnl_d, vnl_e, vnl_f, vnl_g, + vnl_h, vnl_i, vnl_j, vnl_k, vnl_l, vnl_m, vnl_n, + vnl_o, vnl_p, vnl_q, vnl_r, vnl_s, vnl_t, vnl_u, + vnl_v, vnl_w, vnl_x, vnl_y, vnl_z}; + +UkCharType UkcMap[256]; + +struct _ascVnLexi { + int asc; + VnLexiName lexi; +}; + +// List of western characters outside range A-Z that are +// also Vietnamese characters +_ascVnLexi AscVnLexiList[] = { + {0xC0, vnl_A2}, {0xC1, vnl_A1}, {0xC2, vnl_Ar}, {0xC2, vnl_A4}, + {0xC8, vnl_E2}, {0xC9, vnl_E1}, {0xCA, vnl_Er}, {0xCC, vnl_I2}, + {0xCD, vnl_I1}, {0xD2, vnl_O2}, {0xD3, vnl_O1}, {0xD4, vnl_Or}, + {0xD5, vnl_O4}, {0xD9, vnl_U2}, {0xDA, vnl_U1}, {0xDD, vnl_Y1}, + {0xE0, vnl_a2}, {0xE1, vnl_a1}, {0xE2, vnl_ar}, {0xE3, vnl_a4}, + {0xE8, vnl_e2}, {0xE9, vnl_e1}, {0xEA, vnl_er}, {0xEC, vnl_i2}, + {0xED, vnl_i1}, {0xF2, vnl_o2}, {0xF3, vnl_o1}, {0xF4, vnl_or}, + {0xF5, vnl_o4}, {0xF9, vnl_u2}, {0xFA, vnl_u1}, {0xFD, vnl_y1}, + {0x00, vnl_nonVnChar}}; + +VnLexiName IsoVnLexiMap[256]; + +bool ClassifierTableInitialized = false; + +DllExport UkKeyMapping TelexMethodMapping[] = {{'Z', vneTone0}, + {'S', vneTone1}, + {'F', vneTone2}, + {'R', vneTone3}, + {'X', vneTone4}, + {'J', vneTone5}, + {'W', vne_telex_w}, + {'A', vneRoof_a}, + {'E', vneRoof_e}, + {'O', vneRoof_o}, + {'D', vneDd}, + {'[', vneCount + vnl_oh}, + {']', vneCount + vnl_uh}, + {'{', vneCount + vnl_Oh}, + {'}', vneCount + vnl_Uh}, + {0, vneNormal}}; + +DllExport UkKeyMapping SimpleTelexMethodMapping[] = { + {'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, + {'X', vneTone4}, {'J', vneTone5}, {'W', vneHookAll}, {'A', vneRoof_a}, + {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping SimpleTelex2MethodMapping[] = { + {'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, + {'X', vneTone4}, {'J', vneTone5}, {'W', vne_telex_w}, {'A', vneRoof_a}, + {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping VniMethodMapping[] = { + {'0', vneTone0}, {'1', vneTone1}, {'2', vneTone2}, {'3', vneTone3}, + {'4', vneTone4}, {'5', vneTone5}, {'6', vneRoofAll}, {'7', vneHook_uo}, + {'8', vneBowl}, {'9', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping VIQRMethodMapping[] = { + {'0', vneTone0}, {'\'', vneTone1}, {'`', vneTone2}, {'?', vneTone3}, + {'~', vneTone4}, {'.', vneTone5}, {'^', vneRoofAll}, {'+', vneHook_uo}, + {'*', vneHook_uo}, {'(', vneBowl}, {'D', vneDd}, {'\\', vneEscChar}, + {0, vneNormal}}; + +DllExport UkKeyMapping MsViMethodMapping[] = {{'5', vneTone2}, + {'%', vneTone2}, + {'6', vneTone3}, + {'^', vneTone3}, + {'7', vneTone4}, + {'&', vneTone4}, + {'8', vneTone1}, + {'*', vneTone1}, + {'9', vneTone5}, + {'(', vneTone5}, + {'1', vneCount + vnl_ab}, + {'!', vneCount + vnl_Ab}, + {'2', vneCount + vnl_ar}, + {'@', vneCount + vnl_Ar}, + {'3', vneCount + vnl_er}, + {'#', vneCount + vnl_Er}, + {'4', vneCount + vnl_or}, + {'$', vneCount + vnl_Or}, + {'0', vneCount + vnl_dd}, + {')', vneCount + vnl_DD}, + {'[', vneCount + vnl_uh}, + {']', vneCount + vnl_oh}, + {'{', vneCount + vnl_Uh}, + {'}', vneCount + vnl_Oh}, + {0, vneNormal}}; + +//------------------------------------------- +void SetupInputClassifierTable() { + if (!ClassifierTableInitialized) { + ClassifierTableInitialized = true; + } + unsigned int c; + int i; + + for (c = 0; c <= 32; c++) { + UkcMap[c] = ukcReset; + } + + for (c = 33; c < 256; c++) { + UkcMap[c] = ukcNonVn; + } + + /* + for (c = '0'; c <= '9'; c++) + UkcMap[c] = ukcNonVn; + */ + + for (c = 'a'; c <= 'z'; c++) + UkcMap[c] = ukcVn; + for (c = 'A'; c <= 'Z'; c++) + UkcMap[c] = ukcVn; + + for (i = 0; AscVnLexiList[i].asc; i++) { + UkcMap[AscVnLexiList[i].asc] = ukcVn; + } + + UkcMap[(unsigned char)'j'] = ukcNonVn; + UkcMap[(unsigned char)'J'] = ukcNonVn; + UkcMap[(unsigned char)'f'] = ukcNonVn; + UkcMap[(unsigned char)'F'] = ukcNonVn; + UkcMap[(unsigned char)'w'] = ukcNonVn; + UkcMap[(unsigned char)'W'] = ukcNonVn; + + for (auto wordBreakSym : WordBreakSyms) + UkcMap[wordBreakSym] = ukcWordBreak; + + // Calculate IsoVnLexiMap + for (i = 0; i < 256; i++) { + IsoVnLexiMap[i] = vnl_nonVnChar; + } + + for (i = 0; AscVnLexiList[i].asc; i++) { + IsoVnLexiMap[AscVnLexiList[i].asc] = AscVnLexiList[i].lexi; + } + + for (c = 'a'; c <= 'z'; c++) { + IsoVnLexiMap[c] = AZLexiLower[c - 'a']; + } + + for (c = 'A'; c <= 'Z'; c++) { + IsoVnLexiMap[c] = AZLexiUpper[c - 'A']; + } +} + +//------------------------------------------- +void UkInputProcessor::init() { + SetupInputClassifierTable(); + setIM(UkTelex); +} + +//------------------------------------------- +int UkInputProcessor::setIM(UkInputMethod im) { + m_im = im; + switch (im) { + case UkTelex: + useBuiltIn(TelexMethodMapping); + break; + case UkSimpleTelex: + useBuiltIn(SimpleTelexMethodMapping); + break; + case UkSimpleTelex2: + useBuiltIn(SimpleTelex2MethodMapping); + break; + case UkVni: + useBuiltIn(VniMethodMapping); + break; + case UkViqr: + useBuiltIn(VIQRMethodMapping); + break; + case UkMsVi: + useBuiltIn(MsViMethodMapping); + break; + default: + m_im = UkTelex; + useBuiltIn(TelexMethodMapping); + } + return 1; +} + +//------------------------------------------- +int UkInputProcessor::setIM(int map[256]) { + int i; + m_im = UkUsrIM; + for (i = 0; i < 256; i++) + m_keyMap[i] = map[i]; + return 1; +} + +//------------------------------------------- +void UkResetKeyMap(int keyMap[256]) { + unsigned int c; + for (c = 0; c < 256; c++) + keyMap[c] = vneNormal; +} + +//------------------------------------------- +void UkInputProcessor::useBuiltIn(UkKeyMapping *map) { + UkResetKeyMap(m_keyMap); + for (int i = 0; map[i].key; i++) { + m_keyMap[map[i].key] = map[i].action; + if (map[i].action < vneCount) { + if (islower(map[i].key)) { + m_keyMap[toupper(map[i].key)] = map[i].action; + } else if (isupper(map[i].key)) { + m_keyMap[tolower(map[i].key)] = map[i].action; + } + } + } +} + +//------------------------------------------- +void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev) { + ev.keyCode = keyCode; + if (keyCode == 0) { + ev.evType = vneNormal; + ev.vnSym = vnl_nonVnChar; + ev.chType = ukcWordBreak; + } else if (keyCode > 255) { + ev.evType = vneNormal; + ev.vnSym = IsoToVnLexi(keyCode); + ev.chType = (ev.vnSym == vnl_nonVnChar) ? ukcNonVn : ukcVn; + } else { + ev.chType = UkcMap[keyCode]; + ev.evType = m_keyMap[keyCode]; + + if (ev.evType >= vneTone0 && ev.evType <= vneTone5) { + ev.tone = ev.evType - vneTone0; + } + + if (ev.evType >= vneCount) { + ev.chType = ukcVn; + ev.vnSym = (VnLexiName)(ev.evType - vneCount); + ev.evType = vneMapChar; + } else { + ev.vnSym = IsoToVnLexi(keyCode); + } + } +} + +//---------------------------------------------------------------- +// This method translates a key stroke to a symbol. +// Key strokes are simply considered character input, not action keys as in +// keyCodeToEvent method +//---------------------------------------------------------------- +void UkInputProcessor::keyCodeToSymbol(unsigned int keyCode, UkKeyEvent &ev) { + ev.keyCode = keyCode; + ev.evType = vneNormal; + ev.vnSym = IsoToVnLexi(keyCode); + if (keyCode > 255) { + ev.chType = (ev.vnSym == vnl_nonVnChar) ? ukcNonVn : ukcVn; + } else { + ev.chType = UkcMap[keyCode]; + } +} + +//------------------------------------------- +UkCharType UkInputProcessor::getCharType(unsigned int keyCode) const { + if (keyCode > 255) + return (IsoToVnLexi(keyCode) == vnl_nonVnChar) ? ukcNonVn : ukcVn; + return UkcMap[keyCode]; +} + +//------------------------------------------- +void UkInputProcessor::getKeyMap(int map[256]) const { + int i; + for (i = 0; i < 256; i++) + map[i] = m_keyMap[i]; +} diff --git a/unikey/core/inputproc.h b/unikey/core/inputproc.h new file mode 100644 index 00000000..bf1311f5 --- /dev/null +++ b/unikey/core/inputproc.h @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ +#ifndef __UK_INPUT_PROCESSOR_H +#define __UK_INPUT_PROCESSOR_H + +#include "keycons.h" +#include "vnlexi.h" +#include + +#if defined(_WIN32) +#define DllExport __declspec(dllexport) +#define DllImport __declspec(dllimport) +#if defined(UNIKEYHOOK) +#define DllInterface __declspec(dllexport) +#else +#define DllInterface __declspec(dllimport) +#endif +#else +#define DllInterface // not used +#define DllExport +#define DllImport +#endif + +enum UkKeyEvName { + vneRoofAll, + vneRoof_a, + vneRoof_e, + vneRoof_o, + vneHookAll, + vneHook_uo, + vneHook_u, + vneHook_o, + vneBowl, + vneDd, + vneTone0, + vneTone1, + vneTone2, + vneTone3, + vneTone4, + vneTone5, + vne_telex_w, // special for telex + vneMapChar, // e.g. [ -> u+ , ] -> o+ + vneEscChar, + vneNormal, // does not belong to any of the above categories + vneCount // just to count how many event types there are +}; + +enum UkCharType { ukcVn, ukcWordBreak, ukcNonVn, ukcReset }; + +struct UkKeyEvent { + int evType; + UkCharType chType; + VnLexiName vnSym; // meaningful only when chType==ukcVn + unsigned int keyCode; + int tone; // meaningful only when this is a vowel +}; + +struct UkKeyMapping { + unsigned char key; + int action; +}; + +/////////////////////////////////////////// +class UkInputProcessor { + +public: + // don't do anything with constructor, because + // this object can be allocated in shared memory + // Use init method instead + // UkInputProcessor(); + + void init(); + + UkInputMethod getIM() const { return m_im; } + + void keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev); + void keyCodeToSymbol(unsigned int keyCode, UkKeyEvent &ev); + int setIM(UkInputMethod im); + int setIM(int map[256]); + void getKeyMap(int map[256]) const; + + UkCharType getCharType(unsigned int keyCode) const; + +protected: + static bool m_classInit; + + UkInputMethod m_im; + int m_keyMap[256]; + + void useBuiltIn(UkKeyMapping *map); +}; + +void UkResetKeyMap(int keyMap[256]); +void SetupInputClassifierTable(); + +DllInterface extern UkKeyMapping TelexMethodMapping[]; +DllInterface extern UkKeyMapping SimpleTelexMethodMapping[]; +DllInterface extern UkKeyMapping SimpleTelex2MethodMapping[]; +DllInterface extern UkKeyMapping VniMethodMapping[]; +DllInterface extern UkKeyMapping VIQRMethodMapping[]; +DllInterface extern UkKeyMapping MsViMethodMapping[]; + +extern VnLexiName IsoVnLexiMap[]; +inline VnLexiName IsoToVnLexi(unsigned int keyCode) { + return (keyCode >= 256) ? vnl_nonVnChar : IsoVnLexiMap[keyCode]; +} + +extern const std::unordered_set WordBreakSyms; + +#endif diff --git a/unikey/core/keycons.h b/unikey/core/keycons.h new file mode 100644 index 00000000..cdfcb329 --- /dev/null +++ b/unikey/core/keycons.h @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 1998-2004 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef __KEY_CONS_H +#define __KEY_CONS_H + +// macro table constants +#define MAX_MACRO_KEY_LEN 16 +// #define MAX_MACRO_TEXT_LEN 256 +#define MAX_MACRO_TEXT_LEN 1024 +#define MAX_MACRO_ITEMS 1024 +#define MAX_MACRO_LINE (MAX_MACRO_TEXT_LEN + MAX_MACRO_KEY_LEN) + +#define MACRO_MEM_SIZE (1024 * 128) // 128 KB + +#define CP_US_ANSI 1252 + +enum UkInputMethod { + UkTelex, + UkVni, + UkViqr, + UkMsVi, + UkUsrIM, + UkSimpleTelex, + UkSimpleTelex2 +}; + +struct UnikeyOptions { + int freeMarking; + int modernStyle; + int macroEnabled; + int useUnicodeClipboard; + int alwaysMacro; + int strictSpellCheck; + int useIME; // for Win32 only + int spellCheckEnabled; + int autoNonVnRestore; +}; + +#define UKOPT_FLAG_ALL 0xFFFFFFFF +#define UKOPT_FLAG_FREE_STYLE 0x00000001 +// #define UKOPT_FLAG_MANUAL_TONE 0x00000002 +#define UKOPT_FLAG_MODERN 0x00000004 +#define UKOPT_FLAG_MACRO_ENABLED 0x00000008 +#define UKOPT_FLAG_USE_CLIPBOARD 0x00000010 +#define UKOPT_FLAG_ALWAYS_MACRO 0x00000020 +#define UKOPT_FLAG_STRICT_SPELL 0x00000040 +#define UKOPT_FLAG_USE_IME 0x00000080 +#define UKOPT_FLAG_SPELLCHECK_ENABLED 0x00000100 + +#if defined(WIN32) +typedef struct _UnikeySysInfo UnikeySysInfo; +struct _UnikeySysInfo { + int switchKey; + HHOOK keyHook; + HHOOK mouseHook; + HWND hMainDlg; + UINT iconMsgId; + HICON hVietIcon, hEnIcon; + int unicodePlatform; + DWORD winMajorVersion, winMinorVersion; +}; +#endif + +typedef enum { UkCharOutput, UkKeyOutput } UkOutputType; + +#endif diff --git a/unikey/core/mactab.cpp b/unikey/core/mactab.cpp new file mode 100644 index 00000000..990637b2 --- /dev/null +++ b/unikey/core/mactab.cpp @@ -0,0 +1,319 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "mactab.h" +#include "vnconv.h" +#include +#include +#include +#include + +using namespace std; +#define UKMACRO_VERSION_UTF8 1 + +//--------------------------------------------------------------- +void CMacroTable::init() { + m_memSize = MACRO_MEM_SIZE; + m_count = 0; + m_occupied = 0; +} + +//--------------------------------------------------------------- +char *MacCompareStartMem; + +#define STD_TO_LOWER(x) \ + (((x) >= VnStdCharOffset && \ + (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && !((x) & 1)) \ + ? (x + 1) \ + : (x)) + +int macCompare(const void *p1, const void *p2) { + StdVnChar *s1 = + (StdVnChar *)((char *)MacCompareStartMem + ((MacroDef *)p1)->keyOffset); + StdVnChar *s2 = + (StdVnChar *)((char *)MacCompareStartMem + ((MacroDef *)p2)->keyOffset); + + int i; + StdVnChar ls1, ls2; + + for (i = 0; s1[i] != 0 && s2[i] != 0; i++) { + ls1 = STD_TO_LOWER(s1[i]); + ls2 = STD_TO_LOWER(s2[i]); + if (ls1 > ls2) + return 1; + if (ls1 < ls2) + return -1; + /* + if (s1[i] > s2[i]) + return 1; + if (s1[i] < s2[i]) + return -1; + */ + } + if (s1[i] == 0) + return (s2[i] == 0) ? 0 : -1; + return 1; +} + +//--------------------------------------------------------------- +int macKeyCompare(const void *key, const void *ele) { + StdVnChar *s1 = (StdVnChar *)key; + StdVnChar *s2 = (StdVnChar *)((char *)MacCompareStartMem + + ((MacroDef *)ele)->keyOffset); + + StdVnChar ls1, ls2; + int i; + for (i = 0; s1[i] != 0 && s2[i] != 0; i++) { + ls1 = STD_TO_LOWER(s1[i]); + ls2 = STD_TO_LOWER(s2[i]); + if (ls1 > ls2) + return 1; + if (ls1 < ls2) + return -1; + /* + if (s1[i] > s2[i]) + return 1; + if (s1[i] < s2[i]) + return -1; + */ + } + if (s1[i] == 0) + return (s2[i] == 0) ? 0 : -1; + return 1; +} + +//--------------------------------------------------------------- +const StdVnChar *CMacroTable::lookup(StdVnChar *key) { + MacCompareStartMem = m_macroMem; + MacroDef *p = (MacroDef *)bsearch(key, m_table, m_count, sizeof(MacroDef), + macKeyCompare); + if (p) + return (StdVnChar *)(m_macroMem + p->textOffset); + return 0; +} + +//---------------------------------------------------------------------------- +// Read header, if it's present in the file. Get the version of the file +// If header is absent, go back to the beginning of file and set version to 0 +// Return false if reading failed. +// +// Header format: ;[DO NOT DELETE THIS LINE]***version=n +//---------------------------------------------------------------------------- +bool CMacroTable::readHeader(FILE *f, int &version) { + char line[MAX_MACRO_LINE]; + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) { + fseek(f, 0, SEEK_SET); + version = 0; + return true; + } + return false; + } + + // if BOM is available, skip it + char *p = line; + size_t len = strlen(line); + if (len >= 3 && (unsigned char)line[0] == 0xEF && + (unsigned char)line[1] == 0xBB && (unsigned char)line[2] == 0xBF) { + p += 3; + } + + // read version number + p = strstr(p, "***"); + if (p) { + p += 3; + // skip possible spaces + while (*p == ' ') + p++; + if (sscanf(p, "version=%d", &version) == 1) + return true; + } + + fseek(f, 0, SEEK_SET); + version = 0; + return true; +} + +//---------------------------------------------------------------- +void CMacroTable::writeHeader(FILE *f) { +#if defined(WIN32) + fprintf(f, "\xEF\xBB\xBF;DO NOT DELETE THIS LINE*** version=%d ***\n", + UKMACRO_VERSION_UTF8); +#else + fprintf(f, "DO NOT DELETE THIS LINE*** version=%d ***\n", + UKMACRO_VERSION_UTF8); +#endif +} +//--------------------------------------------------------------- +int CMacroTable::loadFromFile(const char *fname) { + FILE *f; +#if defined(WIN32) + f = _tfopen(fname, _TEXT("rt")); +#else + f = fopen(fname, "r"); +#endif + + if (f == NULL) + return 0; + char line[MAX_MACRO_LINE]; + size_t len; + + resetContent(); + + // read possible header + int version; + if (!readHeader(f, version)) { + version = 0; + } + + while (fgets(line, sizeof(line), f)) { + len = strlen(line); + if (len > 0 && line[len - 1] == '\n') + line[len - 1] = 0; + if (len > 1 && line[len - 2] == '\r') + line[len - 2] = 0; + if (version == UKMACRO_VERSION_UTF8) + addItem(line, CONV_CHARSET_UNIUTF8); + else + addItem(line, CONV_CHARSET_VIQR); + } + fclose(f); + MacCompareStartMem = m_macroMem; + qsort(m_table, m_count, sizeof(MacroDef), macCompare); + // Convert old version + if (version != UKMACRO_VERSION_UTF8) { + writeToFile(fname); + } + return 1; +} + +//--------------------------------------------------------------- +int CMacroTable::writeToFile(const char *fname) { + FILE *f; + f = fopen(fname, "w"); + return writeToFp(f); +} + +int CMacroTable::writeToFp(FILE *f) { + int ret; + int inLen, maxOutLen; + + if (f == NULL) + return 0; + + char line[MAX_MACRO_LINE * 3 + 1]; // 1 VnChar may need 3 chars in UTF8 + char key[MAX_MACRO_KEY_LEN * 3]; + char text[MAX_MACRO_TEXT_LEN * 3]; + + writeHeader(f); + + UKBYTE *p; + for (int i = 0; i < m_count; i++) { + p = (UKBYTE *)m_macroMem + m_table[i].keyOffset; + inLen = -1; + maxOutLen = sizeof(key); + ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, + (UKBYTE *)p, (UKBYTE *)key, &inLen, &maxOutLen); + if (ret != 0) + continue; + + p = (UKBYTE *)m_macroMem + m_table[i].textOffset; + inLen = -1; + maxOutLen = sizeof(text); + ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, p, + (UKBYTE *)text, &inLen, &maxOutLen); + if (ret != 0) + continue; + if (i < m_count - 1) + sprintf(line, "%s:%s\n", key, text); + else + sprintf(line, "%s:%s", key, text); + fputs(line, f); + } + + fclose(f); + return 1; +} + +//--------------------------------------------------------------- +int CMacroTable::addItem(const void *key, const void *text, int charset) { + int ret; + int inLen, maxOutLen; + int offset = m_occupied; + char *p = m_macroMem + offset; + + if (m_count >= MAX_MACRO_ITEMS) + return -1; + + m_table[m_count].keyOffset = offset; + + // Convert macro key to VN standard + inLen = -1; // input is null-terminated + maxOutLen = MAX_MACRO_KEY_LEN * sizeof(StdVnChar); + if (maxOutLen + offset > m_memSize) + maxOutLen = m_memSize - offset; + ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE *)key, + (UKBYTE *)p, &inLen, &maxOutLen); + if (ret != 0) + return -1; + + offset += maxOutLen; + p += maxOutLen; + + // convert macro text to VN standard + m_table[m_count].textOffset = offset; + inLen = -1; // input is null-terminated + maxOutLen = MAX_MACRO_TEXT_LEN * sizeof(StdVnChar); + if (maxOutLen + offset > m_memSize) + maxOutLen = m_memSize - offset; + ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE *)text, + (UKBYTE *)p, &inLen, &maxOutLen); + if (ret != 0) + return -1; + + m_occupied = offset + maxOutLen; + m_count++; + return (m_count - 1); +} + +//--------------------------------------------------------------- +// add a new macro into the sorted macro table +// item format: key:text (key and text are separated by a colon) +//--------------------------------------------------------------- +int CMacroTable::addItem(const char *item, int charset) { + char key[MAX_MACRO_KEY_LEN]; + + // Parse the input item + char *pos = (char *)strchr(item, ':'); + if (pos == NULL) + return -1; + int keyLen = (int)(pos - item); + if (keyLen > MAX_MACRO_KEY_LEN - 1) + keyLen = MAX_MACRO_KEY_LEN - 1; + strncpy(key, item, keyLen); + key[keyLen] = '\0'; + return addItem(key, ++pos, charset); +} + +//--------------------------------------------------------------- +void CMacroTable::resetContent() { + m_occupied = 0; + m_count = 0; +} + +//--------------------------------------------------------------- +const StdVnChar *CMacroTable::getKey(int idx) const { + if (idx < 0 || idx >= m_count) + return 0; + return (StdVnChar *)(m_macroMem + m_table[idx].keyOffset); +} + +//--------------------------------------------------------------- +const StdVnChar *CMacroTable::getText(int idx) const { + if (idx < 0 || idx >= m_count) + return 0; + return (StdVnChar *)(m_macroMem + m_table[idx].textOffset); +} diff --git a/unikey/core/mactab.h b/unikey/core/mactab.h new file mode 100644 index 00000000..3fc53e1e --- /dev/null +++ b/unikey/core/mactab.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef __MACRO_TABLE_H +#define __MACRO_TABLE_H + +#include "charset.h" +#include "keycons.h" + +#if defined(_WIN32) +#if defined(UNIKEYHOOK) +#define DllInterface __declspec(dllexport) +#else +#define DllInterface __declspec(dllimport) +#endif +#else +#define DllInterface // not used +#define DllExport +#define DllImport +#endif + +struct MacroDef { + int keyOffset; + int textOffset; +}; + +#if !defined(WIN32) +typedef char TCHAR; +#endif + +class DllInterface CMacroTable { +public: + void init(); + int loadFromFile(const char *fname); + int writeToFile(const char *fname); + int writeToFp(FILE *f); + + const StdVnChar *lookup(StdVnChar *key); + const StdVnChar *getKey(int idx) const; + const StdVnChar *getText(int idx) const; + int getCount() const { return m_count; } + void resetContent(); + int addItem(const char *item, int charset); + int addItem(const void *key, const void *text, int charset); + +protected: + bool readHeader(FILE *f, int &version); + void writeHeader(FILE *f); + + MacroDef m_table[MAX_MACRO_ITEMS]; + char m_macroMem[MACRO_MEM_SIZE]; + + int m_count; + int m_memSize, m_occupied; +}; + +#endif diff --git a/unikey/core/pattern.cpp b/unikey/core/pattern.cpp new file mode 100644 index 00000000..30adc091 --- /dev/null +++ b/unikey/core/pattern.cpp @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "pattern.h" + +////////////////////////////////////////////////// +// Pattern matching (based on KPM algorithm) +////////////////////////////////////////////////// + +//---------------------------- +void PatternState::reset() { + m_pos = 0; + m_found = 0; +} + +//---------------------------- +void PatternState::init(char *pattern) { + m_pos = 0; + m_found = 0; + m_pattern = pattern; + + int i = 0, j = -1; + m_border[i] = j; + while (m_pattern[i]) { + while (j >= 0 && m_pattern[i] != m_pattern[j]) + j = m_border[j]; + i++; + j++; + m_border[i] = j; + } +} + +//----------------------------------------------------- +// get next input char, returns 1 if pattern is found. +//----------------------------------------------------- +int PatternState::foundAtNextChar(char ch) { + int ret = 0; + // int j = m_pos; + while (m_pos >= 0 && ch != m_pattern[m_pos]) + m_pos = m_border[m_pos]; + m_pos++; + if (m_pattern[m_pos] == 0) { + m_found++; + m_pos = m_border[m_pos]; + ret = 1; + } + return ret; +} + +//----------------------------------------------------- +void PatternList::init(char **patterns, int count) { + m_count = count; + delete[] m_patterns; + m_patterns = new PatternState[count]; + for (int i = 0; i < count; i++) + m_patterns[i].init(patterns[i]); +} + +//----------------------------------------------------- +// return the order number of the pattern that is found. +// If more than 1 pattern is found, returns any pattern +// Returns -1 if no pattern is found +//----------------------------------------------------- +int PatternList::foundAtNextChar(char ch) { + int patternFound = -1; + for (int i = 0; i < m_count; i++) { + if (m_patterns[i].foundAtNextChar(ch)) + patternFound = i; + } + return patternFound; +} + +//----------------------------------------------------- +void PatternList::reset() { + for (int i = 0; i < m_count; i++) + m_patterns[i].reset(); +} diff --git a/unikey/core/pattern.h b/unikey/core/pattern.h new file mode 100644 index 00000000..611e43c9 --- /dev/null +++ b/unikey/core/pattern.h @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef __PATTERN_H +#define __PATTERN_H + +#if defined(_WIN32) +#if defined(UNIKEYHOOK) +#define DllInterface __declspec(dllexport) +#else +#define DllInterface __declspec(dllimport) +#endif +#else +#define DllInterface // not used +#endif + +#define MAX_PATTERN_LEN 40 + +class DllInterface PatternState { +public: + char *m_pattern; + int m_border[MAX_PATTERN_LEN + 1]; + int m_pos; + int m_found; + void init(char *pattern); + void reset(); + int foundAtNextChar( + char ch); // get next input char, returns 1 if pattern is found. +}; + +class DllInterface PatternList { +public: + PatternState *m_patterns; + int m_count; + void init(char **patterns, int count); + int foundAtNextChar(char ch); + void reset(); + + PatternList() { + m_count = 0; + m_patterns = 0; + } + + ~PatternList() { + if (m_patterns) + delete[] m_patterns; + } +}; + +#endif diff --git a/unikey/core/ukengine.cpp b/unikey/core/ukengine.cpp new file mode 100644 index 00000000..0e619be7 --- /dev/null +++ b/unikey/core/ukengine.cpp @@ -0,0 +1,3002 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "keycons.h" +#include +#include +#include +#include +#include + +/* +#if defined(_WIN32) +#include "keyhook.h" +#endif +*/ + +#include "ukengine.h" +#include "vnlexi.h" + +#include "charset.h" + +using namespace std; + +#define ENTER_CHAR 13 +#define IS_ODD(x) (x & 1) +#define IS_EVEN(x) (!(x & 1)) + +#define IS_STD_VN_LOWER(x) \ + ((x) >= VnStdCharOffset && \ + (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(x)) +#define IS_STD_VN_UPPER(x) \ + ((x) >= VnStdCharOffset && \ + (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(x)) + +bool IsVnVowel[vnl_lastChar]; + +extern VnLexiName AZLexiUpper[]; // defined in inputproc.cpp +extern VnLexiName AZLexiLower[]; + +// see vnconv/data.cpp for explanation of these characters +unsigned char SpecialWesternChars[] = { + 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, + 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F, 0x00}; + +StdVnChar IsoStdVnCharMap[256]; + +inline StdVnChar IsoToStdVnChar(int keyCode) { + return (keyCode < 256) ? IsoStdVnCharMap[keyCode] : keyCode; +} + +struct VowelSeqInfo { + int len; + int complete; + int conSuffix; // allow consonnant suffix + VnLexiName v[3]; + VowelSeq sub[3]; + + int roofPos; + VowelSeq withRoof; + + int hookPos; + VowelSeq withHook; // hook & bowl +}; + +VowelSeqInfo VSeqList[] = {{1, + 1, + 1, + {vnl_a, vnl_nonVnChar, vnl_nonVnChar}, + {vs_a, vs_nil, vs_nil}, + -1, + vs_ar, + -1, + vs_ab}, + {1, + 1, + 1, + {vnl_ar, vnl_nonVnChar, vnl_nonVnChar}, + {vs_ar, vs_nil, vs_nil}, + 0, + vs_nil, + -1, + vs_ab}, + {1, + 1, + 1, + {vnl_ab, vnl_nonVnChar, vnl_nonVnChar}, + {vs_ab, vs_nil, vs_nil}, + -1, + vs_ar, + 0, + vs_nil}, + {1, + 1, + 1, + {vnl_e, vnl_nonVnChar, vnl_nonVnChar}, + {vs_e, vs_nil, vs_nil}, + -1, + vs_er, + -1, + vs_nil}, + {1, + 1, + 1, + {vnl_er, vnl_nonVnChar, vnl_nonVnChar}, + {vs_er, vs_nil, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {1, + 1, + 1, + {vnl_i, vnl_nonVnChar, vnl_nonVnChar}, + {vs_i, vs_nil, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {1, + 1, + 1, + {vnl_o, vnl_nonVnChar, vnl_nonVnChar}, + {vs_o, vs_nil, vs_nil}, + -1, + vs_or, + -1, + vs_oh}, + {1, + 1, + 1, + {vnl_or, vnl_nonVnChar, vnl_nonVnChar}, + {vs_or, vs_nil, vs_nil}, + 0, + vs_nil, + -1, + vs_oh}, + {1, + 1, + 1, + {vnl_oh, vnl_nonVnChar, vnl_nonVnChar}, + {vs_oh, vs_nil, vs_nil}, + -1, + vs_or, + 0, + vs_nil}, + {1, + 1, + 1, + {vnl_u, vnl_nonVnChar, vnl_nonVnChar}, + {vs_u, vs_nil, vs_nil}, + -1, + vs_nil, + -1, + vs_uh}, + {1, + 1, + 1, + {vnl_uh, vnl_nonVnChar, vnl_nonVnChar}, + {vs_uh, vs_nil, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {1, + 1, + 1, + {vnl_y, vnl_nonVnChar, vnl_nonVnChar}, + {vs_y, vs_nil, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_i, vnl_nonVnChar}, + {vs_a, vs_ai, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_o, vnl_nonVnChar}, + {vs_a, vs_ao, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_u, vnl_nonVnChar}, + {vs_a, vs_au, vs_nil}, + -1, + vs_aru, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_y, vnl_nonVnChar}, + {vs_a, vs_ay, vs_nil}, + -1, + vs_ary, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_ar, vnl_u, vnl_nonVnChar}, + {vs_ar, vs_aru, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_ar, vnl_y, vnl_nonVnChar}, + {vs_ar, vs_ary, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_e, vnl_o, vnl_nonVnChar}, + {vs_e, vs_eo, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 0, + 0, + {vnl_e, vnl_u, vnl_nonVnChar}, + {vs_e, vs_eu, vs_nil}, + -1, + vs_eru, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_er, vnl_u, vnl_nonVnChar}, + {vs_er, vs_eru, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_i, vnl_a, vnl_nonVnChar}, + {vs_i, vs_ia, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 0, + 1, + {vnl_i, vnl_e, vnl_nonVnChar}, + {vs_i, vs_ie, vs_nil}, + -1, + vs_ier, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_i, vnl_er, vnl_nonVnChar}, + {vs_i, vs_ier, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_i, vnl_u, vnl_nonVnChar}, + {vs_i, vs_iu, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_o, vnl_a, vnl_nonVnChar}, + {vs_o, vs_oa, vs_nil}, + -1, + vs_nil, + -1, + vs_oab}, + {2, + 1, + 1, + {vnl_o, vnl_ab, vnl_nonVnChar}, + {vs_o, vs_oab, vs_nil}, + -1, + vs_nil, + 1, + vs_nil}, + {2, + 1, + 1, + {vnl_o, vnl_e, vnl_nonVnChar}, + {vs_o, vs_oe, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_o, vnl_i, vnl_nonVnChar}, + {vs_o, vs_oi, vs_nil}, + -1, + vs_ori, + -1, + vs_ohi}, + {2, + 1, + 0, + {vnl_or, vnl_i, vnl_nonVnChar}, + {vs_or, vs_ori, vs_nil}, + 0, + vs_nil, + -1, + vs_ohi}, + {2, + 1, + 0, + {vnl_oh, vnl_i, vnl_nonVnChar}, + {vs_oh, vs_ohi, vs_nil}, + -1, + vs_ori, + 0, + vs_nil}, + {2, + 1, + 1, + {vnl_u, vnl_a, vnl_nonVnChar}, + {vs_u, vs_ua, vs_nil}, + -1, + vs_uar, + -1, + vs_uha}, + {2, + 1, + 1, + {vnl_u, vnl_ar, vnl_nonVnChar}, + {vs_u, vs_uar, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {2, + 0, + 1, + {vnl_u, vnl_e, vnl_nonVnChar}, + {vs_u, vs_ue, vs_nil}, + -1, + vs_uer, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_u, vnl_er, vnl_nonVnChar}, + {vs_u, vs_uer, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_u, vnl_i, vnl_nonVnChar}, + {vs_u, vs_ui, vs_nil}, + -1, + vs_nil, + -1, + vs_uhi}, + {2, + 0, + 1, + {vnl_u, vnl_o, vnl_nonVnChar}, + {vs_u, vs_uo, vs_nil}, + -1, + vs_uor, + -1, + vs_uho}, + {2, + 1, + 1, + {vnl_u, vnl_or, vnl_nonVnChar}, + {vs_u, vs_uor, vs_nil}, + 1, + vs_nil, + -1, + vs_uoh}, + {2, + 1, + 1, + {vnl_u, vnl_oh, vnl_nonVnChar}, + {vs_u, vs_uoh, vs_nil}, + -1, + vs_uor, + 1, + vs_uhoh}, + {2, + 0, + 0, + {vnl_u, vnl_u, vnl_nonVnChar}, + {vs_u, vs_uu, vs_nil}, + -1, + vs_nil, + -1, + vs_uhu}, + {2, + 1, + 1, + {vnl_u, vnl_y, vnl_nonVnChar}, + {vs_u, vs_uy, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_uh, vnl_a, vnl_nonVnChar}, + {vs_uh, vs_uha, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 1, + 0, + {vnl_uh, vnl_i, vnl_nonVnChar}, + {vs_uh, vs_uhi, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 0, + 1, + {vnl_uh, vnl_o, vnl_nonVnChar}, + {vs_uh, vs_uho, vs_nil}, + -1, + vs_nil, + 0, + vs_uhoh}, + {2, + 1, + 1, + {vnl_uh, vnl_oh, vnl_nonVnChar}, + {vs_uh, vs_uhoh, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 1, + 0, + {vnl_uh, vnl_u, vnl_nonVnChar}, + {vs_uh, vs_uhu, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 0, + 1, + {vnl_y, vnl_e, vnl_nonVnChar}, + {vs_y, vs_ye, vs_nil}, + -1, + vs_yer, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_y, vnl_er, vnl_nonVnChar}, + {vs_y, vs_yer, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_i, vnl_e, vnl_u}, + {vs_i, vs_ie, vs_ieu}, + -1, + vs_ieru, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_i, vnl_er, vnl_u}, + {vs_i, vs_ier, vs_ieru}, + 1, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_o, vnl_a, vnl_i}, + {vs_o, vs_oa, vs_oai}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_o, vnl_a, vnl_y}, + {vs_o, vs_oa, vs_oay}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_o, vnl_e, vnl_o}, + {vs_o, vs_oe, vs_oeo}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_u, vnl_a, vnl_y}, + {vs_u, vs_ua, vs_uay}, + -1, + vs_uary, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_u, vnl_ar, vnl_y}, + {vs_u, vs_uar, vs_uary}, + 1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_u, vnl_o, vnl_i}, + {vs_u, vs_uo, vs_uoi}, + -1, + vs_uori, + -1, + vs_uhoi}, + {3, + 0, + 0, + {vnl_u, vnl_o, vnl_u}, + {vs_u, vs_uo, vs_uou}, + -1, + vs_nil, + -1, + vs_uhou}, + {3, + 1, + 0, + {vnl_u, vnl_or, vnl_i}, + {vs_u, vs_uor, vs_uori}, + 1, + vs_nil, + -1, + vs_uohi}, + {3, + 0, + 0, + {vnl_u, vnl_oh, vnl_i}, + {vs_u, vs_uoh, vs_uohi}, + -1, + vs_uori, + 1, + vs_uhohi}, + {3, + 0, + 0, + {vnl_u, vnl_oh, vnl_u}, + {vs_u, vs_uoh, vs_uohu}, + -1, + vs_nil, + 1, + vs_uhohu}, + {3, + 1, + 0, + {vnl_u, vnl_y, vnl_a}, + {vs_u, vs_uy, vs_uya}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 1, + {vnl_u, vnl_y, vnl_e}, + {vs_u, vs_uy, vs_uye}, + -1, + vs_uyer, + -1, + vs_nil}, + {3, + 1, + 1, + {vnl_u, vnl_y, vnl_er}, + {vs_u, vs_uy, vs_uyer}, + 2, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_u, vnl_y, vnl_u}, + {vs_u, vs_uy, vs_uyu}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_uh, vnl_o, vnl_i}, + {vs_uh, vs_uho, vs_uhoi}, + -1, + vs_nil, + 0, + vs_uhohi}, + {3, + 0, + 0, + {vnl_uh, vnl_o, vnl_u}, + {vs_uh, vs_uho, vs_uhou}, + -1, + vs_nil, + 0, + vs_uhohu}, + {3, + 1, + 0, + {vnl_uh, vnl_oh, vnl_i}, + {vs_uh, vs_uhoh, vs_uhohi}, + -1, + vs_nil, + 0, + vs_nil}, + {3, + 1, + 0, + {vnl_uh, vnl_oh, vnl_u}, + {vs_uh, vs_uhoh, vs_uhohu}, + -1, + vs_nil, + 0, + vs_nil}, + {3, + 0, + 0, + {vnl_y, vnl_e, vnl_u}, + {vs_y, vs_ye, vs_yeu}, + -1, + vs_yeru, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_y, vnl_er, vnl_u}, + {vs_y, vs_yer, vs_yeru}, + 1, + vs_nil, + -1, + vs_nil}}; + +struct ConSeqInfo { + int len; + VnLexiName c[3]; + bool suffix; +}; + +ConSeqInfo CSeqList[] = {{1, {vnl_b, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_c, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_c, vnl_h, vnl_nonVnChar}, true}, + {1, {vnl_d, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_dd, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_d, vnl_z, vnl_nonVnChar}, false}, + {1, {vnl_g, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_g, vnl_h, vnl_nonVnChar}, false}, + {2, {vnl_g, vnl_i, vnl_nonVnChar}, false}, + {3, {vnl_g, vnl_i, vnl_n}, false}, + {1, {vnl_h, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_k, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_k, vnl_h, vnl_nonVnChar}, false}, + {1, {vnl_l, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_m, vnl_nonVnChar, vnl_nonVnChar}, true}, + {1, {vnl_n, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_n, vnl_g, vnl_nonVnChar}, true}, + {3, {vnl_n, vnl_g, vnl_h}, false}, + {2, {vnl_n, vnl_h, vnl_nonVnChar}, true}, + {1, {vnl_p, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_p, vnl_h, vnl_nonVnChar}, false}, + {1, {vnl_q, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_q, vnl_u, vnl_nonVnChar}, false}, + {1, {vnl_r, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_s, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_t, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_t, vnl_h, vnl_nonVnChar}, false}, + {2, {vnl_t, vnl_r, vnl_nonVnChar}, false}, + {1, {vnl_v, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_x, vnl_nonVnChar, vnl_nonVnChar}, false}}; + +const int VSeqCount = sizeof(VSeqList) / sizeof(VowelSeqInfo); +struct VSeqPair { + VnLexiName v[3]; + VowelSeq vs; +}; +VSeqPair SortedVSeqList[VSeqCount]; + +const int CSeqCount = sizeof(CSeqList) / sizeof(ConSeqInfo); +struct CSeqPair { + VnLexiName c[3]; + ConSeq cs; +}; +CSeqPair SortedCSeqList[CSeqCount]; + +struct VCPair { + VowelSeq v; + ConSeq c; +}; + +VCPair VCPairList[] = {{vs_a, cs_c}, {vs_a, cs_ch}, {vs_a, cs_m}, + {vs_a, cs_n}, {vs_a, cs_ng}, {vs_a, cs_nh}, + {vs_a, cs_p}, {vs_a, cs_t}, {vs_ar, cs_c}, + {vs_ar, cs_m}, {vs_ar, cs_n}, {vs_ar, cs_ng}, + {vs_ar, cs_p}, {vs_ar, cs_t}, {vs_ab, cs_c}, + {vs_ab, cs_m}, {vs_ab, cs_n}, {vs_ab, cs_ng}, + {vs_ab, cs_p}, {vs_ab, cs_t}, + + {vs_e, cs_c}, {vs_e, cs_ch}, {vs_e, cs_m}, + {vs_e, cs_n}, {vs_e, cs_ng}, {vs_e, cs_nh}, + {vs_e, cs_p}, {vs_e, cs_t}, {vs_er, cs_c}, + {vs_er, cs_ch}, {vs_er, cs_m}, {vs_er, cs_n}, + {vs_er, cs_nh}, {vs_er, cs_p}, {vs_er, cs_t}, + + {vs_i, cs_c}, {vs_i, cs_ch}, {vs_i, cs_m}, + {vs_i, cs_n}, {vs_i, cs_nh}, {vs_i, cs_p}, + {vs_i, cs_t}, + + {vs_o, cs_c}, {vs_o, cs_m}, {vs_o, cs_n}, + {vs_o, cs_ng}, {vs_o, cs_p}, {vs_o, cs_t}, + {vs_or, cs_c}, {vs_or, cs_m}, {vs_or, cs_n}, + {vs_or, cs_ng}, {vs_or, cs_p}, {vs_or, cs_t}, + {vs_oh, cs_m}, {vs_oh, cs_n}, {vs_oh, cs_p}, + {vs_oh, cs_t}, + + {vs_u, cs_c}, {vs_u, cs_m}, {vs_u, cs_n}, + {vs_u, cs_ng}, {vs_u, cs_p}, {vs_u, cs_t}, + {vs_uh, cs_c}, {vs_uh, cs_m}, {vs_uh, cs_n}, + {vs_uh, cs_ng}, {vs_uh, cs_t}, + + {vs_y, cs_t}, {vs_ie, cs_c}, {vs_ie, cs_m}, + {vs_ie, cs_n}, {vs_ie, cs_ng}, {vs_ie, cs_p}, + {vs_ie, cs_t}, {vs_ier, cs_c}, {vs_ier, cs_m}, + {vs_ier, cs_n}, {vs_ier, cs_ng}, {vs_ier, cs_p}, + {vs_ier, cs_t}, + + {vs_oa, cs_c}, {vs_oa, cs_ch}, {vs_oa, cs_m}, + {vs_oa, cs_n}, {vs_oa, cs_ng}, {vs_oa, cs_nh}, + {vs_oa, cs_p}, {vs_oa, cs_t}, {vs_oab, cs_c}, + {vs_oab, cs_m}, {vs_oab, cs_n}, {vs_oab, cs_ng}, + {vs_oab, cs_t}, + + {vs_oe, cs_n}, {vs_oe, cs_t}, + + {vs_ua, cs_n}, {vs_ua, cs_ng}, {vs_ua, cs_t}, + {vs_uar, cs_n}, {vs_uar, cs_ng}, {vs_uar, cs_t}, + + {vs_ue, cs_c}, {vs_ue, cs_ch}, {vs_ue, cs_n}, + {vs_ue, cs_nh}, {vs_uer, cs_c}, {vs_uer, cs_ch}, + {vs_uer, cs_n}, {vs_uer, cs_nh}, + + {vs_uo, cs_c}, {vs_uo, cs_m}, {vs_uo, cs_n}, + {vs_uo, cs_ng}, {vs_uo, cs_p}, {vs_uo, cs_t}, + {vs_uor, cs_c}, {vs_uor, cs_m}, {vs_uor, cs_n}, + {vs_uor, cs_ng}, {vs_uor, cs_t}, {vs_uho, cs_c}, + {vs_uho, cs_m}, {vs_uho, cs_n}, {vs_uho, cs_ng}, + {vs_uho, cs_p}, {vs_uho, cs_t}, {vs_uhoh, cs_c}, + {vs_uhoh, cs_m}, {vs_uhoh, cs_n}, {vs_uhoh, cs_ng}, + {vs_uhoh, cs_p}, {vs_uhoh, cs_t}, + + {vs_uy, cs_c}, {vs_uy, cs_ch}, {vs_uy, cs_n}, + {vs_uy, cs_nh}, {vs_uy, cs_p}, {vs_uy, cs_t}, + + {vs_ye, cs_m}, {vs_ye, cs_n}, {vs_ye, cs_ng}, + {vs_ye, cs_p}, {vs_ye, cs_t}, {vs_yer, cs_m}, + {vs_yer, cs_n}, {vs_yer, cs_ng}, {vs_yer, cs_t}, + + {vs_uye, cs_n}, {vs_uye, cs_t}, {vs_uyer, cs_n}, + {vs_uyer, cs_t} + +}; + +const int VCPairCount = sizeof(VCPairList) / sizeof(VCPair); + +// TODO: auto-complete: e.g. luan -> lua^n + +typedef int (UkEngine::*UkKeyProc)(UkKeyEvent &ev); + +UkKeyProc UkKeyProcList[vneCount] = { + &UkEngine::processRoof, // vneRoofAll + &UkEngine::processRoof, // vneRoof_a + &UkEngine::processRoof, // vneRoof_e + &UkEngine::processRoof, // vneRoof_o + &UkEngine::processHook, // vneHookAll + &UkEngine::processHook, // vneHook_uo + &UkEngine::processHook, // vneHook_u + &UkEngine::processHook, // vneHook_o + &UkEngine::processHook, // vneBowl + &UkEngine::processDd, // vneDd + &UkEngine::processTone, // vneTone0 + &UkEngine::processTone, // vneTone1 + &UkEngine::processTone, // vneTone2 + &UkEngine::processTone, // vneTone3 + &UkEngine::processTone, // vneTone4 + &UkEngine::processTone, // vneTone5 + &UkEngine::processTelexW, // vne_telex_w + &UkEngine::processMapChar, // vneMapChar + &UkEngine::processEscChar, // vneEscChar + &UkEngine::processAppend // vneNormal +}; + +VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2 = vnl_nonVnChar, + VnLexiName v3 = vnl_nonVnChar); +ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2 = vnl_nonVnChar, + VnLexiName c3 = vnl_nonVnChar); + +bool UkEngine::m_classInit = false; + +//------------------------------------------------ +int tripleVowelCompare(const void *p1, const void *p2) { + VSeqPair *t1 = (VSeqPair *)p1; + VSeqPair *t2 = (VSeqPair *)p2; + + for (int i = 0; i < 3; i++) { + if (t1->v[i] < t2->v[i]) + return -1; + if (t1->v[i] > t2->v[i]) + return 1; + } + return 0; +} + +//------------------------------------------------ +int tripleConCompare(const void *p1, const void *p2) { + CSeqPair *t1 = (CSeqPair *)p1; + CSeqPair *t2 = (CSeqPair *)p2; + + for (int i = 0; i < 3; i++) { + if (t1->c[i] < t2->c[i]) + return -1; + if (t1->c[i] > t2->c[i]) + return 1; + } + return 0; +} + +//------------------------------------------------ +int VCPairCompare(const void *p1, const void *p2) { + VCPair *t1 = (VCPair *)p1; + VCPair *t2 = (VCPair *)p2; + + if (t1->v < t2->v) + return -1; + if (t1->v > t2->v) + return 1; + + if (t1->c < t2->c) + return -1; + if (t1->c > t2->c) + return 1; + return 0; +} + +//---------------------------------------------------------- +bool isValidCV(ConSeq c, VowelSeq v) { + if (c == cs_nil || v == vs_nil) + return true; + + VowelSeqInfo &vInfo = VSeqList[v]; + + // gi doesn't go with i + // qu doesn't go with u, uh + // q doesn't go with any vowel + if ((c == cs_gi && vInfo.v[0] == vnl_i) || + (c == cs_qu && (vInfo.v[0] == vnl_u || vInfo.v[0] == vnl_uh)) || + (c == cs_q)) + return false; + + // k can only go with the following vowel sequences + if (c == cs_k) { + static VowelSeq kVseq[] = {vs_e, vs_i, vs_y, vs_er, vs_eo, + vs_eu, vs_eru, vs_ia, vs_ie, vs_ier, + vs_ieu, vs_ieru, vs_nil}; + int i; + for (i = 0; kVseq[i] != vs_nil && kVseq[i] != v; i++) + ; + return (kVseq[i] != vs_nil); + } + + // More checks + return true; +} + +//---------------------------------------------------------- +bool isValidVC(VowelSeq v, ConSeq c) { + if (v == vs_nil || c == cs_nil) + return true; + + VowelSeqInfo &vInfo = VSeqList[v]; + if (!vInfo.conSuffix) + return false; + + ConSeqInfo &cInfo = CSeqList[c]; + if (!cInfo.suffix) + return false; + + VCPair p; + p.v = v; + p.c = c; + if (bsearch(&p, VCPairList, VCPairCount, sizeof(VCPair), VCPairCompare)) + return true; + + return false; +} + +//---------------------------------------------------------- +bool isValidCVC(ConSeq c1, VowelSeq v, ConSeq c2) { + if (v == vs_nil) + return (c1 == cs_nil || c2 != cs_nil); + + if (c1 == cs_nil) + return isValidVC(v, c2); + + if (c2 == cs_nil) + return isValidCV(c1, v); + + bool okCV = isValidCV(c1, v); + bool okVC = isValidVC(v, c2); + + if (okCV && okVC) + return true; + + if (!okVC) { + // check some exceptions: vc fails but cvc passes + + // quyn, quynh + if (c1 == cs_qu && v == vs_y && (c2 == cs_n || c2 == cs_nh)) + return true; + + // gieng, gie^ng + if (c1 == cs_gi && (v == vs_e || v == vs_er) && + (c2 == cs_n || c2 == cs_ng)) + return true; + } + return false; +} + +//------------------------------------------------ +void engineClassInit() { + int i, j; + + for (i = 0; i < VSeqCount; i++) { + for (j = 0; j < 3; j++) + SortedVSeqList[i].v[j] = VSeqList[i].v[j]; + SortedVSeqList[i].vs = (VowelSeq)i; + } + + for (i = 0; i < CSeqCount; i++) { + for (j = 0; j < 3; j++) + SortedCSeqList[i].c[j] = CSeqList[i].c[j]; + SortedCSeqList[i].cs = (ConSeq)i; + } + + qsort(SortedVSeqList, VSeqCount, sizeof(VSeqPair), tripleVowelCompare); + qsort(SortedCSeqList, CSeqCount, sizeof(CSeqPair), tripleConCompare); + qsort(VCPairList, VCPairCount, sizeof(VCPair), VCPairCompare); + + for (i = 0; i < vnl_lastChar; i++) + IsVnVowel[i] = true; + + unsigned char ch; + for (ch = 'a'; ch <= 'z'; ch++) { + if (ch != 'a' && ch != 'e' && ch != 'i' && ch != 'o' && ch != 'u' && + ch != 'y') { + IsVnVowel[AZLexiLower[ch - 'a']] = false; + IsVnVowel[AZLexiUpper[ch - 'a']] = false; + } + } + IsVnVowel[vnl_dd] = false; + IsVnVowel[vnl_DD] = false; +} + +//------------------------------------------------ +VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2, VnLexiName v3) { + VSeqPair key; + key.v[0] = v1; + key.v[1] = v2; + key.v[2] = v3; + + VSeqPair *pInfo = (VSeqPair *)bsearch(&key, SortedVSeqList, VSeqCount, + sizeof(VSeqPair), tripleVowelCompare); + if (pInfo == 0) + return vs_nil; + return pInfo->vs; +} + +//------------------------------------------------ +ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2, VnLexiName c3) { + CSeqPair key; + key.c[0] = c1; + key.c[1] = c2; + key.c[2] = c3; + + CSeqPair *pInfo = (CSeqPair *)bsearch(&key, SortedCSeqList, CSeqCount, + sizeof(CSeqPair), tripleConCompare); + if (pInfo == 0) + return cs_nil; + return pInfo->cs; +} + +//------------------------------------------------------------------ +int UkEngine::processRoof(UkKeyEvent &ev) { + if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0) + return processAppend(ev); + + VnLexiName target; + switch (ev.evType) { + case vneRoof_a: + target = vnl_ar; + break; + case vneRoof_e: + target = vnl_er; + break; + case vneRoof_o: + target = vnl_or; + break; + default: + target = vnl_nonVnChar; + } + + VowelSeq vs, newVs; + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + int changePos; + bool roofRemoved = false; + + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - (VSeqList[vs].len - 1); + curTonePos = vStart + getTonePosition(vs, vEnd == m_current); + tone = m_buffer[curTonePos].tone; + + bool doubleChangeUO = false; + if (vs == vs_uho || vs == vs_uhoh || vs == vs_uhoi || vs == vs_uhohi) { + // special cases: u+o+ -> uo^, u+o -> uo^, u+o+i -> uo^i, u+oi -> uo^i + newVs = lookupVSeq(vnl_u, vnl_or, VSeqList[vs].v[2]); + doubleChangeUO = true; + } else { + newVs = VSeqList[vs].withRoof; + } + + VowelSeqInfo *pInfo; + + if (newVs == vs_nil) { + if (VSeqList[vs].roofPos == -1) + return processAppend(ev); // roof is not applicable + + // a roof already exists -> undo roof + VnLexiName curCh = m_buffer[vStart + VSeqList[vs].roofPos].vnSym; + if (target != vnl_nonVnChar && curCh != target) + return processAppend( + ev); // specific roof and the roof character don't match + + VnLexiName newCh = + (curCh == vnl_ar) ? vnl_a : ((curCh == vnl_er) ? vnl_e : vnl_o); + changePos = vStart + VSeqList[vs].roofPos; + + if (!m_pCtrl->options.freeMarking && changePos != m_current) + return processAppend(ev); + + markChange(changePos); + m_buffer[changePos].vnSym = newCh; + + if (VSeqList[vs].len == 3) + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, + m_buffer[vStart + 2].vnSym); + else if (VSeqList[vs].len == 2) + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); + else + newVs = lookupVSeq(m_buffer[vStart].vnSym); + + pInfo = &VSeqList[newVs]; + roofRemoved = true; + } else { + pInfo = &VSeqList[newVs]; + if (target != vnl_nonVnChar && pInfo->v[pInfo->roofPos] != target) + return processAppend(ev); + + // check validity of new VC and CV + bool valid = true; + ConSeq c1 = cs_nil; + ConSeq c2 = cs_nil; + if (m_buffer[m_current].c1Offset != -1) + c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; + + if (m_buffer[m_current].c2Offset != -1) + c2 = m_buffer[m_current - m_buffer[m_current].c2Offset].cseq; + + valid = isValidCVC(c1, newVs, c2); + if (!valid) + return processAppend(ev); + + if (doubleChangeUO) { + changePos = vStart; + } else { + changePos = vStart + pInfo->roofPos; + } + if (!m_pCtrl->options.freeMarking && changePos != m_current) + return processAppend(ev); + markChange(changePos); + if (doubleChangeUO) { + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_or; + } else { + m_buffer[changePos].vnSym = pInfo->v[pInfo->roofPos]; + } + } + + for (i = 0; i < pInfo->len; i++) { // update sub-sequences + m_buffer[vStart + i].vseq = pInfo->sub[i]; + } + + // check if tone re-position is needed + newTonePos = vStart + getTonePosition(newVs, vEnd == m_current); + /* //For now, users don't seem to like the following processing, thus + commented out if (roofRemoved && tone != 0 && + (!pInfo->complete || changePos == curTonePos)) { + //remove tone if the vowel sequence becomes incomplete as a result of + roof removal OR + //if removed roof is at the same position as the current tone + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + } else + */ + if (curTonePos != newTonePos && tone != 0) { + markChange(newTonePos); + m_buffer[newTonePos].tone = tone; + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + } + + if (roofRemoved) { + m_singleMode = false; + processAppend(ev); + m_reverted = true; + } + + return 1; +} + +//------------------------------------------------------------------ +// can only be called from processHook +//------------------------------------------------------------------ +int UkEngine::processHookWithUO(UkKeyEvent &ev) { + VowelSeq vs, newVs; + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + bool hookRemoved = false; + bool removeWithUndo = true; + bool toneRemoved = false; + + (void)toneRemoved; // fix warning + + VnLexiName *v; + + if (!m_pCtrl->options.freeMarking && m_buffer[m_current].vOffset != 0) + return processAppend(ev); + + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - (VSeqList[vs].len - 1); + v = VSeqList[vs].v; + curTonePos = vStart + getTonePosition(vs, vEnd == m_current); + tone = m_buffer[curTonePos].tone; + + switch (ev.evType) { + case vneHook_u: + if (v[0] == vnl_u) { + newVs = VSeqList[vs].withHook; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + } else { // v[0] = vnl_uh, -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + hookRemoved = true; + toneRemoved = (m_buffer[vStart].tone != 0); + } + break; + case vneHook_o: + if (v[1] == vnl_o || v[1] == vnl_or) { + if (vEnd == m_current && VSeqList[vs].len == 2 && + m_buffer[m_current].form == vnw_cv && + m_buffer[m_current - 2].cseq == cs_th) { + // o|o^ -> o+ + newVs = VSeqList[vs].withHook; + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { + newVs = lookupVSeq(vnl_uh, vnl_oh, v[2]); + if (v[0] == vnl_u) { + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } + } + } else { // v[1] = vnl_oh, -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); + if (v[0] == vnl_uh) { + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + } else { + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_o; + } + hookRemoved = true; + toneRemoved = (m_buffer[vStart + 1].tone != 0); + } + break; + default: // vneHookAll, vneHookUO: + if (v[0] == vnl_u) { + if (v[1] == vnl_o || v[1] == vnl_or) { + // uo -> uo+ if prefixed by "h", "kh", "th", or stand alone + if ((vs == vs_uo || vs == vs_uor) && vEnd == m_current && + ((m_buffer[m_current].form == vnw_cv && + (m_buffer[m_current - 2].cseq == cs_h || + m_buffer[m_current - 2].cseq == cs_kh || + m_buffer[m_current - 2].cseq == cs_th)) || + m_buffer[m_current].form == vnw_v)) { + newVs = vs_uoh; + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { + // uo -> u+o+ + newVs = VSeqList[vs].withHook; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + newVs = VSeqList[newVs].withHook; + m_buffer[vStart + 1].vnSym = vnl_oh; + } + } else { // uo+ -> u+o+ + newVs = VSeqList[vs].withHook; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + } + } else { // v[0] == vnl_uh + if (v[1] == vnl_o) { // u+o -> u+o+ + newVs = VSeqList[vs].withHook; + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { // v[1] == vnl_oh, u+o+ -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); // vs_uo; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + hookRemoved = true; + toneRemoved = (m_buffer[vStart].tone != 0 || + m_buffer[vStart + 1].tone != 0); + } + } + break; + } + + VowelSeqInfo *p = &VSeqList[newVs]; + for (i = 0; i < p->len; i++) { // update sub-sequences + m_buffer[vStart + i].vseq = p->sub[i]; + } + + // check if tone re-position is needed + newTonePos = vStart + getTonePosition(newVs, vEnd == m_current); + /* //For now, users don't seem to like the following processing, thus + commented out if (hookRemoved && tone != 0 && (!p->complete || toneRemoved)) + { + //remove tone if the vowel sequence becomes incomplete as a result of + hook removal + //OR if a removed hook is at the same position as the current tone + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + } + else + */ + if (curTonePos != newTonePos && tone != 0) { + markChange(newTonePos); + m_buffer[newTonePos].tone = tone; + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + } + + if (hookRemoved && removeWithUndo) { + m_singleMode = false; + processAppend(ev); + m_reverted = true; + } + + return 1; +} + +//------------------------------------------------------------------ +int UkEngine::processHook(UkKeyEvent &ev) { + if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0) + return processAppend(ev); + + VowelSeq vs, newVs; + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + int changePos; + bool hookRemoved = false; + VowelSeqInfo *pInfo; + VnLexiName *v; + + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + + v = VSeqList[vs].v; + + if (VSeqList[vs].len > 1 && ev.evType != vneBowl && + (v[0] == vnl_u || v[0] == vnl_uh) && + (v[1] == vnl_o || v[1] == vnl_oh || v[1] == vnl_or)) + return processHookWithUO(ev); + + vStart = vEnd - (VSeqList[vs].len - 1); + curTonePos = vStart + getTonePosition(vs, vEnd == m_current); + tone = m_buffer[curTonePos].tone; + + newVs = VSeqList[vs].withHook; + if (newVs == vs_nil) { + if (VSeqList[vs].hookPos == -1) + return processAppend(ev); // hook is not applicable + + // a hook already exists -> undo hook + VnLexiName curCh = m_buffer[vStart + VSeqList[vs].hookPos].vnSym; + VnLexiName newCh = + (curCh == vnl_ab) ? vnl_a : ((curCh == vnl_uh) ? vnl_u : vnl_o); + changePos = vStart + VSeqList[vs].hookPos; + if (!m_pCtrl->options.freeMarking && changePos != m_current) + return processAppend(ev); + + switch (ev.evType) { + case vneHook_u: + if (curCh != vnl_uh) + return processAppend(ev); + break; + case vneHook_o: + if (curCh != vnl_oh) + return processAppend(ev); + break; + case vneBowl: + if (curCh != vnl_ab) + return processAppend(ev); + break; + default: + if (ev.evType == vneHook_uo && curCh == vnl_ab) + return processAppend(ev); + } + + markChange(changePos); + m_buffer[changePos].vnSym = newCh; + + if (VSeqList[vs].len == 3) + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, + m_buffer[vStart + 2].vnSym); + else if (VSeqList[vs].len == 2) + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); + else + newVs = lookupVSeq(m_buffer[vStart].vnSym); + + pInfo = &VSeqList[newVs]; + hookRemoved = true; + } else { + pInfo = &VSeqList[newVs]; + + switch (ev.evType) { + case vneHook_u: + if (pInfo->v[pInfo->hookPos] != vnl_uh) + return processAppend(ev); + break; + case vneHook_o: + if (pInfo->v[pInfo->hookPos] != vnl_oh) + return processAppend(ev); + break; + case vneBowl: + if (pInfo->v[pInfo->hookPos] != vnl_ab) + return processAppend(ev); + break; + default: // vneHook_uo, vneHookAll + if (ev.evType == vneHook_uo && pInfo->v[pInfo->hookPos] == vnl_ab) + return processAppend(ev); + } + + // check validity of new VC and CV + bool valid = true; + ConSeq c1 = cs_nil; + ConSeq c2 = cs_nil; + if (m_buffer[m_current].c1Offset != -1) + c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; + + if (m_buffer[m_current].c2Offset != -1) + c2 = m_buffer[m_current - m_buffer[m_current].c2Offset].cseq; + + valid = isValidCVC(c1, newVs, c2); + + if (!valid) + return processAppend(ev); + + changePos = vStart + pInfo->hookPos; + if (!m_pCtrl->options.freeMarking && changePos != m_current) + return processAppend(ev); + + markChange(changePos); + m_buffer[changePos].vnSym = pInfo->v[pInfo->hookPos]; + } + + for (i = 0; i < pInfo->len; i++) { // update sub-sequences + m_buffer[vStart + i].vseq = pInfo->sub[i]; + } + + // check if tone re-position is needed + newTonePos = vStart + getTonePosition(newVs, vEnd == m_current); + /* //For now, users don't seem to like the following processing, thus + commented out if (hookRemoved && tone != 0 && + (!pInfo->complete || (hookRemoved && curTonePos == changePos))) { + //remove tone if the vowel sequence becomes incomplete as a result of + hook removal + //OR if a removed hook was at the same position as the current tone + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + } + else */ + if (curTonePos != newTonePos && tone != 0) { + markChange(newTonePos); + m_buffer[newTonePos].tone = tone; + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + } + + if (hookRemoved) { + m_singleMode = false; + processAppend(ev); + m_reverted = true; + } + + return 1; +} + +//---------------------------------------------------------- +int UkEngine::getTonePosition(VowelSeq vs, bool terminated) const { + VowelSeqInfo &info = VSeqList[vs]; + if (info.len == 1) + return 0; + + if (info.roofPos != -1) + return info.roofPos; + if (info.hookPos != -1) { + if (vs == vs_uhoh || vs == vs_uhohi || + vs == vs_uhohu) // u+o+, u+o+u, u+o+i + return 1; + return info.hookPos; + } + + if (info.len == 3) + return 1; + + if (m_pCtrl->options.modernStyle && + (vs == vs_oa || vs == vs_oe || vs == vs_uy)) + return 1; + + return terminated ? 0 : 1; +} + +//---------------------------------------------------------- +int UkEngine::processTone(UkKeyEvent &ev) { + if (m_current < 0 || !m_pCtrl->vietKey) + return processAppend(ev); + + if (m_buffer[m_current].form == vnw_c && + (m_buffer[m_current].cseq == cs_gi || + m_buffer[m_current].cseq == cs_gin)) { + int p = (m_buffer[m_current].cseq == cs_gi) ? m_current : m_current - 1; + if (m_buffer[p].tone == 0 && ev.tone == 0) + return processAppend(ev); + markChange(p); + if (m_buffer[p].tone == ev.tone) { + m_buffer[p].tone = 0; + m_singleMode = false; + processAppend(ev); + m_reverted = true; + return 1; + } + m_buffer[p].tone = ev.tone; + return 1; + } + + if (m_buffer[m_current].vOffset < 0) + return processAppend(ev); + + int vEnd; + VowelSeq vs; + + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + VowelSeqInfo &info = VSeqList[vs]; + if (m_pCtrl->options.spellCheckEnabled && !m_pCtrl->options.freeMarking && + !info.complete) + return processAppend(ev); + + if (m_buffer[m_current].form == vnw_vc || + m_buffer[m_current].form == vnw_cvc) { + ConSeq cs = m_buffer[m_current].cseq; + if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && + (ev.tone == 2 || ev.tone == 3 || ev.tone == 4)) + return processAppend(ev); // c, ch, p, t suffixes don't allow ` ? ~ + } + + int toneOffset = getTonePosition(vs, vEnd == m_current); + int tonePos = vEnd - (info.len - 1) + toneOffset; + + if (m_buffer[tonePos].tone == 0 && ev.tone == 0) + return processAppend(ev); + + if (m_buffer[tonePos].tone == ev.tone) { + markChange(tonePos); + m_buffer[tonePos].tone = 0; + m_singleMode = false; + processAppend(ev); + m_reverted = true; + return 1; + } + + markChange(tonePos); + m_buffer[tonePos].tone = ev.tone; + return 1; +} + +//---------------------------------------------------------- +int UkEngine::processDd(UkKeyEvent &ev) { + if (!m_pCtrl->vietKey || m_current < 0) + return processAppend(ev); + + int pos; + + // we want to allow dd even in non-vn sequence, because dd is used a lot in + // abbreviation we allow dd only if preceding character is not a vowel + if (m_buffer[m_current].form == vnw_nonVn && + m_buffer[m_current].vnSym == vnl_d && + (m_buffer[m_current - 1].vnSym == vnl_nonVnChar || + !IsVnVowel[m_buffer[m_current - 1].vnSym])) { + m_singleMode = true; + pos = m_current; + markChange(pos); + m_buffer[pos].cseq = cs_dd; + m_buffer[pos].vnSym = vnl_dd; + m_buffer[pos].form = vnw_c; + m_buffer[pos].c1Offset = 0; + m_buffer[pos].c2Offset = -1; + m_buffer[pos].vOffset = -1; + return 1; + } + + if (m_buffer[m_current].c1Offset < 0) { + return processAppend(ev); + } + + pos = m_current - m_buffer[m_current].c1Offset; + if (!m_pCtrl->options.freeMarking && pos != m_current) + return processAppend(ev); + + if (m_buffer[pos].cseq == cs_d) { + markChange(pos); + m_buffer[pos].cseq = cs_dd; + m_buffer[pos].vnSym = vnl_dd; + // never spellcheck a word which starts with dd, because it's used alot + // in abbreviation + m_singleMode = true; + return 1; + } + + if (m_buffer[pos].cseq == cs_dd) { + // undo dd + markChange(pos); + m_buffer[pos].cseq = cs_d; + m_buffer[pos].vnSym = vnl_d; + m_singleMode = false; + processAppend(ev); + m_reverted = true; + return 1; + } + + return processAppend(ev); +} + +//---------------------------------------------------------- +VnLexiName changeCase(VnLexiName x) { + if (x == vnl_nonVnChar) + return x; + if (!(x & 0x01)) + return (VnLexiName)(x + 1); + return (VnLexiName)(x - 1); +} + +//---------------------------------------------------------- +inline VnLexiName vnToLower(VnLexiName x) { + if (x == vnl_nonVnChar) + return x; + if (!(x & 0x01)) // even + return (VnLexiName)(x + 1); + return x; +} + +//---------------------------------------------------------- +int UkEngine::processMapChar(UkKeyEvent &ev) { + int capsLockOn = 0; + int shiftPressed = 0; + if (m_keyCheckFunc) + m_keyCheckFunc(&shiftPressed, &capsLockOn); + + if (capsLockOn) + ev.vnSym = changeCase(ev.vnSym); + + int ret = processAppend(ev); + if (!m_pCtrl->vietKey) + return ret; + + if (m_current >= 0 && m_buffer[m_current].form != vnw_empty && + m_buffer[m_current].form != vnw_nonVn) { + return 1; + } + + if (m_current < 0) + return 0; + + // mapChar doesn't apply + m_current--; + WordInfo &entry = m_buffer[m_current]; + + bool undo = false; + // test if undo is needed + if (entry.form != vnw_empty && entry.form != vnw_nonVn) { + VnLexiName prevSym = entry.vnSym; + if (entry.caps) { + prevSym = (VnLexiName)(prevSym - 1); + } + if (prevSym == ev.vnSym) { + if (entry.form != vnw_c) { + int vStart, vEnd, curTonePos, newTonePos, tone; + VowelSeq vs, newVs; + + vEnd = m_current - entry.vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - VSeqList[vs].len + 1; + curTonePos = vStart + getTonePosition(vs, vEnd == m_current); + tone = m_buffer[curTonePos].tone; + markChange(m_current); + m_current--; + + // check if tone position is needed + if (tone != 0 && m_current >= 0 && + (m_buffer[m_current].form == vnw_v || + m_buffer[m_current].form == vnw_cv)) { + newVs = m_buffer[m_current].vseq; + newTonePos = vStart + getTonePosition(newVs, true); + if (newTonePos != curTonePos) { + markChange(newTonePos); + m_buffer[newTonePos].tone = tone; + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + } + } + } else { + markChange(m_current); + m_current--; + } + undo = true; + } + } + + ev.evType = vneNormal; + ev.chType = m_pCtrl->input.getCharType(ev.keyCode); + ev.vnSym = IsoToVnLexi(ev.keyCode); + ret = processAppend(ev); + if (undo) { + m_singleMode = false; + m_reverted = true; + return 1; + } + return ret; +} + +//---------------------------------------------------------- +int UkEngine::processTelexW(UkKeyEvent &ev) { + if (!m_pCtrl->vietKey) + return processAppend(ev); + + int ret; + static bool usedAsMapChar = false; + int capsLockOn = 0; + int shiftPressed = 0; + if (m_keyCheckFunc) + m_keyCheckFunc(&shiftPressed, &capsLockOn); + + if (usedAsMapChar) { + ev.evType = vneMapChar; + ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; + if (capsLockOn) + ev.vnSym = changeCase(ev.vnSym); + ev.chType = ukcVn; + ret = processMapChar(ev); + if (ret == 0) { + if (m_current >= 0) + m_current--; + usedAsMapChar = false; + ev.evType = vneHookAll; + return processHook(ev); + } + return ret; + } + + ev.evType = vneHookAll; + usedAsMapChar = false; + ret = processHook(ev); + if (ret == 0) { + if (m_current >= 0) + m_current--; + ev.evType = vneMapChar; + ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; + if (capsLockOn) + ev.vnSym = changeCase(ev.vnSym); + ev.chType = ukcVn; + usedAsMapChar = true; + return processMapChar(ev); + } + return ret; +} + +//---------------------------------------------------------- +int UkEngine::checkEscapeVIQR(UkKeyEvent &ev) { + if (m_current < 0) + return 0; + WordInfo &entry = m_buffer[m_current]; + int escape = 0; + if (entry.form == vnw_v || entry.form == vnw_cv) { + switch (ev.keyCode) { + case '^': + escape = (entry.vnSym == vnl_a || entry.vnSym == vnl_o || + entry.vnSym == vnl_e); + break; + case '(': + escape = (entry.vnSym == vnl_a); + break; + case '+': + escape = (entry.vnSym == vnl_o || entry.vnSym == vnl_u); + break; + case '\'': + case '`': + case '?': + case '~': + case '.': + escape = (entry.tone == 0); + break; + } + } else if (entry.form == vnw_nonVn) { + unsigned char ch = toupper(entry.keyCode); + switch (ev.keyCode) { + case '^': + escape = (ch == 'A' || ch == 'O' || ch == 'E'); + break; + case '(': + escape = (ch == 'A'); + break; + case '+': + escape = (ch == 'O' || ch == 'U'); + break; + case '\'': + case '`': + case '?': + case '~': + case '.': + escape = (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || + ch == 'U' || ch == 'Y'); + break; + } + } + + if (escape) { + m_current++; + WordInfo *p = &m_buffer[m_current]; + p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + p->c1Offset = p->c2Offset = p->vOffset = -1; + p->keyCode = '?'; + p->vnSym = vnl_nonVnChar; + + m_current++; + p++; + p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + p->c1Offset = p->c2Offset = p->vOffset = -1; + p->keyCode = ev.keyCode; + p->vnSym = vnl_nonVnChar; + + // write output + m_pOutBuf[0] = '\\'; + m_pOutBuf[1] = ev.keyCode; + *m_pOutSize = 2; + m_outputWritten = true; + } + return escape; +} + +//---------------------------------------------------------- +int UkEngine::processAppend(UkKeyEvent &ev) { + int ret = 0; + switch (ev.chType) { + case ukcReset: +#if defined(_WIN32) + if (ev.keyCode == ENTER_CHAR) { + if (m_pCtrl->options.macroEnabled && macroMatch(ev)) + return 1; + } +#endif + reset(); + return 0; + case ukcWordBreak: + m_singleMode = false; + return processWordEnd(ev); + case ukcNonVn: { + if (m_pCtrl->vietKey && m_pCtrl->charsetId == CONV_CHARSET_VIQR && + checkEscapeVIQR(ev)) + return 1; + + m_current++; + WordInfo &entry = m_buffer[m_current]; + entry.form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + entry.keyCode = ev.keyCode; + entry.vnSym = vnToLower(ev.vnSym); + entry.tone = 0; + entry.caps = (entry.vnSym != ev.vnSym); + if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + } + case ukcVn: { + if (IsVnVowel[ev.vnSym]) { + VnLexiName v = (VnLexiName)StdVnNoTone[vnToLower(ev.vnSym)]; + if (m_current >= 0 && m_buffer[m_current].form == vnw_c && + ((m_buffer[m_current].cseq == cs_q && v == vnl_u) || + (m_buffer[m_current].cseq == cs_g && v == vnl_i))) { + return appendConsonnant( + ev); // process u after q, i after g as consonnants + } + return appendVowel(ev); + } + return appendConsonnant(ev); + } break; + } + + return ret; +} + +//---------------------------------------------------------- +int UkEngine::appendVowel(UkKeyEvent &ev) { + bool autoCompleted = false; + bool complexEvent = false; + + m_current++; + WordInfo &entry = m_buffer[m_current]; + + VnLexiName lowerSym = vnToLower(ev.vnSym); + VnLexiName canSym = (VnLexiName)StdVnNoTone[lowerSym]; + + entry.vnSym = canSym; + entry.caps = (lowerSym != ev.vnSym); + entry.tone = (lowerSym - canSym) / 2; + entry.keyCode = ev.keyCode; + + if (m_current == 0 || !m_pCtrl->vietKey) { + entry.form = vnw_v; + entry.c1Offset = entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = lookupVSeq(canSym); + + if (!m_pCtrl->vietKey || + ((m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && + isalpha(entry.keyCode))) { + return 0; + } + markChange(m_current); + return 1; + } + + WordInfo &prev = m_buffer[m_current - 1]; + VowelSeq vs, newVs; + ConSeq cs; + int prevTonePos; + int tone, newTone, tonePos, newTonePos; + + switch (prev.form) { + + case vnw_empty: + entry.form = vnw_v; + entry.c1Offset = entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs = lookupVSeq(canSym); + break; + + case vnw_nonVn: + case vnw_cvc: + case vnw_vc: + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; + + case vnw_v: + case vnw_cv: + vs = prev.vseq; + + prevTonePos = (m_current - 1) - (VSeqList[vs].len - 1) + + getTonePosition(vs, true); + tone = m_buffer[prevTonePos].tone; + + // u+o/uo+ + u/i -> u+o+ + u/i + if ((vs == vs_uoh || vs == vs_uho) && + (lowerSym == vnl_i || lowerSym == vnl_u)) { + if (vs == vs_uho) { + markChange(m_current - 1); + prev.vnSym = vnl_oh; + prev.vseq = vs_uhoh; + } else { + markChange(m_current - 2); + m_buffer[m_current - 2].vnSym = vnl_uh; + m_buffer[m_current - 2].vseq = vs_uh; + } + + vs = vs_uhoh; + complexEvent = true; + } + + if (lowerSym != canSym && tone != 0) // new sym has a tone, but there's + // is already a preceeding tone + newVs = vs_nil; + else { + if (VSeqList[vs].len == 3) + newVs = vs_nil; + else if (VSeqList[vs].len == 2) + newVs = + lookupVSeq(VSeqList[vs].v[0], VSeqList[vs].v[1], canSym); + else + newVs = lookupVSeq(VSeqList[vs].v[0], canSym); + } + + if (newVs != vs_nil && prev.form == vnw_cv) { + cs = m_buffer[m_current - 1 - prev.c1Offset].cseq; + if (!isValidCV(cs, newVs)) + newVs = vs_nil; + } + + if (newVs == vs_nil) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; + } + + entry.form = prev.form; + if (prev.form == vnw_cv) + entry.c1Offset = prev.c1Offset + 1; + else + entry.c1Offset = -1; + entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs; + entry.tone = 0; + + newTone = (lowerSym - canSym) / 2; + if (tone == 0) { + if (newTone != 0) { + tone = newTone; + tonePos = getTonePosition(newVs, true) + + ((m_current - 1) - VSeqList[vs].len + 1); + markChange(tonePos); + m_buffer[tonePos].tone = tone; + return 1; + } + } else { + newTonePos = getTonePosition(newVs, true) + + ((m_current - 1) - VSeqList[vs].len + 1); + if (newTonePos != prevTonePos) { + markChange(prevTonePos); + m_buffer[prevTonePos].tone = 0; + markChange(newTonePos); + if (newTone != 0) + tone = newTone; + m_buffer[newTonePos].tone = tone; + return 1; + } + if (newTone != 0 && newTone != tone) { + tone = newTone; + markChange(prevTonePos); + m_buffer[prevTonePos].tone = tone; + return 1; + } + } + + break; + case vnw_c: + newVs = lookupVSeq(canSym); + cs = prev.cseq; + if (!isValidCV(cs, newVs)) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; + } + + entry.form = vnw_cv; + entry.c1Offset = 1; + entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs; + + if (cs == cs_gi && prev.tone != 0) { + if (entry.tone == 0) + entry.tone = prev.tone; + markChange(m_current - 1); + prev.tone = 0; + return 1; + } + + break; + } + + if (complexEvent) { + return 1; + } + + if (!autoCompleted && (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && + isalpha(entry.keyCode)) { + return 0; + } + + markChange(m_current); + return 1; +} + +//---------------------------------------------------------- +int UkEngine::appendConsonnant(UkKeyEvent &ev) { + bool complexEvent = false; + m_current++; + WordInfo &entry = m_buffer[m_current]; + + VnLexiName lowerSym = vnToLower(ev.vnSym); + + entry.vnSym = lowerSym; + entry.caps = (lowerSym != ev.vnSym); + entry.keyCode = ev.keyCode; + entry.tone = 0; + + if (m_current == 0 || !m_pCtrl->vietKey) { + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + entry.cseq = lookupCSeq(lowerSym); + if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + } + + ConSeq cs, newCs, c1; + VowelSeq vs, newVs; + bool isValid; + + WordInfo &prev = m_buffer[m_current - 1]; + + switch (prev.form) { + case vnw_nonVn: + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_empty: + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + entry.cseq = lookupCSeq(lowerSym); + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_v: + case vnw_cv: + vs = prev.vseq; + newVs = vs; + if (vs == vs_uoh || vs == vs_uho) { + newVs = vs_uhoh; + } + + c1 = cs_nil; + if (prev.c1Offset != -1) + c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; + + newCs = lookupCSeq(lowerSym); + isValid = isValidCVC(c1, newVs, newCs); + + if (isValid) { + // check u+o -> u+o+ + if (vs == vs_uho) { + markChange(m_current - 1); + prev.vnSym = vnl_oh; + prev.vseq = vs_uhoh; + complexEvent = true; + } else if (vs == vs_uoh) { + markChange(m_current - 2); + m_buffer[m_current - 2].vnSym = vnl_uh; + m_buffer[m_current - 2].vseq = vs_uh; + prev.vseq = vs_uhoh; + complexEvent = true; + } + + if (prev.form == vnw_v) { + entry.form = vnw_vc; + entry.c1Offset = -1; + entry.c2Offset = 0; + entry.vOffset = 1; + } else { // prev == vnw_cv + entry.form = vnw_cvc; + entry.c1Offset = prev.c1Offset + 1; + entry.c2Offset = 0; + entry.vOffset = 1; + } + entry.cseq = newCs; + + // reposition tone if needed + int oldIdx = (m_current - 1) - (VSeqList[vs].len - 1) + + getTonePosition(vs, true); + if (m_buffer[oldIdx].tone != 0) { + int newIdx = (m_current - 1) - (VSeqList[newVs].len - 1) + + getTonePosition(newVs, false); + if (newIdx != oldIdx) { + markChange(newIdx); + m_buffer[newIdx].tone = m_buffer[oldIdx].tone; + markChange(oldIdx); + m_buffer[oldIdx].tone = 0; + return 1; + } + } + } else { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + } + + if (complexEvent) { + return 1; + } + + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_c: + case vnw_vc: + case vnw_cvc: + cs = prev.cseq; + if (CSeqList[cs].len == 3) + newCs = cs_nil; + else if (CSeqList[cs].len == 2) + newCs = lookupCSeq(CSeqList[cs].c[0], CSeqList[cs].c[1], lowerSym); + else + newCs = lookupCSeq(CSeqList[cs].c[0], lowerSym); + + if (newCs != cs_nil && (prev.form == vnw_vc || prev.form == vnw_cvc)) { + // Check CVC combination + c1 = cs_nil; + if (prev.c1Offset != -1) + c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; + + int vIdx = (m_current - 1) - prev.vOffset; + vs = m_buffer[vIdx].vseq; + isValid = isValidCVC(c1, vs, newCs); + + if (!isValid) + newCs = cs_nil; + } + + if (newCs == cs_nil) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + } else { + if (prev.form == vnw_c) { + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + } else if (prev.form == vnw_vc) { + entry.form = vnw_vc; + entry.c1Offset = -1; + entry.c2Offset = 0; + entry.vOffset = prev.vOffset + 1; + } else { // vnw_cvc + entry.form = vnw_cvc; + entry.c1Offset = prev.c1Offset + 1; + entry.c2Offset = 0; + entry.vOffset = prev.vOffset + 1; + } + entry.cseq = newCs; + } + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + } + + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; +} + +//---------------------------------------------------------- +int UkEngine::processEscChar(UkKeyEvent &ev) { + if (m_pCtrl->vietKey && m_current >= 0 && + m_buffer[m_current].form != vnw_empty && + m_buffer[m_current].form != vnw_nonVn) { + m_toEscape = true; + } + return processAppend(ev); +} + +//---------------------------------------------------------- +void UkEngine::pass(int keyCode) { + UkKeyEvent ev; + m_pCtrl->input.keyCodeToEvent(keyCode, ev); + processAppend(ev); +} + +//--------------------------------------------- +// This can be called only after other processing have been done. +// The new event is supposed to be put into m_buffer already +//--------------------------------------------- +int UkEngine::processNoSpellCheck(UkKeyEvent &ev) { + WordInfo &entry = m_buffer[m_current]; + if (IsVnVowel[entry.vnSym]) { + entry.form = vnw_v; + entry.vOffset = 0; + entry.vseq = lookupVSeq(entry.vnSym); + entry.c1Offset = entry.c2Offset = -1; + } else { + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + entry.cseq = lookupCSeq(entry.vnSym); + } + + if (ev.evType == vneNormal && + ((entry.keyCode >= 'a' && entry.keyCode <= 'z') || + (entry.keyCode >= 'A' && entry.keyCode <= 'Z'))) + return 0; + markChange(m_current); + return 1; +} +//---------------------------------------------------------- +int UkEngine::process(unsigned int keyCode, int &backs, unsigned char *outBuf, + int &outSize, UkOutputType &outType) { + UkKeyEvent ev; + prepareBuffer(); + m_backs = 0; + m_changePos = m_current + 1; + m_pOutBuf = outBuf; + m_pOutSize = &outSize; + m_outputWritten = false; + m_reverted = false; + m_keyRestored = false; + m_keyRestoring = false; + m_outType = UkCharOutput; + + m_pCtrl->input.keyCodeToEvent(keyCode, ev); + + int ret; + if (!m_toEscape) { + ret = (this->*UkKeyProcList[ev.evType])(ev); + } else { + m_toEscape = false; + if (m_current < 0 || ev.evType == vneNormal || + ev.evType == vneEscChar) { + ret = processAppend(ev); + } else { + m_current--; + processAppend(ev); + markChange(m_current); // this will assign m_backs to 1 and mark the + // character for output + ret = 1; + } + } + + if (m_pCtrl->vietKey && m_current >= 0 && + m_buffer[m_current].form == vnw_nonVn && ev.chType == ukcVn && + (!m_pCtrl->options.spellCheckEnabled || m_singleMode)) { + + // The spell check has failed, but because we are in non-spellcheck + // mode, we consider the new character as the beginning of a new word + ret = processNoSpellCheck(ev); + /* + if ((!m_pCtrl->options.spellCheckEnabled || m_singleMode) || + ( !m_reverted && + (m_current < 1 || m_buffer[m_current-1].form != vnw_nonVn)) ) { + + ret = processNoSpellCheck(ev); + } + */ + } + + // we add key to key buffer only if that key has not caused a reset + if (m_current >= 0) { + ev.chType = m_pCtrl->input.getCharType(ev.keyCode); + m_keyCurrent++; + m_keyStrokes[m_keyCurrent].ev = ev; + m_keyStrokes[m_keyCurrent].converted = (ret && !m_keyRestored); + } + + if (ret == 0) { + backs = 0; + outSize = 0; + outType = m_outType; + return 0; + } + + backs = m_backs; + if (!m_outputWritten) { + writeOutput(outBuf, outSize); + } + outType = m_outType; + + return ret; +} +//---------------------------------------------------------- +void UkEngine::rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, + int &outSize) { + static const std::unordered_map map{ + {vnl_Ar, vneRoof_a}, {vnl_Ab, vneBowl}, {vnl_DD, vneDd}, + {vnl_Er, vneRoof_e}, {vnl_Or, vneRoof_o}, {vnl_Oh, vneHook_o}, + {vnl_Uh, vneHook_u}}; + + if (ch == vnl_nonVnChar) { + return; + } + + prepareBuffer(); + m_backs = 0; + m_changePos = m_current + 1; + m_pOutBuf = outBuf; + m_pOutSize = &outSize; + + UkKeyEvent ev; + + auto rootChar = StdVnRootChar[ch]; + auto noToneChar = StdVnNoTone[ch]; + + auto keyCode = UnicodeTable[rootChar]; + m_pCtrl->input.keyCodeToEvent(keyCode, ev); + + // root char + processAppend(ev); + + // add root char to key strokes + m_keyCurrent++; + m_keyStrokes[m_keyCurrent].ev = ev; + m_keyStrokes[m_keyCurrent].converted = true; + + // modify vowel + auto it = + map.find(noToneChar % 2 == 0 ? static_cast(noToneChar) + : static_cast(noToneChar - 1)); + if (it != map.end()) { + ev.evType = it->second; + (this->*UkKeyProcList[ev.evType])(ev); + } + + // tone + auto tone = (ch - noToneChar) / 2; + if (tone >= 1 && tone <= 5) { + ev.evType = vneTone0 + tone; + ev.tone = tone; + (this->*UkKeyProcList[ev.evType])(ev); + } + + backs = m_backs; + writeOutput(outBuf, outSize); +} + +//---------------------------------------------------------- +// Returns 0 on success +// error code otherwise +// outBuf: buffer to write +// outSize: [in] size of buffer in bytes +// [out] bytes written to buffer +//---------------------------------------------------------- +int UkEngine::writeOutput(unsigned char *outBuf, int &outSize) { + StdVnChar stdChar; + int i, bytesWritten; + int ret = 1; + StringBOStream os(outBuf, outSize); + VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); + pCharset->startOutput(); + + for (i = m_changePos; i <= m_current; i++) { + if (m_buffer[i].vnSym != vnl_nonVnChar) { + // process vn symbol + stdChar = m_buffer[i].vnSym + VnStdCharOffset; + if (m_buffer[i].caps) + stdChar--; + if (m_buffer[i].tone != 0) + stdChar += m_buffer[i].tone * 2; + } else { + stdChar = IsoToStdVnChar(m_buffer[i].keyCode); + } + + if (stdChar != INVALID_STD_CHAR) + ret = pCharset->putChar(os, stdChar, bytesWritten); + } + + outSize = os.getOutBytes(); + return (ret ? 0 : VNCONV_OUT_OF_MEMORY); +} + +//--------------------------------------------- +// Returns the number of backspaces needed to +// go back from last to first +//--------------------------------------------- +int UkEngine::getSeqSteps(int first, int last) const { + StdVnChar stdChar; + + if (last < first) + return 0; + + if (m_pCtrl->charsetId == CONV_CHARSET_XUTF8 || + m_pCtrl->charsetId == CONV_CHARSET_UNICODE) + return (last - first + 1); + + StringBOStream os(0, 0); + int i, bytesWritten; + + VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); + pCharset->startOutput(); + + for (i = first; i <= last; i++) { + if (m_buffer[i].vnSym != vnl_nonVnChar) { + // process vn symbol + stdChar = m_buffer[i].vnSym + VnStdCharOffset; + if (m_buffer[i].caps) + stdChar--; + if (m_buffer[i].tone != 0) + stdChar += m_buffer[i].tone * 2; + } else { + stdChar = m_buffer[i].keyCode; + } + + if (stdChar != INVALID_STD_CHAR) + pCharset->putChar(os, stdChar, bytesWritten); + } + + int len = os.getOutBytes(); + if (m_pCtrl->charsetId == CONV_CHARSET_UNIDECOMPOSED) + len = len / 2; + return len; +} + +//--------------------------------------------- +void UkEngine::markChange(int pos) { + if (pos < m_changePos) { + m_backs += getSeqSteps(pos, m_changePos - 1); + m_changePos = pos; + } +} + +//---------------------------------------------------------------- +// Called from processBackspace to keep +// character buffer (m_buffer) and key stroke buffer in synch +//---------------------------------------------------------------- +void UkEngine::synchKeyStrokeBuffer() { + // synchronize with key-stroke buffer + if (m_keyCurrent >= 0) + m_keyCurrent--; + if (m_current >= 0 && m_buffer[m_current].form == vnw_empty) { + // in character buffer, we have reached a word break, + // so we also need to move key stroke pointer backward to corresponding + // word break + while (m_keyCurrent >= 0 && + m_keyStrokes[m_keyCurrent].ev.chType != ukcWordBreak) { + m_keyCurrent--; + } + } +} + +//--------------------------------------------- +int UkEngine::processBackspace(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType) { + outType = UkCharOutput; + if (!m_pCtrl->vietKey || m_current < 0) { + backs = 0; + outSize = 0; + return 0; + } + + m_backs = 0; + m_changePos = m_current + 1; + markChange(m_current); + + if (m_current == 0 || m_buffer[m_current].form == vnw_empty || + m_buffer[m_current].form == vnw_nonVn || + m_buffer[m_current].form == vnw_c || + m_buffer[m_current - 1].form == vnw_c || + m_buffer[m_current - 1].form == vnw_cvc || + m_buffer[m_current - 1].form == vnw_vc) { + + m_current--; + backs = m_backs; + outSize = 0; + synchKeyStrokeBuffer(); + return (backs > 1); + } + + VowelSeq vs, newVs; + int curTonePos, newTonePos, tone, vStart, vEnd; + + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - VSeqList[vs].len + 1; + newVs = m_buffer[m_current - 1].vseq; + curTonePos = vStart + getTonePosition(vs, vEnd == m_current); + newTonePos = vStart + getTonePosition(newVs, true); + tone = m_buffer[curTonePos].tone; + + if (tone == 0 || curTonePos == newTonePos || + (curTonePos == m_current && m_buffer[m_current].tone != 0)) { + m_current--; + backs = m_backs; + outSize = 0; + synchKeyStrokeBuffer(); + return (backs > 1); + } + + markChange(newTonePos); + m_buffer[newTonePos].tone = tone; + markChange(curTonePos); + m_buffer[curTonePos].tone = 0; + m_current--; + synchKeyStrokeBuffer(); + backs = m_backs; + writeOutput(outBuf, outSize); + return 1; +} + +//------------------------------------------------ +void UkEngine::reset() { + m_current = -1; + m_keyCurrent = -1; + m_singleMode = false; + m_toEscape = false; +} + +//------------------------------------------------ +void UkEngine::resetKeyBuf() { m_keyCurrent = -1; } + +//------------------------------------------------ +UkEngine::UkEngine() { + if (!m_classInit) { + engineClassInit(); + m_classInit = true; + } + m_pCtrl = 0; + m_bufSize = MAX_UK_ENGINE; + m_keyBufSize = MAX_UK_ENGINE; + m_current = -1; + m_keyCurrent = -1; + m_singleMode = false; + m_keyCheckFunc = 0; + m_reverted = false; + m_toEscape = false; + m_keyRestored = false; +} + +//---------------------------------------------------- +// make sure there are at least 10 entries available +//---------------------------------------------------- +void UkEngine::prepareBuffer() { + int rid; + // prepare symbol buffer + if (m_current >= 0 && m_current + 10 >= m_bufSize) { + // Get rid of at least half of the current entries + // don't get rid from the middle of a word. + for (rid = m_current / 2; + m_buffer[rid].form != vnw_empty && rid < m_current; rid++) + ; + if (rid == m_current) { + m_current = -1; + } else { + rid++; + memmove(m_buffer, m_buffer + rid, + (m_current - rid + 1) * sizeof(WordInfo)); + m_current -= rid; + } + } + + // prepare key stroke buffer + if (m_keyCurrent > 0 && m_keyCurrent + 1 >= m_keyBufSize) { + // Get rid of at least half of the current entries + rid = m_keyCurrent / 2; + memmove(m_keyStrokes, m_keyStrokes + rid, + (m_keyCurrent - rid + 1) * sizeof(m_keyStrokes[0])); + m_keyCurrent -= rid; + } +} + +#define ENTER_CHAR 13 +enum VnCaseType { VnCaseNoChange, VnCaseAllCapital, VnCaseAllSmall }; + +//---------------------------------------------------- +int UkEngine::macroMatch(UkKeyEvent &ev) { + int capsLockOn = 0; + int shiftPressed = 0; + if (m_keyCheckFunc) + m_keyCheckFunc(&shiftPressed, &capsLockOn); + + if (shiftPressed && (ev.keyCode == ' ' || ev.keyCode == ENTER_CHAR)) + return 0; + + const StdVnChar *pMacText = NULL; + StdVnChar key[MAX_MACRO_KEY_LEN + 1]; + StdVnChar *pKeyStart; + + // Use static macro text so we can gain a bit of performance + // by avoiding memory allocation each time this function is called + static StdVnChar macroText[MAX_MACRO_TEXT_LEN + 1]; + + int i, j; + + i = m_current; + while (i >= 0 && (m_current - i + 1) < MAX_MACRO_KEY_LEN) { + while (i >= 0 && m_buffer[i].form != vnw_empty && + (m_current - i + 1) < MAX_MACRO_KEY_LEN) + i--; + if (i >= 0 && m_buffer[i].form != vnw_empty) + return 0; + + if (i >= 0) { + if (m_buffer[i].vnSym != vnl_nonVnChar) { + key[0] = m_buffer[i].vnSym + VnStdCharOffset; + if (m_buffer[i].caps) + key[0]--; + key[0] += m_buffer[i].tone * 2; + } else + key[0] = m_buffer[i].keyCode; + } + + for (j = i + 1; j <= m_current; j++) { + if (m_buffer[j].vnSym != vnl_nonVnChar) { + key[j - i] = m_buffer[j].vnSym + VnStdCharOffset; + if (m_buffer[j].caps) + key[j - i]--; + key[j - i] += m_buffer[j].tone * 2; + } else + key[j - i] = m_buffer[j].keyCode; + } + key[m_current - i + 1] = 0; + // search macro table + pMacText = m_pCtrl->macStore.lookup(key + 1); + if (pMacText) { + i++; // mark the position where change is needed + pKeyStart = key + 1; + break; + } + if (i >= 0) { + pMacText = m_pCtrl->macStore.lookup(key); + if (pMacText) { + pKeyStart = key; + break; + } + } + i--; + } + + if (!pMacText) { + return 0; + } + + markChange(i); + + // determine the form of macro replacements: ALL CAPITALS, First Character + // Capital, or no change + VnCaseType macroCase; + if (IS_STD_VN_LOWER(*pKeyStart)) { + macroCase = VnCaseAllSmall; + } else if (IS_STD_VN_UPPER(*pKeyStart)) { + macroCase = VnCaseAllCapital; + for (i = 1; pKeyStart[i]; i++) { + if (IS_STD_VN_LOWER(pKeyStart[i])) { + macroCase = VnCaseNoChange; + } + } + } else + macroCase = VnCaseNoChange; + + // Convert case of macro text according to macroCase + int charCount = 0; + while (pMacText[charCount] != 0) + charCount++; + + for (i = 0; i < charCount; i++) { + if (macroCase == VnCaseAllCapital) + macroText[i] = StdVnToUpper(pMacText[i]); + else if (macroCase == VnCaseAllSmall) + macroText[i] = StdVnToLower(pMacText[i]); + else + macroText[i] = pMacText[i]; + } + + // Convert to target output charset + int outSize; + int maxOutSize = *m_pOutSize; + int inLen = charCount * sizeof(StdVnChar); + VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, (UKBYTE *)macroText, + (UKBYTE *)m_pOutBuf, &inLen, &maxOutSize); + outSize = maxOutSize; + + // write the last input character + StdVnChar vnChar; + if (outSize < *m_pOutSize && ev.keyCode) { + maxOutSize = *m_pOutSize - outSize; + if (ev.vnSym != vnl_nonVnChar) + vnChar = ev.vnSym + VnStdCharOffset; + else + vnChar = ev.keyCode; + inLen = sizeof(StdVnChar); + VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, + (UKBYTE *)&vnChar, ((UKBYTE *)m_pOutBuf) + outSize, &inLen, + &maxOutSize); + outSize += maxOutSize; + } + int backs = m_backs; // store m_backs before calling reset + reset(); + m_outputWritten = true; + m_backs = backs; + *m_pOutSize = outSize; + return 1; +} + +//---------------------------------------------------- +int UkEngine::restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType) { + outType = UkKeyOutput; + if (!lastWordHasVnMark()) { + backs = 0; + outSize = 0; + return 0; + } + + m_backs = 0; + m_changePos = m_current + 1; + + int keyStart; + bool converted = false; + for (keyStart = m_keyCurrent; + keyStart >= 0 && m_keyStrokes[keyStart].ev.chType != ukcWordBreak; + keyStart--) { + if (m_keyStrokes[keyStart].converted) { + converted = true; + } + } + keyStart++; + + if (!converted) { + // no key stroke has been converted, so it doesn't make sense to restore + // key strokes + backs = 0; + outSize = 0; + return 0; + } + + // int i = m_current; + while (m_current >= 0 && m_buffer[m_current].form != vnw_empty) + m_current--; + markChange(m_current + 1); + backs = m_backs; + + int count; + int i; + UkKeyEvent ev; + m_keyRestoring = true; + for (i = keyStart, count = 0; i <= m_keyCurrent; i++) { + if (count < outSize) { + outBuf[count++] = (unsigned char)m_keyStrokes[i].ev.keyCode; + } + m_pCtrl->input.keyCodeToSymbol(m_keyStrokes[i].ev.keyCode, ev); + m_keyStrokes[i].converted = false; + processAppend(ev); + } + outSize = count; + m_keyRestoring = false; + + return 1; +} + +//-------------------------------------------------- +void UkEngine::setSingleMode() { m_singleMode = true; } + +//-------------------------------------------------- +static void SetupUnikeyEngineOnce() { + SetupInputClassifierTable(); + int i; + VnLexiName lexi; + + // Calculate IsoStdVnCharMap + for (i = 0; i < 256; i++) { + IsoStdVnCharMap[i] = i; + } + + for (i = 0; SpecialWesternChars[i]; i++) { + IsoStdVnCharMap[SpecialWesternChars[i]] = + (vnl_lastChar + i) + VnStdCharOffset; + } + + for (i = 0; i < 256; i++) { + if ((lexi = IsoToVnLexi(i)) != vnl_nonVnChar) { + IsoStdVnCharMap[i] = lexi + VnStdCharOffset; + } + } +} + +std::once_flag setupFlag; + +void SetupUnikeyEngine() { std::call_once(setupFlag, SetupUnikeyEngineOnce); } + +//-------------------------------------------------- +bool UkEngine::atWordBeginning() const { + return (m_current < 0 || m_buffer[m_current].form == vnw_empty); +} + +//-------------------------------------------------- +// Check for macro first, if there's a match, expand macro. If not: +// Spell-check, if is valid Vietnamese, return normally, if not: +// restore key strokes if auto-restore is enabled +//-------------------------------------------------- +int UkEngine::processWordEnd(UkKeyEvent &ev) { + if (m_pCtrl->options.macroEnabled && macroMatch(ev)) + return 1; + + auto putKeyInBuffer = [this](UkKeyEvent &ev) { + m_current++; + WordInfo &entry = m_buffer[m_current]; + entry.form = vnw_empty; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + entry.keyCode = ev.keyCode; + entry.vnSym = vnToLower(ev.vnSym); + entry.caps = (entry.vnSym != ev.vnSym); + }; + + if (!m_pCtrl->options.spellCheckEnabled || m_singleMode || m_current < 0 || + m_keyRestoring) { + putKeyInBuffer(ev); + return 0; + } + + int outSize = 0; + if (m_pCtrl->options.autoNonVnRestore && lastWordIsNonVn()) { + outSize = *m_pOutSize; + if (restoreKeyStrokes(m_backs, m_pOutBuf, outSize, m_outType)) { + m_keyRestored = true; + m_outputWritten = true; + } + } + + putKeyInBuffer(ev); + + if (m_keyRestored && outSize < *m_pOutSize) { + if (ev.keyCode) { + m_pOutBuf[outSize] = ev.keyCode; + outSize++; + } + *m_pOutSize = outSize; + return 1; + } + + return 0; +} + +//--------------------------------------------------------------------------- +// Test if last word is a non-Vietnamese word, so that +// the engine can restore key strokes if it is indeed not a Vietnamese word +//--------------------------------------------------------------------------- +bool UkEngine::lastWordIsNonVn() const { + if (m_current < 0) + return false; + + switch (m_buffer[m_current].form) { + case vnw_nonVn: + return true; + case vnw_empty: + case vnw_c: + return false; + case vnw_v: + case vnw_cv: + return !VSeqList[m_buffer[m_current].vseq].complete; + case vnw_vc: + case vnw_cvc: { + int vIndex = m_current - m_buffer[m_current].vOffset; + VowelSeq vs = m_buffer[vIndex].vseq; + if (!VSeqList[vs].complete) + return true; + ConSeq cs = m_buffer[m_current].cseq; + ConSeq c1 = cs_nil; + if (m_buffer[m_current].c1Offset != -1) + c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; + + if (!isValidCVC(c1, vs, cs)) { + return true; + } + + int tonePos = + (vIndex - VSeqList[vs].len + 1) + getTonePosition(vs, false); + int tone = m_buffer[tonePos].tone; + if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && + (tone == 2 || tone == 3 || tone == 4)) { + return true; + } + } + } + return false; +} + +//--------------------------------------------------------------------------- +// Test if last word has a Vietnamese mark, that is tones, decorators +//--------------------------------------------------------------------------- +bool UkEngine::lastWordHasVnMark() const { + for (int i = m_current; i >= 0 && m_buffer[i].form != vnw_empty; i--) { + VnLexiName sym = m_buffer[i].vnSym; + if (sym != vnl_nonVnChar) { + if (IsVnVowel[sym]) { + if (m_buffer[i].tone) + return true; + } + if (sym != StdVnRootChar[sym]) + return true; + } + } + return false; +} diff --git a/unikey/core/ukengine.h b/unikey/core/ukengine.h new file mode 100644 index 00000000..c79a1304 --- /dev/null +++ b/unikey/core/ukengine.h @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef __UKENGINE_H +#define __UKENGINE_H + +#include "charset.h" +#include "inputproc.h" +#include "mactab.h" +#include "vnlexi.h" +#include + +// This is a shared object among processes, do not put any pointer in it +struct UkSharedMem { + // states + bool vietKey; + + UnikeyOptions options; + UkInputProcessor input; + bool usrKeyMapLoaded; + int usrKeyMap[256]; + int charsetId; + + CMacroTable macStore; +}; + +#define MAX_UK_ENGINE 128 + +enum VnWordForm { vnw_nonVn, vnw_empty, vnw_c, vnw_v, vnw_cv, vnw_vc, vnw_cvc }; + +typedef std::function + CheckKeyboardCaseCb; + +struct KeyBufEntry { + UkKeyEvent ev; + bool converted; +}; + +class UkEngine { +public: + UkEngine(); + void setCtrlInfo(UkSharedMem *p) { m_pCtrl = p; } + + void setCheckKbCaseFunc(CheckKeyboardCaseCb pFunc) { + m_keyCheckFunc = pFunc; + } + + bool atWordBeginning() const; + + int process(unsigned int keyCode, int &backs, unsigned char *outBuf, + int &outSize, UkOutputType &outType); + // just pass through without filtering + void pass(int keyCode); + // rebuild preedit from surrounding char + void rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, + int &outSize); + + void setSingleMode(); + + int processBackspace(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType); + void reset(); + int restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType); + + // following methods must be public just to enable the use of pointers to + // them they should not be called from outside. + int processTone(UkKeyEvent &ev); + int processRoof(UkKeyEvent &ev); + int processHook(UkKeyEvent &ev); + int processAppend(UkKeyEvent &ev); + int appendVowel(UkKeyEvent &ev); + int appendConsonnant(UkKeyEvent &ev); + int processDd(UkKeyEvent &ev); + int processMapChar(UkKeyEvent &ev); + int processTelexW(UkKeyEvent &ev); + int processEscChar(UkKeyEvent &ev); + +protected: + static bool m_classInit; + CheckKeyboardCaseCb m_keyCheckFunc; + UkSharedMem *m_pCtrl; + + int m_changePos; + int m_backs; + int m_bufSize; + int m_current; + int m_singleMode; + + int m_keyBufSize; + // unsigned int m_keyStrokes[MAX_UK_ENGINE]; + KeyBufEntry m_keyStrokes[MAX_UK_ENGINE]; + int m_keyCurrent; + bool m_toEscape; + + // variables valid in one session + unsigned char *m_pOutBuf; + int *m_pOutSize; + bool m_outputWritten; + bool m_reverted; + bool m_keyRestored; + bool m_keyRestoring; + UkOutputType m_outType; + + struct WordInfo { + // info for word ending at this position + VnWordForm form; + int c1Offset, vOffset, c2Offset; + + union { + VowelSeq vseq; + ConSeq cseq; + }; + + // info for current symbol + int caps, tone; + // canonical symbol, after caps, tone are removed + // for non-Vn, vnSym == -1 + VnLexiName vnSym; + int keyCode; + }; + + WordInfo m_buffer[MAX_UK_ENGINE]; + + int processHookWithUO(UkKeyEvent &ev); + int macroMatch(UkKeyEvent &ev); + void markChange(int pos); + void prepareBuffer(); // make sure we have a least 10 entries available + int writeOutput(unsigned char *outBuf, int &outSize); + // int getSeqLength(int first, int last); + int getSeqSteps(int first, int last) const; + int getTonePosition(VowelSeq vs, bool terminated) const; + void resetKeyBuf(); + int checkEscapeVIQR(UkKeyEvent &ev); + int processNoSpellCheck(UkKeyEvent &ev); + int processWordEnd(UkKeyEvent &ev); + void synchKeyStrokeBuffer(); + bool lastWordHasVnMark() const; + bool lastWordIsNonVn() const; +}; + +void SetupUnikeyEngine(); + +#endif diff --git a/unikey/core/unikeyinputcontext.cpp b/unikey/core/unikeyinputcontext.cpp new file mode 100644 index 00000000..2043bf17 --- /dev/null +++ b/unikey/core/unikeyinputcontext.cpp @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2018-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include "unikeyinputcontext.h" +#include "ukengine.h" +#include "usrkeymap.h" +#include +#include +#include +#include + +using namespace std; + +//-------------------------------------------- +void CreateDefaultUnikeyOptions(UnikeyOptions *pOpt) { + pOpt->freeMarking = 1; + pOpt->modernStyle = 0; + pOpt->macroEnabled = 0; + pOpt->useUnicodeClipboard = 0; + pOpt->alwaysMacro = 0; + pOpt->spellCheckEnabled = 1; + pOpt->autoNonVnRestore = 0; +} + +UnikeyInputMethod::UnikeyInputMethod() + : sharedMem_(std::make_unique()) { + SetupUnikeyEngine(); + sharedMem_->input.init(); + sharedMem_->macStore.init(); + sharedMem_->vietKey = true; + sharedMem_->usrKeyMapLoaded = false; + setInputMethod(UkTelex); + setOutputCharset(CONV_CHARSET_XUTF8); + CreateDefaultUnikeyOptions(&sharedMem_->options); +} + +//-------------------------------------------- +void UnikeyInputMethod::setInputMethod(UkInputMethod im) { + if (im == UkTelex || im == UkVni || im == UkSimpleTelex || + im == UkSimpleTelex2 || im == UkViqr || im == UkMsVi) { + sharedMem_->input.setIM(im); + } else if (im == UkUsrIM && sharedMem_->usrKeyMapLoaded) { + // cout << "Switched to user mode\n"; //DEBUG + sharedMem_->input.setIM(sharedMem_->usrKeyMap); + } + emit(); + // cout << "IM changed to: " << im << endl; //DEBUG +} + +void UnikeyInputMethod::setOutputCharset(int charset) { + sharedMem_->charsetId = charset; + emit(); +} + +//-------------------------------------------- +void UnikeyInputMethod::setOptions(UnikeyOptions *pOpt) { + sharedMem_->options.freeMarking = pOpt->freeMarking; + sharedMem_->options.modernStyle = pOpt->modernStyle; + sharedMem_->options.macroEnabled = pOpt->macroEnabled; + sharedMem_->options.useUnicodeClipboard = pOpt->useUnicodeClipboard; + sharedMem_->options.alwaysMacro = pOpt->alwaysMacro; + sharedMem_->options.spellCheckEnabled = pOpt->spellCheckEnabled; + sharedMem_->options.autoNonVnRestore = pOpt->autoNonVnRestore; +} + +//-------------------------------------------- +void UnikeyInputContext::setCapsState(int shiftPressed, int CapsLockOn) { + // UnikeyCapsAll = (shiftPressed && !CapsLockOn) || (!shiftPressed && + // CapsLockOn); + capsLockOn_ = CapsLockOn; + shiftPressed_ = shiftPressed; +} + +//-------------------------------------------- +UnikeyInputContext::UnikeyInputContext(UnikeyInputMethod *im) { + conn_ = + im->connect([this]() { engine_.reset(); }); + engine_.setCtrlInfo(im->sharedMem()); + engine_.setCheckKbCaseFunc([this](int *pShiftPressed, int *pCapsLockOn) { + *pShiftPressed = shiftPressed_; + *pCapsLockOn = capsLockOn_; + }); +} + +//-------------------------------------------- +UnikeyInputContext::~UnikeyInputContext() {} + +//-------------------------------------------- +void UnikeyInputContext::filter(unsigned int ch) { + bufChars_ = sizeof(buf_); + engine_.process(ch, backspaces_, buf_, bufChars_, output_); +} + +//-------------------------------------------- +void UnikeyInputContext::putChar(unsigned int ch) { + engine_.pass(ch); + bufChars_ = 0; + backspaces_ = 0; +} + +//-------------------------------------------- +void UnikeyInputContext::rebuildChar(VnLexiName ch) { + bufChars_ = sizeof(buf_); + engine_.rebuildChar(ch, backspaces_, buf_, bufChars_); +} + +//-------------------------------------------- +void UnikeyInputContext::resetBuf() { engine_.reset(); } + +//-------------------------------------------- +void UnikeyInputContext::backspacePress() { + bufChars_ = sizeof(buf_); + engine_.processBackspace(backspaces_, buf_, bufChars_, output_); + // printf("Backspaces: %d\n",UnikeyBackspaces); +} + +//-------------------------------------------- +void UnikeyInputContext::restoreKeyStrokes() { + bufChars_ = sizeof(buf_); + engine_.restoreKeyStrokes(backspaces_, buf_, bufChars_, output_); +} + +bool UnikeyInputContext::isAtWordBeginning() const { + return engine_.atWordBeginning(); +} diff --git a/unikey/core/unikeyinputcontext.h b/unikey/core/unikeyinputcontext.h new file mode 100644 index 00000000..bb9f02da --- /dev/null +++ b/unikey/core/unikeyinputcontext.h @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2018-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _UNIKEY_UNIKEYINPUTCONTEXT_H_ +#define _UNIKEY_UNIKEYINPUTCONTEXT_H_ + +#include "keycons.h" +#include "ukengine.h" +#include +#include + +class UnikeyInputMethod : public fcitx::ConnectableObject { +public: + UnikeyInputMethod(); + + // set input method + // im: TELEX_INPUT, VNI_INPUT, VIQR_INPUT, VIQR_STAR_INPUT + void setInputMethod(UkInputMethod im); + // set output format + void setOutputCharset(int charset); + + // set extra options + void setOptions(UnikeyOptions *pOpt); + + //-------------------------------------------- + int loadMacroTable(const char *fileName) { + return sharedMem_->macStore.loadFromFile(fileName); + } + + UkSharedMem *sharedMem() { return sharedMem_.get(); } + + FCITX_DECLARE_SIGNAL(UnikeyInputMethod, Reset, void()); + +private: + FCITX_DEFINE_SIGNAL(UnikeyInputMethod, Reset); + std::unique_ptr sharedMem_; +}; + +class UnikeyInputContext { +public: + UnikeyInputContext(UnikeyInputMethod *im); + ~UnikeyInputContext(); + + // call this to reset Unikey's state when focus, context is changed or + // some control key is pressed + void resetBuf(); + + // main handler, call every time a character input is received + void filter(unsigned int ch); + void putChar(unsigned int ch); // put new char without filtering + + // call to rebuild preedit from surrounding char + void rebuildChar(VnLexiName ch); + + // call this before UnikeyFilter for correctly processing some TELEX + // shortcuts + void setCapsState(int shiftPressed, int CapsLockOn); + + // call this when backspace is pressed + void backspacePress(); + + // call this to restore to original key strokes + void restoreKeyStrokes(); + + bool isAtWordBeginning() const; + + int backspaces() const { return backspaces_; } + int bufChars() const { return bufChars_; } + const unsigned char *buf() const { return buf_; } + +private: + fcitx::ScopedConnection conn_; + + unsigned char buf_[1024]; + int backspaces_ = 0; + int bufChars_; + UkOutputType output_; + UkEngine engine_; + + int capsLockOn_ = 0; + int shiftPressed_ = 0; +}; + +#endif // _UNIKEY_UNIKEYINPUTCONTEXT_H_ diff --git a/unikey/core/usrkeymap.cpp b/unikey/core/usrkeymap.cpp new file mode 100644 index 00000000..03e8b222 --- /dev/null +++ b/unikey/core/usrkeymap.cpp @@ -0,0 +1,180 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "usrkeymap.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +constexpr char OPT_COMMENT_CHAR = ';'; + +struct UkEventLabelPair { + char label[32]; + int ev; +}; + +const char *UkKeyMapHeader = "; This is UniKey user-defined key mapping file, " + "generated from UniKey (Fcitx 5)\n\n"; + +constexpr UkEventLabelPair UkEvLabelList[] = { + {"Tone0", vneTone0}, {"Tone1", vneTone1}, + {"Tone2", vneTone2}, {"Tone3", vneTone3}, + {"Tone4", vneTone4}, {"Tone5", vneTone5}, + {"Roof-All", vneRoofAll}, {"Roof-A", vneRoof_a}, + {"Roof-E", vneRoof_e}, {"Roof-O", vneRoof_o}, + {"Hook-Bowl", vneHookAll}, {"Hook-UO", vneHook_uo}, + {"Hook-U", vneHook_u}, {"Hook-O", vneHook_o}, + {"Bowl", vneBowl}, {"D-Mark", vneDd}, + {"Telex-W", vne_telex_w}, {"Escape", vneEscChar}, + {"DD", vneCount + vnl_DD}, {"dd", vneCount + vnl_dd}, + {"A^", vneCount + vnl_Ar}, {"a^", vneCount + vnl_ar}, + {"A(", vneCount + vnl_Ab}, {"a(", vneCount + vnl_ab}, + {"E^", vneCount + vnl_Er}, {"e^", vneCount + vnl_er}, + {"O^", vneCount + vnl_Or}, {"o^", vneCount + vnl_or}, + {"O+", vneCount + vnl_Oh}, {"o+", vneCount + vnl_oh}, + {"U+", vneCount + vnl_Uh}, {"u+", vneCount + vnl_uh}}; + +constexpr auto UkEvLabelCount = FCITX_ARRAY_SIZE(UkEvLabelList); + +//------------------------------------------- +void initKeyMap(int keyMap[256]) { + unsigned int c; + for (c = 0; c < 256; c++) + keyMap[c] = vneNormal; +} + +int getLabelIndex(int event) { + for (size_t i = 0; i < UkEvLabelCount; i++) { + if (UkEvLabelList[i].ev == event) + return i; + } + return -1; +} + +} // namespace + +//-------------------------------------------------- +static bool parseNameValue(std::string_view line, std::string_view *name, + std::string_view *value) { + if (line.empty()) { + return false; + } + + // get rid of comment + auto pos = line.find(OPT_COMMENT_CHAR); + if (pos != std::string::npos) { + line = line.substr(0, pos); + } + if (line.empty()) { + return false; + } + + pos = line.find('='); + if (pos == std::string::npos) { + return false; + } + auto k = fcitx::stringutils::trimView(line.substr(0, pos)); + auto v = fcitx::stringutils::trimView(line.substr(pos + 1)); + if (k.empty() || v.empty()) { + return false; + } + + *name = k; + *value = v; + return true; +} + +//----------------------------------------------------- +DllExport void UkLoadKeyMap(int fd, int keyMap[256]) { + std::vector orderMap = UkLoadKeyOrderMap(fd); + initKeyMap(keyMap); + for (const auto &item : orderMap) { + keyMap[item.key] = item.action; + if (item.action < vneCount) { + keyMap[tolower(item.key)] = item.action; + } + } +} + +//------------------------------------------------------------------ +DllExport std::vector UkLoadKeyOrderMap(int fd) { + size_t lineCount = 0; + int keyMap[256]; + + initKeyMap(keyMap); + + std::vector pMap; + fcitx::IFDStreamBuf buf(fd); + std::istream in(&buf); + std::string line; + while (std::getline(in, line)) { + lineCount++; + auto text = fcitx::stringutils::trimView(line); + if (text.empty()) { + continue; + } + std::string_view name, value; + if (parseNameValue(text, &name, &value)) { + if (name.size() != 1) { + FCITX_ERROR() << "Error in user key layout, line " << lineCount + << ": key name is not a single character"; + continue; + } + size_t i = 0; + for (; i < UkEvLabelCount; i++) { + if (UkEvLabelList[i].label == value) { + break; + } + } + if (i == UkEvLabelCount) { + FCITX_ERROR() << "Error in user key layout, line " << lineCount + << ": command not found"; + continue; + } + + auto c = static_cast(name[0]); + if (keyMap[c] != vneNormal) { + // already assigned, don't accept this map + break; + } + // cout << "key: " << c << " value: " << + // UkEvLabelList[i].ev << endl; //DEBUG + keyMap[c] = UkEvLabelList[i].ev; + UkKeyMapping newPair; + newPair.action = UkEvLabelList[i].ev; + if (keyMap[c] < vneCount) { + newPair.key = toupper(c); + keyMap[toupper(c)] = UkEvLabelList[i].ev; + } else { + newPair.key = c; + } + pMap.push_back(newPair); + } + } + return pMap; +} + +DllExport void UkStoreKeyOrderMap(FILE *f, + const std::vector &pMap) { + int labelIndex; + + fputs(UkKeyMapHeader, f); + for (const auto &item : pMap) { + labelIndex = getLabelIndex(item.action); + if (labelIndex != -1) { + fprintf(f, "%c = %s\n", item.key, UkEvLabelList[labelIndex].label); + } + } +} diff --git a/unikey/core/usrkeymap.h b/unikey/core/usrkeymap.h new file mode 100644 index 00000000..76098527 --- /dev/null +++ b/unikey/core/usrkeymap.h @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef __UNIKEY_USER_KEY_MAP_H +#define __UNIKEY_USER_KEY_MAP_H + +#include "inputproc.h" +#include +#include + +DllInterface void UkLoadKeyMap(int fd, int keyMap[256]); +DllInterface std::vector UkLoadKeyOrderMap(int fd); +DllInterface void UkStoreKeyOrderMap(FILE *f, + const std::vector &pMap); + +#endif diff --git a/unikey/core/vnconv.h b/unikey/core/vnconv.h new file mode 100644 index 00000000..f51a98a5 --- /dev/null +++ b/unikey/core/vnconv.h @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 1998-2002 Pham Kim Long + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __VN_CONVERT_H +#define __VN_CONVERT_H + +#if defined(_WIN32) +#if defined(UNIKEYHOOK) +#define DllInterface __declspec(dllexport) +#else +#define DllInterface __declspec(dllimport) +#endif +#define DllExport __declspec(dllexport) +#define DllImport __declspec(dllimport) +#else +#define DllInterface // not used +#define DllExport +#define DllImport +#endif + +#define CONV_CHARSET_UNICODE 0 +#define CONV_CHARSET_UNIUTF8 1 +#define CONV_CHARSET_UNIREF 2 //&#D; +#define CONV_CHARSET_UNIREF_HEX 3 +#define CONV_CHARSET_UNIDECOMPOSED 4 +#define CONV_CHARSET_WINCP1258 5 +#define CONV_CHARSET_UNI_CSTRING 6 +#define CONV_CHARSET_VNSTANDARD 7 + +#define CONV_CHARSET_VIQR 10 +#define CONV_CHARSET_UTF8VIQR 11 +#define CONV_CHARSET_XUTF8 12 + +#define CONV_CHARSET_TCVN3 20 +#define CONV_CHARSET_VPS 21 +#define CONV_CHARSET_VISCII 22 +#define CONV_CHARSET_BKHCM1 23 +#define CONV_CHARSET_VIETWAREF 24 +#define CONV_CHARSET_ISC 25 + +#define CONV_CHARSET_VNIWIN 40 +#define CONV_CHARSET_BKHCM2 41 +#define CONV_CHARSET_VIETWAREX 42 +#define CONV_CHARSET_VNIMAC 43 + +#define CONV_TOTAL_SINGLE_CHARSETS 6 +#define CONV_TOTAL_DOUBLE_CHARSETS 4 + +#define IS_SINGLE_BYTE_CHARSET(x) \ + (x >= CONV_CHARSET_TCVN3 && \ + x < CONV_CHARSET_TCVN3 + CONV_TOTAL_SINGLE_CHARSETS) +#define IS_DOUBLE_BYTE_CHARSET(x) \ + (x >= CONV_CHARSET_VNIWIN && \ + x < CONV_CHARSET_VNIWIN + CONV_TOTAL_DOUBLE_CHARSETS) + +typedef unsigned char UKBYTE; + +#if defined(__cplusplus) +extern "C" { +#endif +DllInterface int VnConvert(int inCharset, int outCharset, UKBYTE *input, + UKBYTE *output, int *pInLen, int *pMaxOutLen); + +DllInterface int VnFileConvert(int inCharset, int outCharset, + const char *inFile, const char *outFile); + +#if defined(__cplusplus) +} +#endif + +DllInterface const char *VnConvErrMsg(int errCode); + +enum VnConvError { + VNCONV_NO_ERROR, + VNCONV_UNKNOWN_ERROR, + VNCONV_INVALID_CHARSET, + VNCONV_ERR_INPUT_FILE, + VNCONV_ERR_OUTPUT_FILE, + VNCONV_OUT_OF_MEMORY, + VNCONV_ERR_WRITING, + VNCONV_LAST_ERROR +}; + +typedef struct _CharsetNameId CharsetNameId; + +struct _CharsetNameId { + const char *name; + int id; +}; + +typedef struct _VnConvOptions VnConvOptions; + +struct _VnConvOptions { + int viqrMixed; + int viqrEsc; + int toUpper; + int toLower; + int removeTone; + int smartViqr; +}; + +DllInterface void VnConvSetOptions(VnConvOptions *pOptions); +DllInterface void VnConvGetOptions(VnConvOptions *pOptions); +DllInterface void VnConvResetOptions(VnConvOptions *pOptions); + +#endif diff --git a/unikey/core/vnlexi.h b/unikey/core/vnlexi.h new file mode 100644 index 00000000..151fd1d8 --- /dev/null +++ b/unikey/core/vnlexi.h @@ -0,0 +1,310 @@ +/* + * SPDX-FileCopyrightText: 2000-2005 Pham Kim Long + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#ifndef __VN_LEXI_H +#define __VN_LEXI_H + +enum VnLexiName { + vnl_nonVnChar = -1, + vnl_A, + vnl_a, + vnl_A1, + vnl_a1, + vnl_A2, + vnl_a2, + vnl_A3, + vnl_a3, + vnl_A4, + vnl_a4, + vnl_A5, + vnl_a5, + vnl_Ar, + vnl_ar, + vnl_Ar1, + vnl_ar1, + vnl_Ar2, + vnl_ar2, + vnl_Ar3, + vnl_ar3, + vnl_Ar4, + vnl_ar4, + vnl_Ar5, + vnl_ar5, + vnl_Ab, + vnl_ab, + vnl_Ab1, + vnl_ab1, + vnl_Ab2, + vnl_ab2, + vnl_Ab3, + vnl_ab3, + vnl_Ab4, + vnl_ab4, + vnl_Ab5, + vnl_ab5, + vnl_B, + vnl_b, + vnl_C, + vnl_c, + vnl_D, + vnl_d, + vnl_DD, + vnl_dd, + vnl_E, + vnl_e, + vnl_E1, + vnl_e1, + vnl_E2, + vnl_e2, + vnl_E3, + vnl_e3, + vnl_E4, + vnl_e4, + vnl_E5, + vnl_e5, + vnl_Er, + vnl_er, + vnl_Er1, + vnl_er1, + vnl_Er2, + vnl_er2, + vnl_Er3, + vnl_er3, + vnl_Er4, + vnl_er4, + vnl_Er5, + vnl_er5, + vnl_F, + vnl_f, + vnl_G, + vnl_g, + vnl_H, + vnl_h, + vnl_I, + vnl_i, + vnl_I1, + vnl_i1, + vnl_I2, + vnl_i2, + vnl_I3, + vnl_i3, + vnl_I4, + vnl_i4, + vnl_I5, + vnl_i5, + vnl_J, + vnl_j, + vnl_K, + vnl_k, + vnl_L, + vnl_l, + vnl_M, + vnl_m, + vnl_N, + vnl_n, + vnl_O, + vnl_o, + vnl_O1, + vnl_o1, + vnl_O2, + vnl_o2, + vnl_O3, + vnl_o3, + vnl_O4, + vnl_o4, + vnl_O5, + vnl_o5, + vnl_Or, + vnl_or, + vnl_Or1, + vnl_or1, + vnl_Or2, + vnl_or2, + vnl_Or3, + vnl_or3, + vnl_Or4, + vnl_or4, + vnl_Or5, + vnl_or5, + vnl_Oh, + vnl_oh, + vnl_Oh1, + vnl_oh1, + vnl_Oh2, + vnl_oh2, + vnl_Oh3, + vnl_oh3, + vnl_Oh4, + vnl_oh4, + vnl_Oh5, + vnl_oh5, + vnl_P, + vnl_p, + vnl_Q, + vnl_q, + vnl_R, + vnl_r, + vnl_S, + vnl_s, + vnl_T, + vnl_t, + vnl_U, + vnl_u, + vnl_U1, + vnl_u1, + vnl_U2, + vnl_u2, + vnl_U3, + vnl_u3, + vnl_U4, + vnl_u4, + vnl_U5, + vnl_u5, + vnl_Uh, + vnl_uh, + vnl_Uh1, + vnl_uh1, + vnl_Uh2, + vnl_uh2, + vnl_Uh3, + vnl_uh3, + vnl_Uh4, + vnl_uh4, + vnl_Uh5, + vnl_uh5, + vnl_V, + vnl_v, + vnl_W, + vnl_w, + vnl_X, + vnl_x, + vnl_Y, + vnl_y, + vnl_Y1, + vnl_y1, + vnl_Y2, + vnl_y2, + vnl_Y3, + vnl_y3, + vnl_Y4, + vnl_y4, + vnl_Y5, + vnl_y5, + vnl_Z, + vnl_z, + + vnl_lastChar, +}; + +enum VowelSeq { + vs_nil = -1, + vs_a, + vs_ar, + vs_ab, + vs_e, + vs_er, + vs_i, + vs_o, + vs_or, + vs_oh, + vs_u, + vs_uh, + vs_y, + vs_ai, + vs_ao, + vs_au, + vs_ay, + vs_aru, + vs_ary, + vs_eo, + vs_eu, + vs_eru, + vs_ia, + vs_ie, + vs_ier, + vs_iu, + vs_oa, + vs_oab, + vs_oe, + vs_oi, + vs_ori, + vs_ohi, + vs_ua, + vs_uar, + vs_ue, + vs_uer, + vs_ui, + vs_uo, + vs_uor, + vs_uoh, + vs_uu, + vs_uy, + vs_uha, + vs_uhi, + vs_uho, + vs_uhoh, + vs_uhu, + vs_ye, + vs_yer, + vs_ieu, + vs_ieru, + vs_oai, + vs_oay, + vs_oeo, + vs_uay, + vs_uary, + vs_uoi, + vs_uou, + vs_uori, + vs_uohi, + vs_uohu, + vs_uya, + vs_uye, + vs_uyer, + vs_uyu, + vs_uhoi, + vs_uhou, + vs_uhohi, + vs_uhohu, + vs_yeu, + vs_yeru +}; + +enum ConSeq { + cs_nil = -1, + cs_b, + cs_c, + cs_ch, + cs_d, + cs_dd, + cs_dz, + cs_g, + cs_gh, + cs_gi, + cs_gin, + cs_h, + cs_k, + cs_kh, + cs_l, + cs_m, + cs_n, + cs_ng, + cs_ngh, + cs_nh, + cs_p, + cs_ph, + cs_q, + cs_qu, + cs_r, + cs_s, + cs_t, + cs_th, + cs_tr, + cs_v, + cs_x +}; + +#endif From 855c5b0eacc1b689a0f6edc0345c5616211758bd Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 7 May 2026 07:21:00 +0700 Subject: [PATCH 11/42] test Signed-off-by: Zebra2711 --- server/lotus-server.cpp | 38 +++++++++++++++++++++++++++++--------- src/lotus-state.cpp | 25 ++++++++++++------------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/server/lotus-server.cpp b/server/lotus-server.cpp index df51ea57..0b2d61ef 100644 --- a/server/lotus-server.cpp +++ b/server/lotus-server.cpp @@ -75,12 +75,14 @@ void UinputDevice::send_backspace() { struct input_event ev[4]{}; ev[0].type = EV_KEY; ev[0].code = KEY_BACKSPACE; - ev[0].value = 1; // Press - // Zero-initialize ev[1] via {} set this event to SYN_REPORT + ev[0].value = 1; + ev[1].type = EV_SYN; + ev[1].code = SYN_REPORT; ev[2].type = EV_KEY; ev[2].code = KEY_BACKSPACE; - ev[2].value = 0; // Release - // Zero-initialize ev[3] via {} set this event to SYN_REPORT + ev[2].value = 0; + ev[3].type = EV_SYN; + ev[3].code = SYN_REPORT; write(guard_.get(), ev, sizeof(ev)); } @@ -110,8 +112,21 @@ void signal_handler(int sig) { } std::string get_current_username() { - struct passwd* pw = getpwuid(getuid()); - return (pw != nullptr) ? pw->pw_name : "unknown"; + struct passwd pwd{}; + struct passwd* result = nullptr; + long buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (buf_size == -1) { + buf_size = 16384; + } + std::vector buf(buf_size); + std::string username; + int res = getpwuid_r(getuid(), &pwd, buf.data(), buf_size, &result); + if (res == 0 && result != nullptr) { + username = result->pw_name; + } else { + username = "unknown"; + } + return username; } uid_t get_uid_for_user(const std::string& username) { @@ -251,6 +266,7 @@ int main(int argc, char* argv[]) { sigaction(SIGTERM, &sa, nullptr); sigaction(SIGINT, &sa, nullptr); + int64_t last_bs_ms = 0; while (g_running.load(std::memory_order_acquire)) { int poll_timeout = (pending_backspaces > 0) ? 1 : -1; int ret = poll(fds.data(), fds.size(), poll_timeout); @@ -262,10 +278,14 @@ int main(int argc, char* argv[]) { break; } - if (ret == 0) { - if (pending_backspaces > 0) { + if (pending_backspaces > 0) { + struct timespec ts{}; + clock_gettime(CLOCK_MONOTONIC, &ts); + int64_t now_ms = static_cast(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000; + if (now_ms - last_bs_ms >= 1) { uinput.send_backspace(); --pending_backspaces; + last_bs_ms = now_ms; } } @@ -320,7 +340,7 @@ int main(int argc, char* argv[]) { LotusLogger::instance().warn("Keyboard client disconnected or connection error"); kb_client_fd.reset(-1); fds[KB_CLIENT_INDEX].fd = -1; - } else { + } else if (count > 0) { pending_backspaces += count - 1; uinput.send_backspace(); } diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 96c9e8cf..164820f5 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -461,19 +461,18 @@ namespace fcitx { is_deleting_.store(false); replacement_start_ms_.store(0, std::memory_order_release); replacement_thread_id_.store(0, std::memory_order_release); - std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); - // Validate surr cursor pos should match realtextLen after all BS applied - if (waitAck_) { - const auto& surr = ic_->surroundingText(); - if (!surr.isValid() || surr.cursor() != realtextLen.load(std::memory_order_acquire)) { - // Retry x5 (1 ms each), khi can (chromium,electron,...) - for (int retry = 0; retry < 5; ++retry) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - const auto& surr2 = ic_->surroundingText(); - if (surr2.isValid() && surr2.cursor() == realtextLen.load(std::memory_order_acquire)) { - break; - } - } + int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); + int64_t wait_ms = static_cast(sleepTime) - elapsed_ms; + if (wait_ms > 0) + std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); + { + const unsigned int expected_cursor = static_cast(realtextLen.load(std::memory_order_acquire)); + const int max_retries = waitAck_ ? 15 : 8; + for (int retry = 0; retry < max_retries; ++retry) { + const auto& surr = ic_->surroundingText(); + if (surr.isValid() && surr.cursor() == expected_cursor) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } ic_->commitString(pending_commit_string_); From f4e852dabab728aa8c115b51f5e52833bd61e85c Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 8 May 2026 01:11:29 +0700 Subject: [PATCH 12/42] eee --- src/app_quirks.h | 2 +- src/lotus-config.h | 5 +-- src/lotus-engine.cpp | 72 ++++++++++++++++++++++++++++++++------------ src/lotus-engine.h | 1 + src/lotus-state.cpp | 66 ++++++++++++++++++++++++++++++++-------- src/lotus-state.h | 3 ++ 6 files changed, 115 insertions(+), 34 deletions(-) diff --git a/src/app_quirks.h b/src/app_quirks.h index 30d6ac1f..45cd69b2 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -27,4 +27,4 @@ inline constexpr std::array ack_apps = {"chrome", "chromiu * @brief List of application names have goood support surrowding text * */ -inline constexpr std::array surrtp_apps = {"soffice"}; +inline constexpr std::array surrtp_apps = {"soffice", "mullvad", "waterfox", "librewolf"}; diff --git a/src/lotus-config.h b/src/lotus-config.h index 68a2cc7d..648e3520 100644 --- a/src/lotus-config.h +++ b/src/lotus-config.h @@ -218,8 +218,9 @@ namespace fcitx { this, "InputMethod", _("Input Method"), "Telex", InputMethodConstrain(&inputMethod), {}, InputMethodAnnotation()}; OptionWithAnnotation outputCharset{this, "OutputCharset", _("Output Charset"), "Unicode", {}, {}, StringListAnnotation()}; Option spellCheck{this, "SpellCheck", _("Enable Spell Check"), true}; Option enableMacro{this, "EnableMacro", _("Enable Macro"), true}; - Option capitalizeMacro{this, "CapitalizeMacro", _("Capitalize Macro"), true}; Option autoCapitalizeAfterPunctuation{ - this, "AutoCapitalizeAfterPunctuation", _("Auto capitalize after sentence-ending punctuation (. ! ? Enter) (experimental)"), false}; + Option autoSaveNewAppRules{this, "autoSaveNewAppRules", _("Auto Save"), false}; Option capitalizeMacro{this, "CapitalizeMacro", _("Capitalize Macro"), true}; + Option autoCapitalizeAfterPunctuation{this, "AutoCapitalizeAfterPunctuation", _("Auto capitalize after sentence-ending punctuation (. ! ? Enter) (experimental)"), + false}; Option doubleSpaceToPeriod{this, "DoubleSpaceToPeriod", _("Double Space to Period (experimental)"), false}; Option w2u{this, "W2U", _("Type w to Produce ư"), true}; Option autoNonVnRestore{this, "AutoNonVnRestore", _("Auto Restore Keys With Invalid Words"), true}; Option modernStyle{this, "ModernStyle", _("Use oà, uý (Instead Of òa, úy)"), true}; diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 4d6e444d..6cabedf9 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -80,13 +80,17 @@ namespace fcitx { static inline std::vector convertToStringList(char** list) { std::vector result; - if (list != nullptr) { - for (size_t i = 0; list[i] != nullptr; ++i) { //NOLINT - result.emplace_back(list[i]); //NOLINT - free(list[i]); //NOLINT - } - free(list); //NOLINT - } + if (list == nullptr) + return result; + size_t count = 0; + while (list[count] != nullptr) + ++count; //NOLINT + result.reserve(count); + for (size_t i = 0; i < count; ++i) + result.emplace_back(list[i]); //NOLINT + for (size_t i = 0; i < count; ++i) + free(list[i]); //NOLINT + free(list); //NOLINT return result; } @@ -407,11 +411,13 @@ namespace fcitx { } else if (surrvalid && !state->oldPreBuffer_.empty() && (now_ms() - state->lastDeactivateTime_) < 100) { state->clearAllBuffers(); } - is_deleting_.store(false); + if (!state->isReplacing()) + is_deleting_.store(false); needEngineReset.store(false); if (targetMode == LotusMode::Emoji) { state->updateEmojiPreedit(); } else { + LOTUS_INFO("inputPanel reset"); ic->inputPanel().reset(); ic->updateUserInterface(UserInterfaceComponent::InputPanel); ic->updatePreedit(); @@ -426,6 +432,7 @@ namespace fcitx { if (isSelectingAppMode_ && g_mouse_clicked.load(std::memory_order_acquire)) { closeAppModeMenu(); + LOTUS_INFO("reset inputPanel"); ic->inputPanel().reset(); ic->updateUserInterface(UserInterfaceComponent::InputPanel); auto* state = ic->propertyFor(&factory_); @@ -605,13 +612,21 @@ namespace fcitx { } void LotusEngine::reset(const InputMethodEntry& /*entry*/, InputContextEvent& event) { - LOTUS_INFO("Reset engine"); auto* state = event.inputContext()->propertyFor(&factory_); + if (is_deleting_.load(std::memory_order_acquire)) + return; if (!state->isEmptyHistory() && event.type() != EventType::InputContextFocusOut) { + int64_t now = now_ms(); + if (now - state->lastSkippedResetMs_ >= 500) { + LOTUS_INFO("Reset engine: skipping (has history)"); + state->lastSkippedResetMs_ = now; + } return; } - + state->lastSkippedResetMs_ = 0; + LOTUS_INFO("Reset engine"); if (event.type() == EventType::InputContextFocusOut || event.type() == EventType::InputContextReset) { + LOTUS_INFO("reset stage"); state->reset(event.type() == EventType::InputContextFocusOut); } } @@ -631,7 +646,8 @@ namespace fcitx { if (surrvalid && state->oldPreBuffer_.empty()) state->clearAllBuffers(); } - is_deleting_.store(false); + if (!state->isReplacing()) + is_deleting_.store(false); needEngineReset.store(false); ic->inputPanel().reset(); ic->updateUserInterface(UserInterfaceComponent::InputPanel); @@ -746,19 +762,40 @@ namespace fcitx { file.close(); } - LotusMode LotusEngine::getAppRule(const std::string& appName) const { + LotusMode LotusEngine::getAppRule(const std::string& appName) { std::lock_guard lock(appRulesMutex_); + auto it = appRules_.find(appName); if (it != appRules_.end()) { return it->second; } - return modeStringToEnum(config_.mode.value()); + + const auto globalMode = modeStringToEnum(config_.mode.value()); + + // auto save new app rule from global mode + if (config_.autoSaveNewAppRules.value() && false) { + auto rules = *appRulesTables_.rules; + + lotusAppRule newRule; + newRule.app.setValue(appName); + newRule.mode.setValue(static_cast(globalMode)); + + rules.push_back(std::move(newRule)); + + appRules_[appName] = globalMode; + appRulesTables_.rules.setValue(std::move(rules)); + + saveAppRules(); + } + + return globalMode; } void LotusEngine::setAppRule(const std::string& appName, LotusMode mode) { - auto rules = *appRulesTables_.rules; + std::lock_guard lock(appRulesMutex_); + auto rules = *appRulesTables_.rules; - bool found = false; + bool found = false; for (auto& rule : rules) { if (*rule.app == appName) { rule.mode.setValue(static_cast(mode)); @@ -774,10 +811,7 @@ namespace fcitx { rules.push_back(std::move(newRule)); } - { - std::lock_guard lock(appRulesMutex_); - appRules_[appName] = mode; - } + appRules_[appName] = mode; appRulesTables_.rules.setValue(std::move(rules)); } diff --git a/src/lotus-engine.h b/src/lotus-engine.h index 8f10cf22..b6727947 100644 --- a/src/lotus-engine.h +++ b/src/lotus-engine.h @@ -187,6 +187,7 @@ namespace fcitx { } return *emojiLoader_; } + LotusMode getAppRule(const std::string& appName); private: Instance* instance_; diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 164820f5..c334e29c 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -31,8 +31,42 @@ namespace fcitx { constexpr int MAX_SCAN_LENGTH = 15; static inline bool isWordBreak(uint32_t ucs4) { - // Space, tab, newline, carriage return, null, or punctuation/symbols (: ; < = > ? @) - return ucs4 == ' ' || ucs4 == '\t' || ucs4 == '\n' || ucs4 == '\r' || ucs4 == 0 || (ucs4 >= 58 && ucs4 <= 64); + if (__builtin_expect(ucs4 > 64, 1)) + return false; + if (__builtin_expect(ucs4 == 64, 0)) + return true; // '@' + // btq: single-cycle bit-test (Linux kernel bitmap technique); replaces 6-branch chain. + // Bits set: NUL(0) TAB(9) LF(10) CR(13) SPC(32) :;<=>?(58-63) + static constexpr uint64_t kMask = + (1ULL << 0) | (1ULL << 9) | (1ULL << 10) | (1ULL << 13) | (1ULL << 32) | (1ULL << 58) | (1ULL << 59) | (1ULL << 60) | (1ULL << 61) | (1ULL << 62) | (1ULL << 63); + bool r; + asm("btq %1, %2\n\t" + "setc %0" + : "=r"(r) + : "r"((uint64_t)ucs4), "r"(kMask) + : "cc"); + return r; + } + + // Word-at-a-time high-byte scan (glibc / Linux kernel byte-at-a-time.h technique). + // Reads 8 bytes per iteration; testq checks all 8 in one instruction. + static inline bool hasHighByte(const std::string& s) { + static constexpr uint64_t kHi = 0x8080808080808080ULL; + const uint8_t* p = reinterpret_cast(s.data()); + size_t n = s.size(); + bool r = false; + for (; n >= 8 && !r; p += 8, n -= 8) { + uint64_t w; + __builtin_memcpy(&w, p, 8); + asm("testq %1, %2\n\t" + "setne %0" + : "=r"(r) + : "r"(w), "r"(kHi) + : "cc"); + } + for (; n && !r; --n) + r = (*p++ & 0x80) != 0; + return r; } LotusState::LotusState(LotusEngine* engine, InputContext* ic) : engine_(engine), ic_(ic) { @@ -467,7 +501,7 @@ namespace fcitx { std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); { const unsigned int expected_cursor = static_cast(realtextLen.load(std::memory_order_acquire)); - const int max_retries = waitAck_ ? 15 : 8; + const int max_retries = waitAck_ ? 5 : 1; for (int retry = 0; retry < max_retries; ++retry) { const auto& surr = ic_->surroundingText(); if (surr.isValid() && surr.cursor() == expected_cursor) @@ -670,15 +704,17 @@ namespace fcitx { return; } - if (!processed) { - if (checkEmptyPreedit) { - if (!preeditC || (*preeditC.get() == 0)) { - hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); - oldPreBuffer_.clear(); - keyEvent.forward(); - } + // Treat "processed but no effect" as passthrough + bool hasCommit = (commitF && (*commitF.get() != 0)); + bool hasPreedit = (preeditC && (*preeditC.get() != 0)); + + if (!processed || (!hasCommit && !hasPreedit)) { + if (checkEmptyPreedit && !hasPreedit) { + hasHistory_ = false; + ResetEngine(lotusEngine_.handle()); + oldPreBuffer_.clear(); } + keyEvent.forward(); return; } @@ -1007,7 +1043,7 @@ namespace fcitx { if (isBackspace(currentSym)) { if (realtextLen.load(std::memory_order_acquire) > 0) realtextLen.fetch_sub(1, std::memory_order_acq_rel); - if (handleUInputKeyPress(keyEvent, currentSym, (realMode == LotusMode::Smooth) ? 5 : 20)) { + if (handleUInputKeyPress(keyEvent, currentSym, (realMode == LotusMode::Smooth) ? 3 : 10)) { return; } } else { @@ -1167,6 +1203,12 @@ namespace fcitx { bool LotusState::isEmptyHistory() const { return !hasHistory_; } + bool LotusState::isReplacing() const { + return expected_backspaces_ > 0 && current_backspace_count_ < expected_backspaces_; + } + bool LotusState::isX11() const { + return false; //cat /proc//maps | grep -E 'libX11|libxcb' + } /* void LotusState::replayBufferedKeys() { LOTUS_INFO("Starting replay buffered keys"); diff --git a/src/lotus-state.h b/src/lotus-state.h index ebb9849d..37238655 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -83,6 +83,8 @@ namespace fcitx { * @return True if no history. */ bool isEmptyHistory() const; + bool isReplacing() const; + bool isX11() const; friend class EmojiCandidateWord; friend class LotusEngine; @@ -106,6 +108,7 @@ namespace fcitx { bool shouldCapitalize_ = false; bool isPrevPunctuation_ = false; int64_t lastDeactivateTime_ = 0; + int64_t lastSkippedResetMs_ = 0; bool wa_flag = false; bool surrtp = false; From c7d65d8895a62e282271d17db174194962da8659 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 8 May 2026 02:50:22 +0700 Subject: [PATCH 13/42] nice --- CMakeLists.txt | 4 +- bamboo/.gitignore | 7 - bamboo/CMakeLists.txt | 19 -- bamboo/bamboo-c.go | 409 -------------------------------- bamboo/bamboo-core | 1 - bamboo/fcitxbambooengine.go | 441 ----------------------------------- bamboo/go.mod | 7 - bamboo/macrotable.go | 20 -- src/CMakeLists.txt | 21 +- src/lotus-bamboo-backend.cpp | 139 +++++++++++ src/lotus-engine.cpp | 22 ++ src/lotus-input-backend.hpp | 42 ++++ src/lotus-state.cpp | 195 +++++++--------- src/lotus-state.h | 8 +- src/lotus-unikey-backend.cpp | 282 ++++++++++++++++++++++ src/lotus.h | 90 ++++--- unikey/LotusUnikeyEngine.hpp | 6 +- 17 files changed, 644 insertions(+), 1069 deletions(-) delete mode 100644 bamboo/.gitignore delete mode 100644 bamboo/CMakeLists.txt delete mode 100644 bamboo/bamboo-c.go delete mode 160000 bamboo/bamboo-core delete mode 100644 bamboo/fcitxbambooengine.go delete mode 100644 bamboo/go.mod delete mode 100644 bamboo/macrotable.go create mode 100644 src/lotus-bamboo-backend.cpp create mode 100644 src/lotus-input-backend.hpp create mode 100644 src/lotus-unikey-backend.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5acfbb37..56fc13a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,9 +41,11 @@ fcitx5_add_i18n_definition() if(ENABLE_LOTUS_UNIKEY_ENGINE) add_subdirectory(unikey) +else() + add_subdirectory(bamboo) endif() + add_subdirectory(po) -add_subdirectory(bamboo) add_subdirectory(src) add_subdirectory(data) add_subdirectory(server) diff --git a/bamboo/.gitignore b/bamboo/.gitignore deleted file mode 100644 index 87b79b24..00000000 --- a/bamboo/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# CMake build files -CMakeFiles/ -Makefile -cmake_install.cmake -*.a -# Generated header file -bamboo-core.h diff --git a/bamboo/CMakeLists.txt b/bamboo/CMakeLists.txt deleted file mode 100644 index 1d4dfc99..00000000 --- a/bamboo/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ - -file(GLOB BAMBOO_CORE_GO_SRCS bamboo-core/*.go) -file(GLOB BAMBOO_GO_SRCS *.go) - -# Custom command for 'go build -buildmode=c-archive ...' -# to create a library from Go codes. -add_custom_command(OUTPUT bamboo-core.a bamboo-core.h - DEPENDS ${BAMBOO_GO_SRCS} ${BAMBOO_CORE_GO_SRCS} go.mod - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND env go build -buildmode=c-archive - -o "${CMAKE_CURRENT_BINARY_DIR}/bamboo-core.a" - ${BAMBOO_GO_SRCS}) -add_custom_target(bamboo-core DEPENDS bamboo-core.a) -# Add a custom target for the library. -add_library(Bamboo::Core UNKNOWN IMPORTED GLOBAL) -add_dependencies(Bamboo::Core bamboo-core) -set_target_properties(Bamboo::Core PROPERTIES - IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/bamboo-core.a" - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") \ No newline at end of file diff --git a/bamboo/bamboo-c.go b/bamboo/bamboo-c.go deleted file mode 100644 index 0846fe17..00000000 --- a/bamboo/bamboo-c.go +++ /dev/null @@ -1,409 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2022 CSSlayer - * - * SPDX-License-Identifier: LGPL-2.1-or-later - * - */ -package main - -import ( - /* - #include - #include - typedef const char cchar; - typedef struct { - bool autoNonVnRestore; - bool ddFreeStyle; - bool macroEnabled; - bool autoCapitalizeMacro; - bool spellCheckWithDicts; - const char *outputCharset; - bool modernStyle; - bool freeMarking; - bool w2u; - const char *timeFormat; - const char *dateFormat; - } FcitxBambooEngineOption; - */ - "C" - "bamboo-core" - "os/signal" - "runtime/cgo" - "syscall" - "unsafe" -) -import ( - "bufio" - "os" - "runtime" - "runtime/debug" - "sort" - "strings" - "sync/atomic" -) - -// If FCITX_LOTUS_LOCK_OSTHREAD is set (any non-empty value), bind each CGO callback -// goroutine to its OS thread to reduce scheduler/TLS churn at the C↔Go boundary. -var lockOsThreadEnabled uint32 - -func lockOSThreadForCgo() { - if atomic.LoadUint32(&lockOsThreadEnabled) != 0 { - runtime.LockOSThread() - } -} - -//export Init -func Init() { - signal.Ignore(syscall.SIGPIPE) - debug.SetGCPercent(200) - if os.Getenv("FCITX_LOTUS_LOCK_OSTHREAD") != "" { - atomic.StoreUint32(&lockOsThreadEnabled, 1) - } -} - -func enginePullCommitCString(e *FcitxBambooEngine) *C.char { - commitText := e.commitText - e.commitText = "" - if commitText == "" { - return nil - } - encodedText := bamboo.Encode(e.outputCharset, commitText) - if encodedText == "" { - return nil - } - return C.CString(encodedText) -} - -func enginePullPreeditCString(e *FcitxBambooEngine) *C.char { - if e.preeditText == "" { - return nil - } - return C.CString(e.preeditText) -} - -//export EngineProcessKeyEvent -func EngineProcessKeyEvent(engine uintptr, keyVal, state uint32) bool { - lockOSThreadForCgo() - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return false - } - return bambooEngine.preeditProcessKeyEvent(keyVal, state) -} - -//export EngineProcessKeyEventAndPull -// Runs one key event then returns commit and/or preedit in one CGO transition. -// Pass nil for commitOut or preeditOut if that string is not needed. -func EngineProcessKeyEventAndPull(engine uintptr, keyVal, state uint32, commitOut, preeditOut **C.char) bool { - lockOSThreadForCgo() - if commitOut != nil { - *commitOut = nil - } - if preeditOut != nil { - *preeditOut = nil - } - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return false - } - processed := bambooEngine.preeditProcessKeyEvent(keyVal, state) - if commitOut != nil { - *commitOut = enginePullCommitCString(bambooEngine) - } - if preeditOut != nil { - *preeditOut = enginePullPreeditCString(bambooEngine) - } - return processed -} - -//export EnginePullCommitAndPreedit -func EnginePullCommitAndPreedit(engine uintptr, commitOut, preeditOut **C.char) { - lockOSThreadForCgo() - if commitOut != nil { - *commitOut = nil - } - if preeditOut != nil { - *preeditOut = nil - } - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return - } - if commitOut != nil { - *commitOut = enginePullCommitCString(bambooEngine) - } - if preeditOut != nil { - *preeditOut = enginePullPreeditCString(bambooEngine) - } -} - -//export EngineSetRestoreKeyStroke -func EngineSetRestoreKeyStroke(engine uintptr) { - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return - } - bambooEngine.shouldRestoreKeyStrokes = true -} - -//export EnginePullPreedit -func EnginePullPreedit(engine uintptr) *C.char { - lockOSThreadForCgo() - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return nil - } - return enginePullPreeditCString(bambooEngine) -} - -//export EngineCommitPreedit -func EngineCommitPreedit(engine uintptr) { - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return - } - bambooEngine.commitPreeditAndReset(bambooEngine.getPreeditString()) -} - -//export EnginePullCommit -func EnginePullCommit(engine uintptr) *C.char { - lockOSThreadForCgo() - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return nil - } - return enginePullCommitCString(bambooEngine) -} - -//export EngineSetOption -func EngineSetOption(engine uintptr, option *C.FcitxBambooEngineOption) { - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return - } - bambooEngine.autoNonVnRestore = bool(option.autoNonVnRestore) - bambooEngine.ddFreeStyle = bool(option.ddFreeStyle) - bambooEngine.macroEnabled = bool(option.macroEnabled) - bambooEngine.autoCapitalizeMacro = bool(option.autoCapitalizeMacro) - bambooEngine.spellCheckWithDicts = bool(option.spellCheckWithDicts) - bambooEngine.outputCharset = C.GoString(option.outputCharset) - flags := bamboo.EstdFlags - if option.modernStyle { - flags &= ^bamboo.EstdToneStyle - } else { - flags |= bamboo.EstdToneStyle - } - - if option.freeMarking { - flags |= bamboo.EfreeToneMarking - } else { - flags &= ^bamboo.EfreeToneMarking - } - - if bool(option.w2u) { - flags |= bamboo.Ew2uEnabled - } else { - flags &= ^bamboo.Ew2uEnabled - } - bambooEngine.preeditor.SetFlag(flags) - bambooEngine.timeFormat = C.GoString(option.timeFormat) - bambooEngine.dateFormat = C.GoString(option.dateFormat) -} - -//export NewEngine -func NewEngine(name *C.cchar, dictHandle uintptr, tableHandle uintptr) uintptr { - dict, ok := cgo.Handle(dictHandle).Value().(*map[string]bool) - if !ok { - return 0 - } - - table, ok := cgo.Handle(tableHandle).Value().(*MacroTable) - if !ok { - return 0 - } - - imName := C.GoString(name) - - var engine = &FcitxBambooEngine{ - preeditor: bamboo.NewEngine(bamboo.ParseInputMethod(bamboo.InputMethodDefinitions, imName), bamboo.EstdFlags), - macroTable: table, - dictionary: *dict, - autoNonVnRestore: true, - ddFreeStyle: true, - macroEnabled: false, - autoCapitalizeMacro: false, - lastKeyWithShift: false, - spellCheckWithDicts: true, - preeditText: "", - commitText: "", - shouldRestoreKeyStrokes: false, - outputCharset: "Unicode", - timeFormat: "%H:%M", - dateFormat: "%d/%m/%Y", - } - engine.rebuildAppendingKeySet() - return uintptr(cgo.NewHandle(engine)) -} - -//export NewCustomEngine -func NewCustomEngine(definition **C.char, dictHandle uintptr, tableHandle uintptr) uintptr { - dict, ok := cgo.Handle(dictHandle).Value().(*map[string]bool) - if !ok { - return 0 - } - - table, ok := cgo.Handle(tableHandle).Value().(*MacroTable) - if !ok { - return 0 - } - var definitions = map[string]bamboo.InputMethodDefinition{ - "Custom": map[string]string{}, - } - def := (*[1<<20 - 1]*C.char)(unsafe.Pointer(definition)) - maxEntries := 1<<20 - 1 - - i := 0 - for i < maxEntries && def[i] != nil { - definitions["Custom"][C.GoString(def[i])] = C.GoString(def[i+1]) - i += 2 - } - - var engine = &FcitxBambooEngine{ - preeditor: bamboo.NewEngine(bamboo.ParseInputMethod(definitions, "Custom"), bamboo.EstdFlags), - macroTable: table, - dictionary: *dict, - autoNonVnRestore: true, - ddFreeStyle: true, - macroEnabled: false, - autoCapitalizeMacro: false, - lastKeyWithShift: false, - spellCheckWithDicts: false, - preeditText: "", - commitText: "", - shouldRestoreKeyStrokes: false, - outputCharset: "Unicode", - timeFormat: "%H:%M", - dateFormat: "%d/%m/%Y", - } - engine.rebuildAppendingKeySet() - return uintptr(cgo.NewHandle(engine)) -} - -//export NewMacroTable -func NewMacroTable(definition **C.char) uintptr { - var table = &MacroTable{ - mTable: map[string]string{}, - } - def := (*[1<<20 - 1]*C.char)(unsafe.Pointer(definition)) - maxEntries := 1<<20 - 1 - i := 0 - for i < maxEntries && def[i] != nil { - table.mTable[C.GoString(def[i])] = C.GoString(def[i+1]) - i += 2 - } - - return uintptr(cgo.NewHandle(table)) -} - -//export DeleteObject -func DeleteObject(handle uintptr) { - cgo.Handle(handle).Delete() -} - -//export ResetEngine -func ResetEngine(engine uintptr) { - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return - } - bambooEngine.commitPreeditAndReset("") -} - -//export EngineRebuildFromText -func EngineRebuildFromText(engine uintptr, text *C.cchar) { - bambooEngine, ok := cgo.Handle(engine).Value().(*FcitxBambooEngine) - if !ok { - return - } - goText := C.GoString(text) - bambooEngine.preeditor.RebuildEngineFromText(goText) - bambooEngine.preeditText = bambooEngine.getPreeditString() - bambooEngine.commitText = "" -} - -func toCStringArray(strs []string) **C.char { - array := C.malloc(C.size_t(len(strs)+1) * C.size_t(unsafe.Sizeof(uintptr(0)))) - // convert the C array to a Go Array so we can index it - a := (*[1<<20 - 1]*C.char)(array) - - for idx, substring := range strs { - a[idx] = C.CString(substring) - } - a[len(strs)] = nil - return (**C.char)(array) -} - -//export GetCharsetNames -func GetCharsetNames() **C.char { - return toCStringArray(bamboo.GetCharsetNames()) -} - -//export GetInputMethodNames -func GetInputMethodNames() **C.char { - order := map[string]int{ - "Telex": 0, - "VNI": 1, - "Telex 2": 2, - "Telex + VNI": 3, - "Telex + VNI + VIQR": 4, - "VIQR": 5, - "Microsoft layout": 6, - "VNI Bàn phím tiếng Pháp": 7, - } - names := make([]string, len(bamboo.InputMethodDefinitions)) - i := 0 - for imName := range bamboo.InputMethodDefinitions { - names[i] = imName - i++ - } - sort.Slice(names, func(i, j int) bool { - oi, oki := order[names[i]] - oj, okj := order[names[j]] - if oki && okj { - return oi < oj - } - if oki { - return true - } - if okj { - return false - } - return names[i] < names[j] - }) - return toCStringArray(names) -} - -//export NewDictionary -func NewDictionary(fd uintptr) uintptr { - var data = map[string]bool{} - f := os.NewFile(fd, "dict") - if f == nil { - return 0 - } - defer f.Close() - rd := bufio.NewReader(f) - for { - line, _, err := rd.ReadLine() - if err != nil { - break - } - if len(line) == 0 { - continue - } - var tmp = []byte(strings.ToLower(string(line))) - data[string(tmp)] = true - } - return uintptr(cgo.NewHandle(&data)) -} - -func main() {} diff --git a/bamboo/bamboo-core b/bamboo/bamboo-core deleted file mode 160000 index 5f1974ac..00000000 --- a/bamboo/bamboo-core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5f1974ac5eb6a540fdb05887dcd21131137f1605 diff --git a/bamboo/fcitxbambooengine.go b/bamboo/fcitxbambooengine.go deleted file mode 100644 index 77a79660..00000000 --- a/bamboo/fcitxbambooengine.go +++ /dev/null @@ -1,441 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2018 Luong Thanh Lam - * SPDX-FileCopyrightText: 2022-2022 CSSlayer - * - * SPDX-License-Identifier: LGPL-2.1-or-later - * - */ -package main - -import ( - "bamboo-core" - "strings" - "time" - "unicode" - "unicode/utf8" -) - -type FcitxBambooEngine struct { - preeditor bamboo.IEngine - appendingKeySet map[rune]struct{} - macroTable *MacroTable - dictionary map[string]bool - autoNonVnRestore bool - ddFreeStyle bool - macroEnabled bool - autoCapitalizeMacro bool - lastKeyWithShift bool - spellCheckWithDicts bool - preeditText string - commitText string - shouldRestoreKeyStrokes bool - outputCharset string - w2u bool - timeFormat string - dateFormat string -} - -const ( - FcitxShiftMask = 1 << 0 - FcitxLockMask = 1 << 1 - FcitxControlMask = 1 << 2 - FcitxMod1Mask = 1 << 3 - - /* The next few modifiers are used by XKB so we skip to the end. - * Bits 15 - 23 are currently unused. Bit 29 is used internally. - */ - - FcitxForwardMask = 1 << 25 - FcitxIgnoredMask = FcitxForwardMask - - FcitxSuperMask = 1 << 26 - FcitxHyperMask = 1 << 27 - FcitxMetaMask = 1 << 28 -) -const ( - FcitxBackSpace = 0xff08 - FcitxSpace = 0x020 - FcitxTab = 0xff09 -) - -const ( - VnCaseAllSmall uint8 = iota + 1 - VnCaseAllCapital - VnCaseNoChange -) - -func determineMacroCase(str string) uint8 { - var chars = []rune(str) - if unicode.IsLower(chars[0]) { - return VnCaseAllSmall - } else { - for _, c := range chars[1:] { - if unicode.IsLower(c) { - return VnCaseNoChange - } - } - } - return VnCaseAllCapital -} - -var strftimeReplacer = strings.NewReplacer( - "%H", "15", - "%I", "03", - "%M", "04", - "%S", "05", - "%p", "PM", - "%P", "pm", - "%d", "02", - "%m", "01", - "%Y", "2006", - "%y", "06", - "%b", "Jan", - "%B", "January", - "%a", "Mon", - "%A", "Monday", - // Some common variants - "%D", "01/02/06", - "%F", "2006-01-02", - "%T", "15:04:05", - "%R", "15:04", -) - -func (e *FcitxBambooEngine) formatTime(format string) string { - now := time.Now() - if format == "" { - return "" - } - layout := strftimeReplacer.Replace(format) - if layout == "" { - return "" - } - // If layout was not changed (no placeholders found), default to standard format - if layout == format && strings.Contains(format, "%") { - // Fallback to something reasonable if it looks like they tried to use placeholders - return now.Format("15:04:05 02/01/2006") - } - return now.Format(layout) -} - -func (e *FcitxBambooEngine) expandMacro(str string) string { - var macroText = e.macroTable.GetText(str) - - // Replace dynamic placeholders - if e.timeFormat != "" { - macroText = strings.ReplaceAll(macroText, "$TIME", e.formatTime(e.timeFormat)) - } - if e.dateFormat != "" { - macroText = strings.ReplaceAll(macroText, "$DATE", e.formatTime(e.dateFormat)) - } - - if e.autoCapitalizeMacro { - switch determineMacroCase(str) { - case VnCaseAllSmall: - return strings.ToLower(macroText) - case VnCaseAllCapital: - return strings.ToUpper(macroText) - } - } - return macroText -} - -func (e *FcitxBambooEngine) getMacroText() (bool, string) { - if !e.macroEnabled { - return false, "" - } - var text = e.preeditor.GetProcessedString(bamboo.PunctuationMode) - if e.macroTable.HasKey(text) { - return true, e.expandMacro(text) - } - return false, "" -} - -func (e *FcitxBambooEngine) shouldFallbackToEnglish(checkVnRune bool) bool { - if !e.autoNonVnRestore { - return false - } - var vnSeq = e.preeditor.GetProcessedString(bamboo.VietnameseMode | bamboo.LowerCase) - var vnRunes = []rune(vnSeq) - if len(vnRunes) == 0 { - return false - } - if ok, _ := e.getMacroText(); ok { - return false - } - // we want to allow dd even in non-vn sequence, because dd is used a lot in abbreviation - if e.ddFreeStyle && !bamboo.HasAnyVietnameseVower(vnSeq) && - (vnRunes[len(vnRunes)-1] == 'd' || strings.ContainsRune(vnSeq, 'đ')) { - return false - } - if checkVnRune && !bamboo.HasAnyVietnameseRune(vnSeq) { - return false - } - return !e.preeditor.IsValid(false) -} - -func (e *FcitxBambooEngine) getProcessedString(mode bamboo.Mode) string { - return e.preeditor.GetProcessedString(mode) -} - -func (e *FcitxBambooEngine) getRawKeyLen() int { - return len(e.preeditor.GetProcessedString(bamboo.EnglishMode | bamboo.FullText)) -} - -func (e *FcitxBambooEngine) getPreeditString() string { - if e.macroEnabled { - return e.getProcessedString(bamboo.PunctuationMode) - } - if e.shouldFallbackToEnglish(true) { - return e.getProcessedString(bamboo.EnglishMode) - } - return e.getProcessedString(bamboo.VietnameseMode) -} - -func (e *FcitxBambooEngine) updateLastKeyWithShift(keyVal, state uint32) { - if e.preeditor.CanProcessKey(rune(keyVal)) { - e.lastKeyWithShift = state&FcitxShiftMask != 0 - } else { - e.lastKeyWithShift = false - } -} -func (e *FcitxBambooEngine) runeCount() int { - return utf8.RuneCountInString(e.getPreeditString()) -} - -func (e *FcitxBambooEngine) getBambooInputMode() bamboo.Mode { - if e.shouldFallbackToEnglish(false) { - return bamboo.EnglishMode - } - return bamboo.VietnameseMode -} - -func (e *FcitxBambooEngine) rebuildAppendingKeySet() { - keys := e.preeditor.GetInputMethod().AppendingKeys - e.appendingKeySet = make(map[rune]struct{}, len(keys)) - for _, k := range keys { - e.appendingKeySet[k] = struct{}{} - } -} - -func inKeyList(list []rune, key rune) bool { - for _, s := range list { - if s == key { - return true - } - } - return false -} - -func (e *FcitxBambooEngine) toUpper(keyRune rune) rune { - var keyMapping = map[rune]rune{ - '[': '{', - ']': '}', - '{': '[', - '}': ']', - } - - if upperSpecialKey, found := keyMapping[keyRune]; found { - if _, ok := e.appendingKeySet[keyRune]; !ok { - return keyRune - } - keyRune = upperSpecialKey - } - return keyRune -} - -func (e *FcitxBambooEngine) mustFallbackToEnglish() bool { - if !e.autoNonVnRestore { - return false - } - var vnSeq = e.getProcessedString(bamboo.VietnameseMode | bamboo.LowerCase) - var vnRunes = []rune(vnSeq) - if len(vnRunes) == 0 { - return false - } - // we want to allow dd even in non-vn sequence, because dd is used a lot in abbreviation - if e.ddFreeStyle && strings.ContainsRune(vnSeq, 'đ') { - return false - } - if e.spellCheckWithDicts { - return !e.dictionary[vnSeq] - } - return !e.preeditor.IsValid(true) -} - -func getLastRune(s string) rune { - if len(s) == 0 { - return 0 - } - r, _ := utf8.DecodeLastRuneInString(s) - return r -} - -func (e *FcitxBambooEngine) getCommitText(keyVal, state uint32) (string, bool) { - var keyRune = rune(keyVal) - oldText := e.getPreeditString() - // restore key strokes by pressing Shift + Space - if e.shouldRestoreKeyStrokes { - e.shouldRestoreKeyStrokes = false - e.preeditor.RestoreLastWord(!bamboo.HasAnyVietnameseRune(oldText)) - return e.getPreeditString(), false - } - if e.preeditor.CanProcessKey(keyRune) { - if state&FcitxLockMask != 0 { - keyRune = e.toUpper(keyRune) - } - e.preeditor.ProcessKey(keyRune, e.getBambooInputMode()) - if _, ok := e.appendingKeySet[keyRune]; ok { - var newText string - if e.shouldFallbackToEnglish(true) { - newText = e.getProcessedString(bamboo.EnglishMode) - } else { - newText = e.getProcessedString(bamboo.VietnameseMode) - } - if fullSeq := e.preeditor.GetProcessedString(bamboo.VietnameseMode); len(fullSeq) > 0 && getLastRune(fullSeq) == keyRune { - // [[ => [ - var ret = e.getPreeditString() - var lastRune = getLastRune(ret) - var isWordBreakRune = bamboo.IsWordBreakSymbol(lastRune) - // TODO: THIS IS HACKING - if isWordBreakRune { - e.preeditor.RemoveLastChar(false) - e.preeditor.ProcessKey(' ', bamboo.EnglishMode) - } - return ret, isWordBreakRune - } else if l := []rune(newText); len(l) > 0 && keyRune == l[len(l)-1] { - // f] => f] - var isWordBreakRune = bamboo.IsWordBreakSymbol(keyRune) - if isWordBreakRune { - e.preeditor.RemoveLastChar(false) - e.preeditor.ProcessKey(' ', bamboo.EnglishMode) - } - return oldText + string(keyRune), isWordBreakRune - } else { - // ] => o? - return e.getPreeditString(), false - } - } else if e.macroEnabled { - return e.getProcessedString(bamboo.PunctuationMode), false - } else { - return e.getPreeditString(), false - } - } else if bamboo.IsWordBreakSymbol(keyRune) { - // macro processing - if e.macroEnabled { - var keyS = string(keyRune) - if e.macroTable.HasKey(oldText) { - e.preeditor.Reset() - return e.expandMacro(oldText) + keyS, true - } - } - if bamboo.HasAnyVietnameseRune(oldText) && e.mustFallbackToEnglish() { - e.preeditor.RestoreLastWord(false) - newText := e.preeditor.GetProcessedString(bamboo.EnglishMode) + string(keyRune) - e.preeditor.ProcessKey(keyRune, bamboo.EnglishMode) - return newText, true - } - e.preeditor.ProcessKey(keyRune, bamboo.EnglishMode) - return oldText + string(keyRune), true - } - return "", true -} - -func (e *FcitxBambooEngine) encodeText(text string) string { - return bamboo.Encode(e.outputCharset, text) -} - -func (e *FcitxBambooEngine) commitPreeditAndReset(s string) { - e.commitText = s - e.preeditText = "" - e.preeditor.Reset() -} - -func (e *FcitxBambooEngine) updatePreedit(processedStr string) { - var encodedStr = e.encodeText(processedStr) - var preeditLen = uint32(utf8.RuneCountInString(encodedStr)) - if preeditLen == 0 { - e.preeditText = "" - e.commitText = "" - return - } - - e.preeditText = encodedStr -} - -func (e *FcitxBambooEngine) canProcessKey(keyVal uint32) bool { - var keyRune = rune(keyVal) - if keyVal == FcitxSpace || keyVal == FcitxBackSpace || bamboo.IsWordBreakSymbol(keyRune) { - return true - } - if ok, _ := e.getMacroText(); ok && keyVal == FcitxTab { - return true - } - return e.preeditor.CanProcessKey(keyRune) -} -func (e *FcitxBambooEngine) isValidState(state uint32) bool { - if state&FcitxControlMask != 0 || - state&FcitxMod1Mask != 0 || - state&FcitxIgnoredMask != 0 || - state&FcitxSuperMask != 0 || - state&FcitxHyperMask != 0 || - state&FcitxMetaMask != 0 { - return false - } - return true -} - -func (e *FcitxBambooEngine) getComposedString(oldText string) string { - if bamboo.HasAnyVietnameseRune(oldText) && e.mustFallbackToEnglish() { - return e.getProcessedString(bamboo.EnglishMode) - } - return oldText -} - -func (e *FcitxBambooEngine) preeditProcessKeyEvent(keyVal uint32, state uint32) bool { - var rawKeyLen = e.getRawKeyLen() - var keyRune = rune(keyVal) - var oldText = e.getPreeditString() - defer e.updateLastKeyWithShift(keyVal, state) - - // workaround for chrome's address bar and Google SpreadSheets - if !e.shouldRestoreKeyStrokes { - if !e.isValidState(state) || !e.canProcessKey(keyVal) || - (!e.macroEnabled && rawKeyLen == 0 && !e.preeditor.CanProcessKey(keyRune)) { - if rawKeyLen > 0 { - e.commitPreeditAndReset(e.getPreeditString()) - } - return false - } - } - - if keyVal == FcitxBackSpace { - if e.runeCount() == 1 { - e.commitPreeditAndReset("") - return true - } - if rawKeyLen > 0 { - e.preeditor.RemoveLastChar(true) - e.updatePreedit(e.getPreeditString()) - return true - } else { - return false - } - } - if keyVal == FcitxTab { - if ok, macText := e.getMacroText(); ok { - e.commitPreeditAndReset(macText) - } else { - e.commitPreeditAndReset(e.getComposedString(oldText)) - return false - } - return true - } - - newText, isWordBreakRune := e.getCommitText(keyVal, state) - if isWordBreakRune { - e.commitPreeditAndReset(newText) - return true - } - e.updatePreedit(newText) - return true -} diff --git a/bamboo/go.mod b/bamboo/go.mod deleted file mode 100644 index 7f57d072..00000000 --- a/bamboo/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module bamboo - -go 1.17 - -replace bamboo-core v0.0.0 => ./bamboo-core - -require bamboo-core v0.0.0 // indirect diff --git a/bamboo/macrotable.go b/bamboo/macrotable.go deleted file mode 100644 index 31856ef4..00000000 --- a/bamboo/macrotable.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2022 CSSlayer - * - * SPDX-License-Identifier: LGPL-2.1-or-later - * - */ -package main - -import "strings" - -type MacroTable struct { - mTable map[string]string -} - -func (e *MacroTable) HasKey(key string) bool { - return e.mTable[strings.ToLower(key)] != "" -} -func (e *MacroTable) GetText(key string) string { - return e.mTable[strings.ToLower(key)] -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 073dfd12..215e2840 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,16 +8,21 @@ set(fcitx_lotus_sources emoji.cpp ) +list(APPEND fcitx_lotus_sources lotus-unikey-backend.cpp) + add_library(lotus MODULE ${fcitx_lotus_sources}) set_target_properties(lotus PROPERTIES OUTPUT_NAME "lotus") + target_link_libraries(lotus - Fcitx5::Core - Fcitx5::Config - Fcitx5::Module::Emoji - Bamboo::Core - Pthread::Pthread - X11::X11 -) + Fcitx5::Core + Fcitx5::Config + Fcitx5::Module::Emoji + lotus-unikey-bridge + Pthread::Pthread + X11::X11 + ) + target_compile_definitions(lotus PRIVATE LOTUS_ENGINE_UNIKEY=1) + target_include_directories(lotus PRIVATE "${PROJECT_SOURCE_DIR}/unikey/core") target_include_directories(lotus PRIVATE ${PROJECT_BINARY_DIR} @@ -31,4 +36,4 @@ fcitx5_translate_desktop_file(lotus.conf.in lotus.conf) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lotus.conf" DESTINATION "${CMAKE_INSTALL_DATADIR}/fcitx5/inputmethod") configure_file(lotus-addon.conf.in.in lotus-addon.conf.in) fcitx5_translate_desktop_file("${CMAKE_CURRENT_BINARY_DIR}/lotus-addon.conf.in" lotus-addon.conf) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lotus-addon.conf" RENAME lotus.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon") \ No newline at end of file +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lotus-addon.conf" RENAME lotus.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon") diff --git a/src/lotus-bamboo-backend.cpp b/src/lotus-bamboo-backend.cpp new file mode 100644 index 00000000..5d78641e --- /dev/null +++ b/src/lotus-bamboo-backend.cpp @@ -0,0 +1,139 @@ +/* + * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LOTUS_ENGINE_UNIKEY + +#include "lotus-input-backend.hpp" +#include "lotus-config.h" +#include "lotus-engine.h" +#include "lotus.h" + +#include +#include +#include +#include + +namespace fcitx { + +namespace { + +class LotusBambooInputBackend final : public LotusInputBackend { +public: + void recreateEngine(LotusEngine* engine) override { + engine_.reset(); + if (engine->config().inputMethod.value() == "Custom") { + const auto& keymaps = *engine->customKeymap().customKeymap; + std::vector charArray; + charArray.reserve((keymaps.size() * 2) + 1); + for (const auto& keymap : keymaps) { + charArray.push_back(const_cast(keymap.key->data())); // NOLINT + charArray.push_back(const_cast(keymap.value->data())); // NOLINT + } + charArray.push_back(nullptr); + engine_.reset(NewCustomEngine(charArray.data(), engine->dictionary(), engine->macroTable())); + } else { + engine_.reset(NewEngine(engine->config().inputMethod->data(), engine->dictionary(), engine->macroTable())); + } + } + + void setOptions(LotusEngine* engine) override { + if (!engine_) + return; + FcitxBambooEngineOption option = { + .autoNonVnRestore = *engine->config().autoNonVnRestore, + .ddFreeStyle = *engine->config().ddFreeStyle, + .macroEnabled = *engine->config().enableMacro, + .autoCapitalizeMacro = *engine->config().capitalizeMacro, + .spellCheckWithDicts = *engine->config().spellCheck, + .outputCharset = engine->config().outputCharset->data(), + .modernStyle = *engine->config().modernStyle, + .freeMarking = *engine->config().freeMarking, + .w2u = *engine->config().w2u, + .timeFormat = engine->config().timeFormat->data(), + .dateFormat = engine->config().dateFormat->data(), + }; + EngineSetOption(engine_.handle(), &option); + } + + void resetEngine() override { + if (engine_) + ResetEngine(engine_.handle()); + } + + void rebuildFromText(const char* utf8) override { + if (engine_) + EngineRebuildFromText(engine_.handle(), utf8); + } + + bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) override { + if (!engine_) + return false; + char *cr = nullptr, *pr = nullptr; + bool ok = EngineProcessKeyEventAndPull(engine_.handle(), sym, state, &cr, &pr); + if (commit) { + commit->assign(cr ? cr : ""); + } + if (preedit) { + preedit->assign(pr ? pr : ""); + } + std::free(cr); // NOLINT + std::free(pr); // NOLINT + return ok; + } + + bool processKeyEvent(uint32_t sym, uint32_t state) override { + if (!engine_) + return false; + return EngineProcessKeyEvent(engine_.handle(), sym, state); + } + + void pullCommitAndPreedit(std::string* commit, std::string* preedit) override { + if (!engine_) + return; + char *cp = nullptr, *pp = nullptr; + EnginePullCommitAndPreedit(engine_.handle(), &cp, &pp); + if (commit) + commit->assign(cp ? cp : ""); + if (preedit) + preedit->assign(pp ? pp : ""); + std::free(cp); // NOLINT + std::free(pp); // NOLINT + } + + void pullCommit(std::string* out) override { + if (!engine_ || !out) + return; + char* p = EnginePullCommit(engine_.handle()); + out->assign(p ? p : ""); + std::free(p); // NOLINT + } + + void pullPreedit(std::string* out) override { + if (!engine_ || !out) + return; + char* p = EnginePullPreedit(engine_.handle()); + out->assign(p ? p : ""); + std::free(p); // NOLINT + } + + void commitPreedit() override { + if (engine_) + EngineCommitPreedit(engine_.handle()); + } + +private: + CGoObject engine_; +}; + +} // namespace + +std::unique_ptr makeLotusInputBackend() { + return std::make_unique(); +} + +} // namespace fcitx + +#endif // !LOTUS_ENGINE_UNIKEY diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 6cabedf9..4663cf77 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -65,6 +65,7 @@ namespace fcitx { return isAppModeMenuReservedKey(hotkeySym) ? FcitxKey_f : hotkeySym; } +#ifndef LOTUS_ENGINE_UNIKEY static inline uintptr_t newMacroTable(const lotusMacroTable& macroTable) { const auto& macros = *macroTable.macros; std::vector charArray; @@ -77,6 +78,7 @@ namespace fcitx { charArray.push_back(nullptr); return NewMacroTable(charArray.data()); } +#endif static inline std::vector convertToStringList(char** list) { std::vector result; @@ -106,12 +108,24 @@ namespace fcitx { isGnome_ = (desktop != nullptr) && std::string(desktop).find("GNOME") != std::string::npos; // emptyCustomKeymap_.customKeymap is implicitly initialized to empty by fcitx::Option default value macro. startMonitoring(); +#ifndef LOTUS_ENGINE_UNIKEY Init(); { auto imNames = convertToStringList(GetInputMethodNames()); imNames.push_back("Custom"); imNames_ = std::move(imNames); } +#else + imNames_ = {"Telex", + "VNI", + "Telex 2", + "Telex + VNI", + "Telex + VNI + VIQR", + "VIQR", + "Microsoft layout", + "VNI Bàn phím tiếng Pháp", + "Custom"}; +#endif config_.inputMethod.annotation().setList(imNames_); auto& uiManager = instance_->userInterfaceManager(); @@ -131,7 +145,11 @@ namespace fcitx { charsetMenu_ = std::make_unique(); charsetAction_->setMenu(charsetMenu_.get()); +#ifndef LOTUS_ENGINE_UNIKEY auto charsets = convertToStringList(GetCharsetNames()); +#else + std::vector charsets = {"Unicode", "TCVN3", "VNI Win", "VIQR", "BK HCM 2", "UTF-8 VIQR"}; +#endif for (const auto& charset : charsets) { charsetSubAction_.emplace_back(std::make_unique()); auto* action = charsetSubAction_.back().get(); @@ -246,6 +264,7 @@ namespace fcitx { readAsIni(config_, "conf/lotus.conf"); readAsIni(customKeymap_, CustomKeymapFile); readAsIni(macroTables_, MacroTableFile); +#ifndef LOTUS_ENGINE_UNIKEY macroTableObject_.reset(newMacroTable(macroTables_)); if (config_.enableDictionary.value()) { #if LOTUS_USE_MODERN_FCITX_API @@ -279,6 +298,7 @@ namespace fcitx { } } } +#endif loadAppRules(); populateConfig(); } @@ -320,7 +340,9 @@ namespace fcitx { } else if (path == "lotus-macro") { macroTables_.load(config, true); safeSaveAsIni(macroTables_, MacroTableFile); +#ifndef LOTUS_ENGINE_UNIKEY macroTableObject_.reset(newMacroTable(macroTables_)); +#endif refreshEngine(); } else if (path == "app_rules") { appRulesTables_.load(config, true); diff --git a/src/lotus-input-backend.hpp b/src/lotus-input-backend.hpp new file mode 100644 index 00000000..7de327ec --- /dev/null +++ b/src/lotus-input-backend.hpp @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Abstract input engine: Go/Bamboo vs native Unikey (no CGO). + */ +#ifndef FCITX5_LOTUS_INPUT_BACKEND_HPP +#define FCITX5_LOTUS_INPUT_BACKEND_HPP + +#include +#include +#include + +namespace fcitx { + +class LotusEngine; + +/** + * Per-context Vietnamese engine (Bamboo or Unikey implementation). + */ +class LotusInputBackend { +public: + virtual ~LotusInputBackend() = default; + + virtual void recreateEngine(LotusEngine* engine) = 0; + virtual void setOptions(LotusEngine* engine) = 0; + virtual void resetEngine() = 0; + virtual void rebuildFromText(const char* utf8) = 0; + virtual bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) = 0; + virtual bool processKeyEvent(uint32_t sym, uint32_t state) = 0; + virtual void pullCommitAndPreedit(std::string* commit, std::string* preedit) = 0; + virtual void pullCommit(std::string* out) = 0; + virtual void pullPreedit(std::string* out) = 0; + virtual void commitPreedit() = 0; +}; + +std::unique_ptr makeLotusInputBackend(); + +} // namespace fcitx + +#endif diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index c334e29c..027f3d2e 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -10,6 +10,7 @@ #include "lotus-engine.h" #include "lotus-candidates.h" #include "lotus-utils.h" +#include "lotus-input-backend.hpp" #include "lotus.h" #include @@ -74,43 +75,18 @@ namespace fcitx { } void LotusState::setEngine() { - lotusEngine_.reset(); + inputBackend_.reset(); + inputBackend_ = makeLotusInputBackend(); realMode = modeStringToEnum(engine_->config().mode.value()); - if (engine_->config().inputMethod.value() == "Custom") { - const auto& keymaps = *engine_->customKeymap().customKeymap; - std::vector charArray; - charArray.reserve((keymaps.size() * 2) + 1); - for (const auto& keymap : keymaps) { - charArray.push_back(const_cast(keymap.key->data())); //NOLINT - charArray.push_back(const_cast(keymap.value->data())); //NOLINT - } - charArray.push_back(nullptr); - lotusEngine_.reset(NewCustomEngine(charArray.data(), engine_->dictionary(), engine_->macroTable())); - } else { - lotusEngine_.reset(NewEngine(engine_->config().inputMethod->data(), engine_->dictionary(), engine_->macroTable())); - } + inputBackend_->recreateEngine(engine_); setOption(); } void LotusState::setOption() { - if (!lotusEngine_) + if (!inputBackend_) return; - FcitxBambooEngineOption option = { - .autoNonVnRestore = *engine_->config().autoNonVnRestore, - .ddFreeStyle = *engine_->config().ddFreeStyle, - .macroEnabled = *engine_->config().enableMacro, - .autoCapitalizeMacro = *engine_->config().capitalizeMacro, - .spellCheckWithDicts = *engine_->config().spellCheck, - .outputCharset = engine_->config().outputCharset->data(), - .modernStyle = *engine_->config().modernStyle, - .freeMarking = *engine_->config().freeMarking, - .w2u = *engine_->config().w2u, - .timeFormat = engine_->config().timeFormat->data(), - .dateFormat = engine_->config().dateFormat->data(), - }; - - EngineSetOption(lotusEngine_.handle(), &option); + inputBackend_->setOptions(engine_); } bool LotusState::connect_uinput_server() { @@ -231,20 +207,18 @@ namespace fcitx { } void LotusState::handlePreeditMode(KeyEvent& keyEvent, KeySym currentSym) { - char* commitRaw = nullptr; - char* preeditRaw = nullptr; - bool processed = EngineProcessKeyEventAndPull(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states(), &commitRaw, &preeditRaw) != 0U; - UniqueCPtr commit(commitRaw); - UniqueCPtr preedit(preeditRaw); + std::string commitStr; + std::string preeditStr; + bool processed = inputBackend_->processKeyEventAndPull(currentSym, keyEvent.rawKey().states(), &commitStr, &preeditStr); if (processed) keyEvent.filterAndAccept(); - if (commit && (*commit.get() != 0)) { - LOTUS_INFO("Commit: " + std::string(commit.get())); - ic_->commitString(commit.get()); + if (!commitStr.empty()) { + LOTUS_INFO("Commit: " + commitStr); + ic_->commitString(commitStr); } ic_->inputPanel().reset(); - if (preedit && (*preedit.get() != 0)) { - std::string_view view = preedit.get(); + if (!preeditStr.empty()) { + std::string_view view = preeditStr; Text text; TextFormatFlags fmt = TextFormatFlag::NoFlag; if (utf8::validate(view)) @@ -577,7 +551,7 @@ namespace fcitx { current_backspace_count_ = 0; pending_commit_string_.clear(); hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); oldPreBuffer_.clear(); return true; } @@ -642,13 +616,12 @@ namespace fcitx { if (isBackspace(currentSym) || currentSym == FcitxKey_Return) { if (isBackspace(currentSym)) { hasHistory_ = true; - char* preeditBs = nullptr; - EngineProcessKeyEventAndPull(lotusEngine_.handle(), FcitxKey_BackSpace, 0, nullptr, &preeditBs); - UniqueCPtr preeditC(preeditBs); - oldPreBuffer_ = (preeditC && (*preeditC.get() != 0)) ? preeditC.get() : ""; + std::string preBs; + inputBackend_->processKeyEventAndPull(FcitxKey_BackSpace, 0, nullptr, &preBs); + oldPreBuffer_ = preBs; } else { hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); oldPreBuffer_.clear(); } keyEvent.forward(); @@ -661,14 +634,11 @@ namespace fcitx { return; } - char* commitRaw = nullptr; - char* preeditRaw = nullptr; - bool processed = EngineProcessKeyEventAndPull(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states(), &commitRaw, &preeditRaw) != 0U; - UniqueCPtr commitF(commitRaw); - UniqueCPtr preeditC(preeditRaw); + std::string commitStr; + std::string preeditStrBuf; + bool processed = inputBackend_->processKeyEventAndPull(currentSym, keyEvent.rawKey().states(), &commitStr, &preeditStrBuf); - if (commitF && (*commitF.get() != 0)) { - std::string commitStr = commitF.get(); + if (!commitStr.empty()) { std::string commonPrefix; std::string deletedPart; std::string addedPart; @@ -698,20 +668,17 @@ namespace fcitx { } hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); oldPreBuffer_.clear(); return; } // Treat "processed but no effect" as passthrough - bool hasCommit = (commitF && (*commitF.get() != 0)); - bool hasPreedit = (preeditC && (*preeditC.get() != 0)); - - if (!processed || (!hasCommit && !hasPreedit)) { - if (checkEmptyPreedit && !hasPreedit) { + if (!processed || (!commitStr.empty() && !preeditStrBuf.empty())) { + if (checkEmptyPreedit && !preeditStrBuf.empty()) { hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); oldPreBuffer_.clear(); } keyEvent.forward(); @@ -721,7 +688,7 @@ namespace fcitx { hasHistory_ = true; realtextLen.fetch_add(1, std::memory_order_acq_rel); - std::string preeditStr = (preeditC && (*preeditC.get() != 0)) ? preeditC.get() : ""; + std::string preeditStr = preeditStrBuf; std::string commonPrefix; std::string deletedPart; @@ -756,7 +723,7 @@ namespace fcitx { break; } if (!hasMultibyte && utf8::length(oldPreBuffer_) > 8) { - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); hasHistory_ = false; oldPreBuffer_.clear(); } @@ -802,7 +769,7 @@ namespace fcitx { } if (isBackspace(keyEvent.rawKey().sym())) { - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); keyEvent.forward(); return; } @@ -851,34 +818,32 @@ namespace fcitx { return; } - EngineRebuildFromText(lotusEngine_.handle(), oldWord.c_str()); + inputBackend_->rebuildFromText(oldWord.c_str()); - bool processed = EngineProcessKeyEvent(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states()) != 0U; + bool processed = inputBackend_->processKeyEvent(currentSym, keyEvent.rawKey().states()); if (!processed) { keyEvent.forward(); - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); return; } - char* commitP = nullptr; - char* preeditP = nullptr; - EnginePullCommitAndPreedit(lotusEngine_.handle(), &commitP, &preeditP); - UniqueCPtr commitPtr(commitP); - UniqueCPtr preeditPtr(preeditP); + std::string commitPart; + std::string preeditPart; + inputBackend_->pullCommitAndPreedit(&commitPart, &preeditPart); std::string newWord; - if (commitPtr && (*commitPtr.get() != 0)) - newWord += commitPtr.get(); - if (preeditPtr && (*preeditPtr.get() != 0)) - newWord += preeditPtr.get(); + if (!commitPart.empty()) + newWord += commitPart; + if (!preeditPart.empty()) + newWord += preeditPart; std::string commonPrefix; std::string deletedPart; std::string addedPart; compareAndSplitStrings(oldWord, newWord, commonPrefix, deletedPart, addedPart); if (deletedPart.empty() && addedPart == keyEvent.key().toString()) { - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); keyEvent.forward(); return; } @@ -895,12 +860,12 @@ namespace fcitx { LOTUS_INFO("Commit: " + addedPart); } - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); keyEvent.filterAndAccept(); return; } - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); keyEvent.filterAndAccept(); return; } @@ -908,25 +873,23 @@ namespace fcitx { void LotusState::processNormalKey(KeyEvent& keyEvent, KeySym currentSym) { auto* ic = keyEvent.inputContext(); - ResetEngine(lotusEngine_.handle()); - char* commitP = nullptr; - char* preeditP = nullptr; - bool processed = EngineProcessKeyEventAndPull(lotusEngine_.handle(), currentSym, keyEvent.rawKey().states(), &commitP, &preeditP) != 0U; - UniqueCPtr commitPtr(commitP); - UniqueCPtr preeditPtr(preeditP); + inputBackend_->resetEngine(); + std::string commitPart; + std::string preeditPart; + bool processed = inputBackend_->processKeyEventAndPull(currentSym, keyEvent.rawKey().states(), &commitPart, &preeditPart); if (processed) { std::string out; - if (commitPtr && (*commitPtr.get() != 0)) - out += commitPtr.get(); - if (preeditPtr && (*preeditPtr.get() != 0)) - out += preeditPtr.get(); + if (!commitPart.empty()) + out += commitPart; + if (!preeditPart.empty()) + out += preeditPart; if (!out.empty()) { LOTUS_INFO("Commit: " + out); ic->commitString(out); } - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); keyEvent.filterAndAccept(); } else { keyEvent.forward(); @@ -955,7 +918,7 @@ namespace fcitx { } void LotusState::keyEvent(KeyEvent& keyEvent) { - if (!lotusEngine_ || keyEvent.isRelease()) + if (!inputBackend_ || keyEvent.isRelease()) return; if (uinput_client_fd_ < 0) { LOTUS_WARN("Cannot connect to uinput server, reconnecting...."); @@ -970,7 +933,7 @@ namespace fcitx { LOTUS_INFO("Need engine reset"); oldPreBuffer_.clear(); hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); is_deleting_.store(false); current_backspace_count_ = 0; isPrevSpace_ = false; @@ -1108,19 +1071,20 @@ namespace fcitx { return; } - if (lotusEngine_) { + if (inputBackend_) { isPrevSpace_ = false; shouldCapitalize_ = false; isPrevPunctuation_ = false; if (realMode == LotusMode::Preedit && isFocusOut) { - EngineCommitPreedit(lotusEngine_.handle()); - UniqueCPtr commit(EnginePullCommit(lotusEngine_.handle())); - if (commit && (*commit.get() != 0)) { - ic_->commitString(commit.get()); - LOTUS_INFO("Commit: " + std::string(commit.get())); + inputBackend_->commitPreedit(); + std::string commit; + inputBackend_->pullCommit(&commit); + if (!commit.empty()) { + ic_->commitString(commit); + LOTUS_INFO("Commit: " + commit); } } - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); } if (getFrontendName(ic_) != "dbus") clearAllBuffers(); @@ -1155,12 +1119,13 @@ namespace fcitx { switch (realMode) { case LotusMode::Preedit: { ic_->inputPanel().reset(); - if (lotusEngine_) { - EngineCommitPreedit(lotusEngine_.handle()); - UniqueCPtr commit(EnginePullCommit(lotusEngine_.handle())); - if (commit && (*commit.get() != 0)) - ic_->commitString(commit.get()); - ResetEngine(lotusEngine_.handle()); + if (inputBackend_) { + inputBackend_->commitPreedit(); + std::string commit; + inputBackend_->pullCommit(&commit); + if (!commit.empty()) + ic_->commitString(commit); + inputBackend_->resetEngine(); } ic_->updateUserInterface(UserInterfaceComponent::InputPanel); ic_->updatePreedit(); @@ -1170,8 +1135,8 @@ namespace fcitx { case LotusMode::UinputHC: case LotusMode::Smooth: case LotusMode::SurroundingText: { - if (lotusEngine_) { - ResetEngine(lotusEngine_.handle()); + if (inputBackend_) { + inputBackend_->resetEngine(); } break; } @@ -1196,8 +1161,8 @@ namespace fcitx { buffered_keys_.clear(); shouldCapitalize_ = false; isPrevPunctuation_ = false; - if (lotusEngine_) - ResetEngine(lotusEngine_.handle()); + if (inputBackend_) + inputBackend_->resetEngine(); } bool LotusState::isEmptyHistory() const { @@ -1224,9 +1189,11 @@ namespace fcitx { continue; } - bool processed = EngineProcessKeyEvent(lotusEngine_.handle(), sym, state) != 0U; + bool processed = inputBackend_->processKeyEvent(sym, state); - auto commitF = UniqueCPtr(EnginePullCommit(lotusEngine_.handle())); + std::string commitPull; + inputBackend_->pullCommit(&commitPull); + UniqueCPtr commitF(commitPull.empty() ? nullptr : strdup(commitPull.c_str())); if (commitF && (*commitF.get() != 0)) { std::string commitStr = commitF.get(); std::string commonPrefix; @@ -1243,7 +1210,7 @@ namespace fcitx { } performReplacement(deletedPart, addedPart); hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); oldPreBuffer_.clear(); return; } @@ -1252,7 +1219,7 @@ namespace fcitx { } hasHistory_ = false; - ResetEngine(lotusEngine_.handle()); + inputBackend_->resetEngine(); oldPreBuffer_.clear(); continue; } @@ -1265,8 +1232,8 @@ namespace fcitx { hasHistory_ = true; realtextLen.fetch_add(1, std::memory_order_acq_rel); - UniqueCPtr preeditC(EnginePullPreedit(lotusEngine_.handle())); - std::string preeditStr = (preeditC && (*preeditC.get() != 0)) ? preeditC.get() : ""; + std::string preeditStr; + inputBackend_->pullPreedit(&preeditStr); std::string commonPrefix; std::string deletedPart; diff --git a/src/lotus-state.h b/src/lotus-state.h index 37238655..6a59b6e6 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -15,6 +15,7 @@ #ifndef _FCITX5_LOTUS_STATE_H_ #define _FCITX5_LOTUS_STATE_H_ +#include "lotus-input-backend.hpp" #include "lotus.h" #include "emoji-entry.h" #include "lotus-utils.h" @@ -24,6 +25,7 @@ #include #include +#include struct EmojiEntry; @@ -91,9 +93,9 @@ namespace fcitx { private: static constexpr size_t MAX_BUFFERED_KEYS = 50; - LotusEngine* engine_; - InputContext* ic_; - CGoObject lotusEngine_; + LotusEngine* engine_; + InputContext* ic_; + std::unique_ptr inputBackend_; std::string oldPreBuffer_; bool hasHistory_ = false; int expected_backspaces_ = 0; diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp new file mode 100644 index 00000000..4483959f --- /dev/null +++ b/src/lotus-unikey-backend.cpp @@ -0,0 +1,282 @@ +/* + * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Native Unikey engine (fcitx5-unikey patterns; no Go/CGO). + */ +#ifdef LOTUS_ENGINE_UNIKEY + +#include "lotus-input-backend.hpp" +#include "lotus-config.h" +#include "lotus-engine.h" +#include "../unikey/LotusUnikeyEngine.hpp" +#include "unikeyinputcontext.h" + +#include +#include +#include +#include +#include + +#include + +namespace fcitx { + +namespace { + +static bool isWordBreakSym(unsigned char c) { + static const std::unordered_set WordBreakSyms = { + ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', + }; + return WordBreakSyms.contains(c); +} + +static UkInputMethod mapLotusIm(const std::string& name) { + if (name.find("Telex") != std::string::npos && name.find("VNI") == std::string::npos) + return UkTelex; + if (name.find("VNI") != std::string::npos || name == "VNI") + return UkVni; + if (name.find("VIQR") != std::string::npos) + return UkViqr; + if (name.find("Microsoft") != std::string::npos || name.find("Ms") != std::string::npos) + return UkMsVi; + if (name.find("Simple") != std::string::npos) + return UkSimpleTelex2; + return UkTelex; +} + +static int mapLotusCharset(const std::string& name) { + if (name == "Unicode" || name.empty()) + return CONV_CHARSET_XUTF8; + if (name.find("TCVN") != std::string::npos) + return CONV_CHARSET_TCVN3; + if (name.find("VNI") != std::string::npos && name != "VNI") + return CONV_CHARSET_VNIWIN; + if (name.find("VIQR") != std::string::npos) + return CONV_CHARSET_VIQR; + return CONV_CHARSET_XUTF8; +} + +class LotusUnikeyInputBackend final : public LotusInputBackend { +public: + void recreateEngine(LotusEngine* engine) override { + engineRef_ = engine; + uk_ = std::make_unique<::fcitx::lotus::LotusUnikeyEngine>(); + applyFromConfig(engine); + resetEngine(); + } + + void setOptions(LotusEngine* engine) override { + applyFromConfig(engine); + } + + void resetEngine() override { + pendingPullCommit_.clear(); + preeditStr_.clear(); + lastShiftPressed_ = FcitxKey_None; + lastKeyWithShift_ = false; + autoCommit_ = false; + if (uk_) + uk_->resetBuf(); + } + + void rebuildFromText(const char* utf8) override { + resetEngine(); + if (!uk_ || utf8 == nullptr) + return; + for (auto ucs : utf8::MakeUTF8CharRange(std::string_view(utf8))) { + if (ucs < 128U) + uk_->putChar(static_cast(ucs)); + else + uk_->putChar(ucs); + } + syncState(FcitxKey_None); + } + + bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) override { + pendingPullCommit_.clear(); + bool ok = dispatch(sym, state); + if (commit) + *commit = pendingPullCommit_; + if (preedit) + *preedit = preeditStr_; + pendingPullCommit_.clear(); + return ok; + } + + bool processKeyEvent(uint32_t sym, uint32_t state) override { + pendingPullCommit_.clear(); + return dispatch(sym, state); + } + + void pullCommitAndPreedit(std::string* commit, std::string* preedit) override { + if (commit) + *commit = pendingPullCommit_; + if (preedit) + *preedit = preeditStr_; + pendingPullCommit_.clear(); + } + + void pullCommit(std::string* out) override { + if (out) + *out = pendingPullCommit_; + pendingPullCommit_.clear(); + } + + void pullPreedit(std::string* out) override { + if (out) + *out = preeditStr_; + } + + void commitPreedit() override { + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + if (uk_) + uk_->resetBuf(); + } + +private: + void applyFromConfig(LotusEngine* engine) { + if (!uk_) + return; + UkInputMethod im = mapLotusIm(engine->config().inputMethod.value()); + uk_->setInputMethod(im); + uk_->setOutputCharset(mapLotusCharset(engine->config().outputCharset.value())); + UnikeyOptions opt{}; + opt.freeMarking = *engine->config().freeMarking ? 1 : 0; + opt.modernStyle = *engine->config().modernStyle ? 1 : 0; + opt.macroEnabled = *engine->config().enableMacro ? 1 : 0; + opt.useUnicodeClipboard = 0; + opt.alwaysMacro = 0; + opt.strictSpellCheck = 0; + opt.useIME = 0; + opt.spellCheckEnabled = *engine->config().spellCheck ? 1 : 0; + opt.autoNonVnRestore = *engine->config().autoNonVnRestore ? 1 : 0; + uk_->setOptions(&opt); + } + + void eraseChars(int num_chars) { + int i; + int k = num_chars; + unsigned char c = 0; + for (i = static_cast(preeditStr_.length()) - 1; i >= 0 && k > 0; --i) { + c = preeditStr_.at(static_cast(i)); + if (c < (unsigned char)'\x80' || c >= (unsigned char)'\xC0') + --k; + } + preeditStr_.erase(static_cast(i + 1)); + } + + void syncState(KeySym sym) { + auto* uic = uk_->context(); + if (uic->backspaces() > 0) { + if (static_cast(preeditStr_.length()) <= uic->backspaces()) + preeditStr_.clear(); + else + eraseChars(uic->backspaces()); + } + if (uic->bufChars() > 0) { + preeditStr_.append(reinterpret_cast(uic->buf()), + static_cast(uic->bufChars())); + } else if (sym != FcitxKey_Shift_L && sym != FcitxKey_Shift_R && sym != FcitxKey_None) { + preeditStr_.append(utf8::UCS4ToUTF8(sym)); + } + } + + bool dispatch(uint32_t sym, uint32_t state) { + if (!uk_) + return false; + + KeyStates st(static_cast(state)); + const auto rawSym = static_cast(sym); + + if (st.testAny(KeyState::Ctrl_Alt) || rawSym == FcitxKey_Control_L || rawSym == FcitxKey_Control_R || + rawSym == FcitxKey_Tab || rawSym == FcitxKey_Return || rawSym == FcitxKey_Delete || + rawSym == FcitxKey_KP_Enter || (rawSym >= FcitxKey_Home && rawSym <= FcitxKey_Insert) || + (rawSym >= FcitxKey_KP_Home && rawSym <= FcitxKey_KP_Delete)) { + uk_->context()->filter(0); + syncState(rawSym); + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return false; + } + if (st.test(KeyState::Super)) + return false; + if ((rawSym >= FcitxKey_Caps_Lock && rawSym <= FcitxKey_Hyper_R) || rawSym == FcitxKey_Shift_L || + rawSym == FcitxKey_Shift_R) + return false; + + if (rawSym == FcitxKey_BackSpace) { + uk_->backspacePress(); + if (uk_->context()->backspaces() == 0 || preeditStr_.empty()) { + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return true; + } + if (static_cast(preeditStr_.length()) <= uk_->context()->backspaces()) + preeditStr_.clear(); + else + eraseChars(uk_->context()->backspaces()); + if (uk_->context()->bufChars() > 0) + preeditStr_.append(reinterpret_cast(uk_->context()->buf()), + static_cast(uk_->context()->bufChars())); + return true; + } + + if (rawSym >= FcitxKey_KP_Multiply && rawSym <= FcitxKey_KP_9) { + uk_->context()->filter(0); + syncState(rawSym); + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return false; + } + + if (rawSym >= FcitxKey_space && rawSym <= FcitxKey_asciitilde) { + uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); + uk_->filter(sym); + syncState(rawSym); + + if (!preeditStr_.empty() && preeditStr_.back() == static_cast(sym) && isWordBreakSym(static_cast(sym))) { + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return true; + } + return true; + } + + uk_->context()->filter(0); + syncState(rawSym); + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return false; + } + + std::unique_ptr<::fcitx::lotus::LotusUnikeyEngine> uk_; + LotusEngine* engineRef_ = nullptr; + std::string preeditStr_; + std::string pendingPullCommit_; + KeySym lastShiftPressed_ = FcitxKey_None; + bool lastKeyWithShift_ = false; + bool autoCommit_ = false; +}; + +} // namespace + +std::unique_ptr makeLotusInputBackend() { + return std::make_unique(); +} + +} // namespace fcitx + +#endif // LOTUS_ENGINE_UNIKEY diff --git a/src/lotus.h b/src/lotus.h index 49f4c9e9..c6379113 100644 --- a/src/lotus.h +++ b/src/lotus.h @@ -7,39 +7,26 @@ * */ -/** - * @file lotus.h - * @brief Main header file for fcitx5-lotus Vietnamese input method. - */ - #ifndef _FCITX5_LOTUS_H_ #define _FCITX5_LOTUS_H_ -#include "bamboo-core.h" #include +#ifndef LOTUS_ENGINE_UNIKEY +#include "bamboo-core.h" + namespace fcitx { class LotusEngine; class LotusState; /** - * @brief RAII wrapper for CGo handles. - * - * Manages lifecycle of Go objects accessed from C++ through uintptr_t handles. - * Automatically releases handles on destruction. + * RAII wrapper for CGo handles (Bamboo/Go engine). */ class CGoObject { public: - /** - * @brief Constructs with optional handle. - * @param handle Optional CGo handle. - */ CGoObject(std::optional handle = std::nullopt) : handle_(handle) {} - /** - * @brief Releases the handle on destruction. - */ ~CGoObject() { if (handle_) { DeleteObject(*handle_); @@ -62,27 +49,15 @@ namespace fcitx { return *this; } - /** - * @brief Resets with a new handle, releasing the old one. - * @param handle New handle to store. - */ void reset(std::optional handle = std::nullopt) { clear(); handle_ = handle; } - /** - * @brief Gets the stored handle. - * @return The handle value or 0 if empty. - */ uintptr_t handle() const { return handle_.value_or(0); } - /** - * @brief Releases ownership of the handle. - * @return The handle value or 0 if empty. - */ uintptr_t release() { if (handle_) { uintptr_t v = *handle_; @@ -92,18 +67,11 @@ namespace fcitx { return 0; } - /** - * @brief Checks if a valid handle is stored. - * @return True if handle exists and is non-zero. - */ explicit operator bool() const { return handle_.has_value() && *handle_ != 0; } private: - /** - * @brief Releases the current handle. - */ void clear() { if (handle_) { DeleteObject(*handle_); @@ -116,4 +84,54 @@ namespace fcitx { } // namespace fcitx +#else + +namespace fcitx { + + class LotusEngine; + class LotusState; + + /** Stub when Bamboo/Go is disabled (Unikey engine): no runtime handles. */ + class CGoObject { + public: + CGoObject(std::optional handle = std::nullopt) : handle_(handle) {} + ~CGoObject() = default; + CGoObject(const CGoObject&) = delete; + CGoObject& operator=(const CGoObject&) = delete; + CGoObject(CGoObject&& other) noexcept : handle_(other.handle_) { + other.handle_ = std::nullopt; + } + CGoObject& operator=(CGoObject&& other) noexcept { + if (this != &other) { + handle_ = other.handle_; + other.handle_ = std::nullopt; + } + return *this; + } + void reset(std::optional handle = std::nullopt) { + handle_ = handle; + } + uintptr_t handle() const { + return handle_.value_or(0); + } + uintptr_t release() { + if (handle_) { + uintptr_t v = *handle_; + handle_ = std::nullopt; + return v; + } + return 0; + } + explicit operator bool() const { + return handle_.has_value() && *handle_ != 0; + } + + private: + std::optional handle_; + }; + +} // namespace fcitx + +#endif + #endif // _FCITX5_LOTUS_H_ diff --git a/unikey/LotusUnikeyEngine.hpp b/unikey/LotusUnikeyEngine.hpp index c1249361..a8f934d3 100644 --- a/unikey/LotusUnikeyEngine.hpp +++ b/unikey/LotusUnikeyEngine.hpp @@ -7,8 +7,8 @@ * UnikeyInputContext). Intended to replace the Go/Bamboo engine when * LOTUS_USE_UNIKEY is wired through LotusState. */ -#ifndef FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP -#define FCITX5_LOTUS_LOTUS_ENGINE_HPP +// #ifndef FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP +// #define FCITX5_LOTUS_LOTUS_ENGINE_HPP #include "keycons.h" #include "vnlexi.h" @@ -60,4 +60,4 @@ class LotusUnikeyEngine { } // namespace fcitx::lotus -#endif +//#endif From c086e54ff9a7396fe74c74f7f27fe6c63425d8a9 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 8 May 2026 03:32:50 +0700 Subject: [PATCH 14/42] bye bye bamboo --- .gitmodules | 3 - CMakeLists.txt | 10 +- server/lotus-server.cpp | 2 +- src/CMakeLists.txt | 18 +- src/lotus-bamboo-backend.cpp | 139 ----------- src/lotus-engine.cpp | 10 +- src/lotus-engine.h | 8 +- src/lotus-input-backend.hpp | 40 ++-- src/lotus-state.cpp | 6 +- src/lotus-state.h | 44 ++-- src/lotus-unikey-backend.cpp | 448 +++++++++++++++++------------------ src/lotus.h | 140 +++-------- unikey/CMakeLists.txt | 2 +- unikey/LotusUnikeyEngine.hpp | 9 +- unikey/core/inputproc.cpp | 41 ++-- unikey/core/usrkeymap.cpp | 21 +- 16 files changed, 360 insertions(+), 581 deletions(-) delete mode 100644 .gitmodules delete mode 100644 src/lotus-bamboo-backend.cpp diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 89a793d6..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "bamboo/bamboo-core"] - path = bamboo/bamboo-core - url = https://github.com/LotusInputMethod/bamboo-core.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 56fc13a1..01545707 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,6 @@ include(GNUInstallDirs) include(ECMUninstallTarget) option(ENABLE_QT "Enable Qt based GUI" On) -option(ENABLE_LOTUS_UNIKEY_ENGINE "Build Unikey core + LotusUnikeyEngine bridge in unikey/ (optional Bamboo replacement)" Off) find_package(Fcitx5Core ${REQUIRED_FCITX_VERSION} REQUIRED) find_package(Fcitx5ModuleEmoji REQUIRED) @@ -39,12 +38,7 @@ else() endif() fcitx5_add_i18n_definition() -if(ENABLE_LOTUS_UNIKEY_ENGINE) - add_subdirectory(unikey) -else() - add_subdirectory(bamboo) -endif() - +add_subdirectory(unikey) add_subdirectory(po) add_subdirectory(src) add_subdirectory(data) @@ -70,7 +64,7 @@ fcitx5_translate_desktop_file( install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml" DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) -install(FILES +install(FILES LICENSES/GPL-3.0-or-later.txt LICENSES/LGPL-2.1-or-later.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/${PROJECT_NAME} diff --git a/server/lotus-server.cpp b/server/lotus-server.cpp index 0b2d61ef..2f75fd7a 100644 --- a/server/lotus-server.cpp +++ b/server/lotus-server.cpp @@ -83,7 +83,7 @@ void UinputDevice::send_backspace() { ev[2].value = 0; ev[3].type = EV_SYN; ev[3].code = SYN_REPORT; - write(guard_.get(), ev, sizeof(ev)); + (void)write(guard_.get(), ev, sizeof(ev)); } LibinputContext::LibinputContext(const struct libinput_interface* interface) : udev_(udev_new()) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 215e2840..4ff4d23b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,15 +14,15 @@ add_library(lotus MODULE ${fcitx_lotus_sources}) set_target_properties(lotus PROPERTIES OUTPUT_NAME "lotus") target_link_libraries(lotus - Fcitx5::Core - Fcitx5::Config - Fcitx5::Module::Emoji - lotus-unikey-bridge - Pthread::Pthread - X11::X11 - ) - target_compile_definitions(lotus PRIVATE LOTUS_ENGINE_UNIKEY=1) - target_include_directories(lotus PRIVATE "${PROJECT_SOURCE_DIR}/unikey/core") + Fcitx5::Core + Fcitx5::Config + Fcitx5::Module::Emoji + lotus-unikey-bridge + Pthread::Pthread + X11::X11 +) +target_compile_definitions(lotus PRIVATE LOTUS_ENGINE_UNIKEY=1) +target_include_directories(lotus PRIVATE "${PROJECT_SOURCE_DIR}/unikey/core") target_include_directories(lotus PRIVATE ${PROJECT_BINARY_DIR} diff --git a/src/lotus-bamboo-backend.cpp b/src/lotus-bamboo-backend.cpp deleted file mode 100644 index 5d78641e..00000000 --- a/src/lotus-bamboo-backend.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#ifndef LOTUS_ENGINE_UNIKEY - -#include "lotus-input-backend.hpp" -#include "lotus-config.h" -#include "lotus-engine.h" -#include "lotus.h" - -#include -#include -#include -#include - -namespace fcitx { - -namespace { - -class LotusBambooInputBackend final : public LotusInputBackend { -public: - void recreateEngine(LotusEngine* engine) override { - engine_.reset(); - if (engine->config().inputMethod.value() == "Custom") { - const auto& keymaps = *engine->customKeymap().customKeymap; - std::vector charArray; - charArray.reserve((keymaps.size() * 2) + 1); - for (const auto& keymap : keymaps) { - charArray.push_back(const_cast(keymap.key->data())); // NOLINT - charArray.push_back(const_cast(keymap.value->data())); // NOLINT - } - charArray.push_back(nullptr); - engine_.reset(NewCustomEngine(charArray.data(), engine->dictionary(), engine->macroTable())); - } else { - engine_.reset(NewEngine(engine->config().inputMethod->data(), engine->dictionary(), engine->macroTable())); - } - } - - void setOptions(LotusEngine* engine) override { - if (!engine_) - return; - FcitxBambooEngineOption option = { - .autoNonVnRestore = *engine->config().autoNonVnRestore, - .ddFreeStyle = *engine->config().ddFreeStyle, - .macroEnabled = *engine->config().enableMacro, - .autoCapitalizeMacro = *engine->config().capitalizeMacro, - .spellCheckWithDicts = *engine->config().spellCheck, - .outputCharset = engine->config().outputCharset->data(), - .modernStyle = *engine->config().modernStyle, - .freeMarking = *engine->config().freeMarking, - .w2u = *engine->config().w2u, - .timeFormat = engine->config().timeFormat->data(), - .dateFormat = engine->config().dateFormat->data(), - }; - EngineSetOption(engine_.handle(), &option); - } - - void resetEngine() override { - if (engine_) - ResetEngine(engine_.handle()); - } - - void rebuildFromText(const char* utf8) override { - if (engine_) - EngineRebuildFromText(engine_.handle(), utf8); - } - - bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) override { - if (!engine_) - return false; - char *cr = nullptr, *pr = nullptr; - bool ok = EngineProcessKeyEventAndPull(engine_.handle(), sym, state, &cr, &pr); - if (commit) { - commit->assign(cr ? cr : ""); - } - if (preedit) { - preedit->assign(pr ? pr : ""); - } - std::free(cr); // NOLINT - std::free(pr); // NOLINT - return ok; - } - - bool processKeyEvent(uint32_t sym, uint32_t state) override { - if (!engine_) - return false; - return EngineProcessKeyEvent(engine_.handle(), sym, state); - } - - void pullCommitAndPreedit(std::string* commit, std::string* preedit) override { - if (!engine_) - return; - char *cp = nullptr, *pp = nullptr; - EnginePullCommitAndPreedit(engine_.handle(), &cp, &pp); - if (commit) - commit->assign(cp ? cp : ""); - if (preedit) - preedit->assign(pp ? pp : ""); - std::free(cp); // NOLINT - std::free(pp); // NOLINT - } - - void pullCommit(std::string* out) override { - if (!engine_ || !out) - return; - char* p = EnginePullCommit(engine_.handle()); - out->assign(p ? p : ""); - std::free(p); // NOLINT - } - - void pullPreedit(std::string* out) override { - if (!engine_ || !out) - return; - char* p = EnginePullPreedit(engine_.handle()); - out->assign(p ? p : ""); - std::free(p); // NOLINT - } - - void commitPreedit() override { - if (engine_) - EngineCommitPreedit(engine_.handle()); - } - -private: - CGoObject engine_; -}; - -} // namespace - -std::unique_ptr makeLotusInputBackend() { - return std::make_unique(); -} - -} // namespace fcitx - -#endif // !LOTUS_ENGINE_UNIKEY diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 4663cf77..2f4ae83e 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -116,15 +116,7 @@ namespace fcitx { imNames_ = std::move(imNames); } #else - imNames_ = {"Telex", - "VNI", - "Telex 2", - "Telex + VNI", - "Telex + VNI + VIQR", - "VIQR", - "Microsoft layout", - "VNI Bàn phím tiếng Pháp", - "Custom"}; + imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "Telex + VNI + VIQR", "VIQR", "Microsoft layout", "VNI Bàn phím tiếng Pháp", "Custom"}; #endif config_.inputMethod.annotation().setList(imNames_); diff --git a/src/lotus-engine.h b/src/lotus-engine.h index b6727947..f32f1966 100644 --- a/src/lotus-engine.h +++ b/src/lotus-engine.h @@ -28,7 +28,7 @@ namespace fcitx { - class CGoObject; + class Object; class LotusState; /** @@ -196,7 +196,7 @@ namespace fcitx { lotusCustomKeymap emptyCustomKeymap_; lotusMacroTable macroTables_; - CGoObject macroTableObject_; + Object macroTableObject_; lotusAppRules appRulesTables_; FactoryFor factory_; @@ -217,7 +217,7 @@ namespace fcitx { std::unique_ptr settingsAction_; std::vector toggleActions_; std::vector connections_; - CGoObject dictionary_; + Object dictionary_; std::unordered_map appRules_; std::string appRulesPath_; bool isSelectingAppMode_ = false; @@ -228,7 +228,7 @@ namespace fcitx { mutable std::mutex appRulesMutex_; /** - * @brief Refreshes the bamboo engine with current settings. + * @brief Refreshes the engine with current settings. */ void refreshEngine(); diff --git a/src/lotus-input-backend.hpp b/src/lotus-input-backend.hpp index 7de327ec..99f3bb70 100644 --- a/src/lotus-input-backend.hpp +++ b/src/lotus-input-backend.hpp @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: GPL-3.0-or-later * - * Abstract input engine: Go/Bamboo vs native Unikey (no CGO). + * Abstract input engine: Unikey */ #ifndef FCITX5_LOTUS_INPUT_BACKEND_HPP #define FCITX5_LOTUS_INPUT_BACKEND_HPP @@ -14,28 +14,28 @@ namespace fcitx { -class LotusEngine; + class LotusEngine; -/** + /** * Per-context Vietnamese engine (Bamboo or Unikey implementation). */ -class LotusInputBackend { -public: - virtual ~LotusInputBackend() = default; - - virtual void recreateEngine(LotusEngine* engine) = 0; - virtual void setOptions(LotusEngine* engine) = 0; - virtual void resetEngine() = 0; - virtual void rebuildFromText(const char* utf8) = 0; - virtual bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) = 0; - virtual bool processKeyEvent(uint32_t sym, uint32_t state) = 0; - virtual void pullCommitAndPreedit(std::string* commit, std::string* preedit) = 0; - virtual void pullCommit(std::string* out) = 0; - virtual void pullPreedit(std::string* out) = 0; - virtual void commitPreedit() = 0; -}; - -std::unique_ptr makeLotusInputBackend(); + class LotusInputBackend { + public: + virtual ~LotusInputBackend() = default; + + virtual void recreateEngine(LotusEngine* engine) = 0; + virtual void setOptions(LotusEngine* engine) = 0; + virtual void resetEngine() = 0; + virtual void rebuildFromText(const char* utf8) = 0; + virtual bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) = 0; + virtual bool processKeyEvent(uint32_t sym, uint32_t state) = 0; + virtual void pullCommitAndPreedit(std::string* commit, std::string* preedit) = 0; + virtual void pullCommit(std::string* out) = 0; + virtual void pullPreedit(std::string* out) = 0; + virtual void commitPreedit() = 0; + }; + + std::unique_ptr makeLotusInputBackend(); } // namespace fcitx diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 027f3d2e..db9ebc41 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -77,7 +77,7 @@ namespace fcitx { void LotusState::setEngine() { inputBackend_.reset(); inputBackend_ = makeLotusInputBackend(); - realMode = modeStringToEnum(engine_->config().mode.value()); + realMode = modeStringToEnum(engine_->config().mode.value()); inputBackend_->recreateEngine(engine_); setOption(); @@ -615,7 +615,7 @@ namespace fcitx { if (isBackspace(currentSym) || currentSym == FcitxKey_Return) { if (isBackspace(currentSym)) { - hasHistory_ = true; + hasHistory_ = true; std::string preBs; inputBackend_->processKeyEventAndPull(FcitxKey_BackSpace, 0, nullptr, &preBs); oldPreBuffer_ = preBs; @@ -832,7 +832,7 @@ namespace fcitx { std::string preeditPart; inputBackend_->pullCommitAndPreedit(&commitPart, &preeditPart); - std::string newWord; + std::string newWord; if (!commitPart.empty()) newWord += commitPart; if (!preeditPart.empty()) diff --git a/src/lotus-state.h b/src/lotus-state.h index 6a59b6e6..33c10f0c 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -91,28 +91,28 @@ namespace fcitx { friend class LotusEngine; private: - static constexpr size_t MAX_BUFFERED_KEYS = 50; - - LotusEngine* engine_; - InputContext* ic_; - std::unique_ptr inputBackend_; - std::string oldPreBuffer_; - bool hasHistory_ = false; - int expected_backspaces_ = 0; - int current_backspace_count_ = 0; - std::string pending_commit_string_; - std::atomic current_thread_id_{0}; - std::string emojiBuffer_; - std::vector emojiCandidates_; - bool waitAck_ = false; - std::vector buffered_keys_; ///< Keystrokes buffered during replacement - bool isPrevSpace_ = false; - bool shouldCapitalize_ = false; - bool isPrevPunctuation_ = false; - int64_t lastDeactivateTime_ = 0; - int64_t lastSkippedResetMs_ = 0; - bool wa_flag = false; - bool surrtp = false; + static constexpr size_t MAX_BUFFERED_KEYS = 50; + + LotusEngine* engine_; + InputContext* ic_; + std::unique_ptr inputBackend_; + std::string oldPreBuffer_; + bool hasHistory_ = false; + int expected_backspaces_ = 0; + int current_backspace_count_ = 0; + std::string pending_commit_string_; + std::atomic current_thread_id_{0}; + std::string emojiBuffer_; + std::vector emojiCandidates_; + bool waitAck_ = false; + std::vector buffered_keys_; ///< Keystrokes buffered during replacement + bool isPrevSpace_ = false; + bool shouldCapitalize_ = false; + bool isPrevPunctuation_ = false; + int64_t lastDeactivateTime_ = 0; + int64_t lastSkippedResetMs_ = 0; + bool wa_flag = false; + bool surrtp = false; /** * @brief Connects to the uinput server. diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index 4483959f..fd1ef592 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -23,259 +23,255 @@ namespace fcitx { -namespace { - -static bool isWordBreakSym(unsigned char c) { - static const std::unordered_set WordBreakSyms = { - ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', - }; - return WordBreakSyms.contains(c); -} - -static UkInputMethod mapLotusIm(const std::string& name) { - if (name.find("Telex") != std::string::npos && name.find("VNI") == std::string::npos) - return UkTelex; - if (name.find("VNI") != std::string::npos || name == "VNI") - return UkVni; - if (name.find("VIQR") != std::string::npos) - return UkViqr; - if (name.find("Microsoft") != std::string::npos || name.find("Ms") != std::string::npos) - return UkMsVi; - if (name.find("Simple") != std::string::npos) - return UkSimpleTelex2; - return UkTelex; -} - -static int mapLotusCharset(const std::string& name) { - if (name == "Unicode" || name.empty()) - return CONV_CHARSET_XUTF8; - if (name.find("TCVN") != std::string::npos) - return CONV_CHARSET_TCVN3; - if (name.find("VNI") != std::string::npos && name != "VNI") - return CONV_CHARSET_VNIWIN; - if (name.find("VIQR") != std::string::npos) - return CONV_CHARSET_VIQR; - return CONV_CHARSET_XUTF8; -} - -class LotusUnikeyInputBackend final : public LotusInputBackend { -public: - void recreateEngine(LotusEngine* engine) override { - engineRef_ = engine; - uk_ = std::make_unique<::fcitx::lotus::LotusUnikeyEngine>(); - applyFromConfig(engine); - resetEngine(); - } + namespace { - void setOptions(LotusEngine* engine) override { - applyFromConfig(engine); - } + static bool isWordBreakSym(unsigned char c) { + static const std::unordered_set WordBreakSyms = { + ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', + }; + return WordBreakSyms.contains(c); + } - void resetEngine() override { - pendingPullCommit_.clear(); - preeditStr_.clear(); - lastShiftPressed_ = FcitxKey_None; - lastKeyWithShift_ = false; - autoCommit_ = false; - if (uk_) - uk_->resetBuf(); - } + static UkInputMethod mapLotusIm(const std::string& name) { + if (name.find("Telex") != std::string::npos && name.find("VNI") == std::string::npos) + return UkTelex; + if (name.find("VNI") != std::string::npos || name == "VNI") + return UkVni; + if (name.find("VIQR") != std::string::npos) + return UkViqr; + if (name.find("Microsoft") != std::string::npos || name.find("Ms") != std::string::npos) + return UkMsVi; + if (name.find("Simple") != std::string::npos) + return UkSimpleTelex2; + return UkTelex; + } - void rebuildFromText(const char* utf8) override { - resetEngine(); - if (!uk_ || utf8 == nullptr) - return; - for (auto ucs : utf8::MakeUTF8CharRange(std::string_view(utf8))) { - if (ucs < 128U) - uk_->putChar(static_cast(ucs)); - else - uk_->putChar(ucs); + static int mapLotusCharset(const std::string& name) { + if (name == "Unicode" || name.empty()) + return CONV_CHARSET_XUTF8; + if (name.find("TCVN") != std::string::npos) + return CONV_CHARSET_TCVN3; + if (name.find("VNI") != std::string::npos && name != "VNI") + return CONV_CHARSET_VNIWIN; + if (name.find("VIQR") != std::string::npos) + return CONV_CHARSET_VIQR; + return CONV_CHARSET_XUTF8; } - syncState(FcitxKey_None); - } - bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) override { - pendingPullCommit_.clear(); - bool ok = dispatch(sym, state); - if (commit) - *commit = pendingPullCommit_; - if (preedit) - *preedit = preeditStr_; - pendingPullCommit_.clear(); - return ok; - } + class LotusUnikeyInputBackend final : public LotusInputBackend { + public: + void recreateEngine(LotusEngine* engine) override { + engineRef_ = engine; + uk_ = std::make_unique<::fcitx::lotus::LotusUnikeyEngine>(); + applyFromConfig(engine); + resetEngine(); + } - bool processKeyEvent(uint32_t sym, uint32_t state) override { - pendingPullCommit_.clear(); - return dispatch(sym, state); - } + void setOptions(LotusEngine* engine) override { + applyFromConfig(engine); + } - void pullCommitAndPreedit(std::string* commit, std::string* preedit) override { - if (commit) - *commit = pendingPullCommit_; - if (preedit) - *preedit = preeditStr_; - pendingPullCommit_.clear(); - } + void resetEngine() override { + pendingPullCommit_.clear(); + preeditStr_.clear(); + lastShiftPressed_ = FcitxKey_None; + lastKeyWithShift_ = false; + autoCommit_ = false; + if (uk_) + uk_->resetBuf(); + } - void pullCommit(std::string* out) override { - if (out) - *out = pendingPullCommit_; - pendingPullCommit_.clear(); - } + void rebuildFromText(const char* utf8) override { + resetEngine(); + if (!uk_ || utf8 == nullptr) + return; + for (auto ucs : utf8::MakeUTF8CharRange(std::string_view(utf8))) { + if (ucs < 128U) + uk_->putChar(static_cast(ucs)); + else + uk_->putChar(ucs); + } + syncState(FcitxKey_None); + } - void pullPreedit(std::string* out) override { - if (out) - *out = preeditStr_; - } + bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) override { + pendingPullCommit_.clear(); + bool ok = dispatch(sym, state); + if (commit) + *commit = pendingPullCommit_; + if (preedit) + *preedit = preeditStr_; + pendingPullCommit_.clear(); + return ok; + } - void commitPreedit() override { - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; - preeditStr_.clear(); - if (uk_) - uk_->resetBuf(); - } + bool processKeyEvent(uint32_t sym, uint32_t state) override { + pendingPullCommit_.clear(); + return dispatch(sym, state); + } -private: - void applyFromConfig(LotusEngine* engine) { - if (!uk_) - return; - UkInputMethod im = mapLotusIm(engine->config().inputMethod.value()); - uk_->setInputMethod(im); - uk_->setOutputCharset(mapLotusCharset(engine->config().outputCharset.value())); - UnikeyOptions opt{}; - opt.freeMarking = *engine->config().freeMarking ? 1 : 0; - opt.modernStyle = *engine->config().modernStyle ? 1 : 0; - opt.macroEnabled = *engine->config().enableMacro ? 1 : 0; - opt.useUnicodeClipboard = 0; - opt.alwaysMacro = 0; - opt.strictSpellCheck = 0; - opt.useIME = 0; - opt.spellCheckEnabled = *engine->config().spellCheck ? 1 : 0; - opt.autoNonVnRestore = *engine->config().autoNonVnRestore ? 1 : 0; - uk_->setOptions(&opt); - } + void pullCommitAndPreedit(std::string* commit, std::string* preedit) override { + if (commit) + *commit = pendingPullCommit_; + if (preedit) + *preedit = preeditStr_; + pendingPullCommit_.clear(); + } - void eraseChars(int num_chars) { - int i; - int k = num_chars; - unsigned char c = 0; - for (i = static_cast(preeditStr_.length()) - 1; i >= 0 && k > 0; --i) { - c = preeditStr_.at(static_cast(i)); - if (c < (unsigned char)'\x80' || c >= (unsigned char)'\xC0') - --k; - } - preeditStr_.erase(static_cast(i + 1)); - } + void pullCommit(std::string* out) override { + if (out) + *out = pendingPullCommit_; + pendingPullCommit_.clear(); + } - void syncState(KeySym sym) { - auto* uic = uk_->context(); - if (uic->backspaces() > 0) { - if (static_cast(preeditStr_.length()) <= uic->backspaces()) - preeditStr_.clear(); - else - eraseChars(uic->backspaces()); - } - if (uic->bufChars() > 0) { - preeditStr_.append(reinterpret_cast(uic->buf()), - static_cast(uic->bufChars())); - } else if (sym != FcitxKey_Shift_L && sym != FcitxKey_Shift_R && sym != FcitxKey_None) { - preeditStr_.append(utf8::UCS4ToUTF8(sym)); - } - } + void pullPreedit(std::string* out) override { + if (out) + *out = preeditStr_; + } - bool dispatch(uint32_t sym, uint32_t state) { - if (!uk_) - return false; - - KeyStates st(static_cast(state)); - const auto rawSym = static_cast(sym); - - if (st.testAny(KeyState::Ctrl_Alt) || rawSym == FcitxKey_Control_L || rawSym == FcitxKey_Control_R || - rawSym == FcitxKey_Tab || rawSym == FcitxKey_Return || rawSym == FcitxKey_Delete || - rawSym == FcitxKey_KP_Enter || (rawSym >= FcitxKey_Home && rawSym <= FcitxKey_Insert) || - (rawSym >= FcitxKey_KP_Home && rawSym <= FcitxKey_KP_Delete)) { - uk_->context()->filter(0); - syncState(rawSym); - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; - preeditStr_.clear(); - uk_->resetBuf(); - return false; - } - if (st.test(KeyState::Super)) - return false; - if ((rawSym >= FcitxKey_Caps_Lock && rawSym <= FcitxKey_Hyper_R) || rawSym == FcitxKey_Shift_L || - rawSym == FcitxKey_Shift_R) - return false; - - if (rawSym == FcitxKey_BackSpace) { - uk_->backspacePress(); - if (uk_->context()->backspaces() == 0 || preeditStr_.empty()) { + void commitPreedit() override { if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - uk_->resetBuf(); - return true; + if (uk_) + uk_->resetBuf(); } - if (static_cast(preeditStr_.length()) <= uk_->context()->backspaces()) - preeditStr_.clear(); - else - eraseChars(uk_->context()->backspaces()); - if (uk_->context()->bufChars() > 0) - preeditStr_.append(reinterpret_cast(uk_->context()->buf()), - static_cast(uk_->context()->bufChars())); - return true; - } - if (rawSym >= FcitxKey_KP_Multiply && rawSym <= FcitxKey_KP_9) { - uk_->context()->filter(0); - syncState(rawSym); - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; - preeditStr_.clear(); - uk_->resetBuf(); - return false; - } + private: + void applyFromConfig(LotusEngine* engine) { + if (!uk_) + return; + UkInputMethod im = mapLotusIm(engine->config().inputMethod.value()); + uk_->setInputMethod(im); + uk_->setOutputCharset(mapLotusCharset(engine->config().outputCharset.value())); + UnikeyOptions opt{}; + opt.freeMarking = *engine->config().freeMarking ? 1 : 0; + opt.modernStyle = *engine->config().modernStyle ? 1 : 0; + opt.macroEnabled = *engine->config().enableMacro ? 1 : 0; + opt.useUnicodeClipboard = 0; + opt.alwaysMacro = 0; + opt.strictSpellCheck = 0; + opt.useIME = 0; + opt.spellCheckEnabled = *engine->config().spellCheck ? 1 : 0; + opt.autoNonVnRestore = *engine->config().autoNonVnRestore ? 1 : 0; + uk_->setOptions(&opt); + } + + void eraseChars(int num_chars) { + int i; + int k = num_chars; + unsigned char c = 0; + for (i = static_cast(preeditStr_.length()) - 1; i >= 0 && k > 0; --i) { + c = preeditStr_.at(static_cast(i)); + if (c < (unsigned char)'\x80' || c >= (unsigned char)'\xC0') + --k; + } + preeditStr_.erase(static_cast(i + 1)); + } - if (rawSym >= FcitxKey_space && rawSym <= FcitxKey_asciitilde) { - uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); - uk_->filter(sym); - syncState(rawSym); + void syncState(KeySym sym) { + auto* uic = uk_->context(); + if (uic->backspaces() > 0) { + if (static_cast(preeditStr_.length()) <= uic->backspaces()) + preeditStr_.clear(); + else + eraseChars(uic->backspaces()); + } + if (uic->bufChars() > 0) { + preeditStr_.append(reinterpret_cast(uic->buf()), static_cast(uic->bufChars())); + } else if (sym != FcitxKey_Shift_L && sym != FcitxKey_Shift_R && sym != FcitxKey_None) { + preeditStr_.append(utf8::UCS4ToUTF8(sym)); + } + } - if (!preeditStr_.empty() && preeditStr_.back() == static_cast(sym) && isWordBreakSym(static_cast(sym))) { - pendingPullCommit_ = preeditStr_; + bool dispatch(uint32_t sym, uint32_t state) { + if (!uk_) + return false; + + KeyStates st(static_cast(state)); + const auto rawSym = static_cast(sym); + + if (st.testAny(KeyState::Ctrl_Alt) || rawSym == FcitxKey_Control_L || rawSym == FcitxKey_Control_R || rawSym == FcitxKey_Tab || rawSym == FcitxKey_Return || + rawSym == FcitxKey_Delete || rawSym == FcitxKey_KP_Enter || (rawSym >= FcitxKey_Home && rawSym <= FcitxKey_Insert) || + (rawSym >= FcitxKey_KP_Home && rawSym <= FcitxKey_KP_Delete)) { + uk_->context()->filter(0); + syncState(rawSym); + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return false; + } + if (st.test(KeyState::Super)) + return false; + if ((rawSym >= FcitxKey_Caps_Lock && rawSym <= FcitxKey_Hyper_R) || rawSym == FcitxKey_Shift_L || rawSym == FcitxKey_Shift_R) + return false; + + if (rawSym == FcitxKey_BackSpace) { + uk_->backspacePress(); + if (uk_->context()->backspaces() == 0 || preeditStr_.empty()) { + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return true; + } + if (static_cast(preeditStr_.length()) <= uk_->context()->backspaces()) + preeditStr_.clear(); + else + eraseChars(uk_->context()->backspaces()); + if (uk_->context()->bufChars() > 0) + preeditStr_.append(reinterpret_cast(uk_->context()->buf()), static_cast(uk_->context()->bufChars())); + return true; + } + + if (rawSym >= FcitxKey_KP_Multiply && rawSym <= FcitxKey_KP_9) { + uk_->context()->filter(0); + syncState(rawSym); + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return false; + } + + if (rawSym >= FcitxKey_space && rawSym <= FcitxKey_asciitilde) { + uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); + uk_->filter(sym); + syncState(rawSym); + + if (!preeditStr_.empty() && preeditStr_.back() == static_cast(sym) && isWordBreakSym(static_cast(sym))) { + pendingPullCommit_ = preeditStr_; + preeditStr_.clear(); + uk_->resetBuf(); + return true; + } + return true; + } + + uk_->context()->filter(0); + syncState(rawSym); + if (!preeditStr_.empty()) + pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); - return true; + return false; } - return true; - } - uk_->context()->filter(0); - syncState(rawSym); - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; - preeditStr_.clear(); - uk_->resetBuf(); - return false; - } + std::unique_ptr<::fcitx::lotus::LotusUnikeyEngine> uk_; + LotusEngine* engineRef_ = nullptr; + std::string preeditStr_; + std::string pendingPullCommit_; + KeySym lastShiftPressed_ = FcitxKey_None; + bool lastKeyWithShift_ = false; + bool autoCommit_ = false; + }; - std::unique_ptr<::fcitx::lotus::LotusUnikeyEngine> uk_; - LotusEngine* engineRef_ = nullptr; - std::string preeditStr_; - std::string pendingPullCommit_; - KeySym lastShiftPressed_ = FcitxKey_None; - bool lastKeyWithShift_ = false; - bool autoCommit_ = false; -}; + } // namespace -} // namespace - -std::unique_ptr makeLotusInputBackend() { - return std::make_unique(); -} + std::unique_ptr makeLotusInputBackend() { + return std::make_unique(); + } } // namespace fcitx diff --git a/src/lotus.h b/src/lotus.h index c6379113..96fc6df1 100644 --- a/src/lotus.h +++ b/src/lotus.h @@ -10,128 +10,56 @@ #ifndef _FCITX5_LOTUS_H_ #define _FCITX5_LOTUS_H_ -#include - -#ifndef LOTUS_ENGINE_UNIKEY -#include "bamboo-core.h" +#include +#include namespace fcitx { - class LotusEngine; - class LotusState; - - /** - * RAII wrapper for CGo handles (Bamboo/Go engine). - */ - class CGoObject { - public: - CGoObject(std::optional handle = std::nullopt) : handle_(handle) {} - - ~CGoObject() { - if (handle_) { - DeleteObject(*handle_); - } - } +class LotusEngine; +class LotusState; - CGoObject(const CGoObject&) = delete; - CGoObject& operator=(const CGoObject&) = delete; +class Object { +public: + Object() noexcept = default; - CGoObject(CGoObject&& other) noexcept : handle_(other.handle_) { - other.handle_ = std::nullopt; - } + explicit Object(uintptr_t value) noexcept + : value_(value) {} - CGoObject& operator=(CGoObject&& other) noexcept { - if (this != &other) { - clear(); - handle_ = other.handle_; - other.handle_ = std::nullopt; - } - return *this; - } + ~Object() = default; - void reset(std::optional handle = std::nullopt) { - clear(); - handle_ = handle; - } - - uintptr_t handle() const { - return handle_.value_or(0); - } - - uintptr_t release() { - if (handle_) { - uintptr_t v = *handle_; - handle_ = std::nullopt; - return v; - } - return 0; - } + Object(const Object&) = delete; + Object& operator=(const Object&) = delete; - explicit operator bool() const { - return handle_.has_value() && *handle_ != 0; - } + Object(Object&& other) noexcept + : value_(std::exchange(other.value_, 0)) {} - private: - void clear() { - if (handle_) { - DeleteObject(*handle_); - handle_ = std::nullopt; - } + Object& operator=(Object&& other) noexcept { + if (this != &other) { + value_ = std::exchange(other.value_, 0); } + return *this; + } - std::optional handle_; - }; + void reset(uintptr_t value = 0) noexcept { + value_ = value; + } -} // namespace fcitx + [[nodiscard]] uintptr_t handle() const noexcept { + return value_; + } -#else + [[nodiscard]] uintptr_t release() noexcept { + return std::exchange(value_, 0); + } -namespace fcitx { + explicit operator bool() const noexcept { + return value_ != 0; + } - class LotusEngine; - class LotusState; - - /** Stub when Bamboo/Go is disabled (Unikey engine): no runtime handles. */ - class CGoObject { - public: - CGoObject(std::optional handle = std::nullopt) : handle_(handle) {} - ~CGoObject() = default; - CGoObject(const CGoObject&) = delete; - CGoObject& operator=(const CGoObject&) = delete; - CGoObject(CGoObject&& other) noexcept : handle_(other.handle_) { - other.handle_ = std::nullopt; - } - CGoObject& operator=(CGoObject&& other) noexcept { - if (this != &other) { - handle_ = other.handle_; - other.handle_ = std::nullopt; - } - return *this; - } - void reset(std::optional handle = std::nullopt) { - handle_ = handle; - } - uintptr_t handle() const { - return handle_.value_or(0); - } - uintptr_t release() { - if (handle_) { - uintptr_t v = *handle_; - handle_ = std::nullopt; - return v; - } - return 0; - } - explicit operator bool() const { - return handle_.has_value() && *handle_ != 0; - } - - private: - std::optional handle_; - }; +private: + uintptr_t value_ = 0; +}; } // namespace fcitx -#endif - #endif // _FCITX5_LOTUS_H_ diff --git a/unikey/CMakeLists.txt b/unikey/CMakeLists.txt index 8e0cefcb..f64bed99 100644 --- a/unikey/CMakeLists.txt +++ b/unikey/CMakeLists.txt @@ -1,4 +1,4 @@ -# Vendored Unikey engine (core/) + Lotus wrapper. Optional Bamboo replacement. +# Vendored Unikey engine (core/) + Lotus wrapper # SPDX-FileCopyrightText: Unikey authors (LGPL/GPL); Lotus wrapper GPL-3.0-or-later set(_UK_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") set(LOTUS_UNIKEY_CORE_SRCS diff --git a/unikey/LotusUnikeyEngine.hpp b/unikey/LotusUnikeyEngine.hpp index a8f934d3..0055ba53 100644 --- a/unikey/LotusUnikeyEngine.hpp +++ b/unikey/LotusUnikeyEngine.hpp @@ -3,12 +3,11 @@ * * SPDX-License-Identifier: GPL-3.0-or-later * - * Thin wrapper around fcitx5-unikey's UkEngine stack (UnikeyInputMethod + - * UnikeyInputContext). Intended to replace the Go/Bamboo engine when + * Thin wrapper around fcitx5-unikey's UkEngine stack * LOTUS_USE_UNIKEY is wired through LotusState. */ -// #ifndef FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP -// #define FCITX5_LOTUS_LOTUS_ENGINE_HPP +#ifndef FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP +#define FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP #include "keycons.h" #include "vnlexi.h" @@ -60,4 +59,4 @@ class LotusUnikeyEngine { } // namespace fcitx::lotus -//#endif +#endif diff --git a/unikey/core/inputproc.cpp b/unikey/core/inputproc.cpp index cafa4199..dbc9cc5b 100644 --- a/unikey/core/inputproc.cpp +++ b/unikey/core/inputproc.cpp @@ -17,6 +17,11 @@ unsigned char WordBreakSyms[] = { '_', '~', '`', '@', '#', '$', '%', '^', '&', '(', ')', '{', '}', '[', ']'}; */ +constexpr UkKeyEvName lexi(VnLexiName v) { + return static_cast( + static_cast(vneCount) + static_cast(v)); +} + const std::unordered_set WordBreakSyms = { ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', '<', '>', '=', '+', '-', '*', '/', '\\', '_', '@', '#', @@ -67,10 +72,10 @@ DllExport UkKeyMapping TelexMethodMapping[] = {{'Z', vneTone0}, {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, - {'[', vneCount + vnl_oh}, - {']', vneCount + vnl_uh}, - {'{', vneCount + vnl_Oh}, - {'}', vneCount + vnl_Uh}, + {'[', lexi(vnl_oh)}, + {']', lexi(vnl_uh)}, + {'{', lexi(vnl_Oh)}, + {'}', lexi(vnl_Uh)}, {0, vneNormal}}; DllExport UkKeyMapping SimpleTelexMethodMapping[] = { @@ -104,20 +109,20 @@ DllExport UkKeyMapping MsViMethodMapping[] = {{'5', vneTone2}, {'*', vneTone1}, {'9', vneTone5}, {'(', vneTone5}, - {'1', vneCount + vnl_ab}, - {'!', vneCount + vnl_Ab}, - {'2', vneCount + vnl_ar}, - {'@', vneCount + vnl_Ar}, - {'3', vneCount + vnl_er}, - {'#', vneCount + vnl_Er}, - {'4', vneCount + vnl_or}, - {'$', vneCount + vnl_Or}, - {'0', vneCount + vnl_dd}, - {')', vneCount + vnl_DD}, - {'[', vneCount + vnl_uh}, - {']', vneCount + vnl_oh}, - {'{', vneCount + vnl_Uh}, - {'}', vneCount + vnl_Oh}, + {'1', lexi(vnl_ab)}, + {'!', lexi(vnl_Ab)}, + {'2', lexi(vnl_ar)}, + {'@', lexi(vnl_Ar)}, + {'3', lexi(vnl_er)}, + {'#', lexi(vnl_Er)}, + {'4', lexi(vnl_or)}, + {'$', lexi(vnl_Or)}, + {'0', lexi(vnl_dd)}, + {')', lexi(vnl_DD)}, + {'[', lexi(vnl_uh)}, + {']', lexi(vnl_oh)}, + {'{', lexi(vnl_Uh)}, + {'}', lexi(vnl_Oh)}, {0, vneNormal}}; //------------------------------------------- diff --git a/unikey/core/usrkeymap.cpp b/unikey/core/usrkeymap.cpp index 03e8b222..557f753d 100644 --- a/unikey/core/usrkeymap.cpp +++ b/unikey/core/usrkeymap.cpp @@ -28,6 +28,11 @@ struct UkEventLabelPair { const char *UkKeyMapHeader = "; This is UniKey user-defined key mapping file, " "generated from UniKey (Fcitx 5)\n\n"; +constexpr UkKeyEvName lexi(VnLexiName v) { + return static_cast( + static_cast(vneCount) + static_cast(v)); +} + constexpr UkEventLabelPair UkEvLabelList[] = { {"Tone0", vneTone0}, {"Tone1", vneTone1}, {"Tone2", vneTone2}, {"Tone3", vneTone3}, @@ -38,13 +43,15 @@ constexpr UkEventLabelPair UkEvLabelList[] = { {"Hook-U", vneHook_u}, {"Hook-O", vneHook_o}, {"Bowl", vneBowl}, {"D-Mark", vneDd}, {"Telex-W", vne_telex_w}, {"Escape", vneEscChar}, - {"DD", vneCount + vnl_DD}, {"dd", vneCount + vnl_dd}, - {"A^", vneCount + vnl_Ar}, {"a^", vneCount + vnl_ar}, - {"A(", vneCount + vnl_Ab}, {"a(", vneCount + vnl_ab}, - {"E^", vneCount + vnl_Er}, {"e^", vneCount + vnl_er}, - {"O^", vneCount + vnl_Or}, {"o^", vneCount + vnl_or}, - {"O+", vneCount + vnl_Oh}, {"o+", vneCount + vnl_oh}, - {"U+", vneCount + vnl_Uh}, {"u+", vneCount + vnl_uh}}; + + {"DD", lexi(vnl_DD)}, {"dd", lexi(vnl_dd)}, + {"A^", lexi(vnl_Ar)}, {"a^", lexi(vnl_ar)}, + {"A(", lexi(vnl_Ab)}, {"a(", lexi(vnl_ab)}, + {"E^", lexi(vnl_Er)}, {"e^", lexi(vnl_er)}, + {"O^", lexi(vnl_Or)}, {"o^", lexi(vnl_or)}, + {"O+", lexi(vnl_Oh)}, {"o+", lexi(vnl_oh)}, + {"U+", lexi(vnl_Uh)}, {"u+", lexi(vnl_uh)}, +}; constexpr auto UkEvLabelCount = FCITX_ARRAY_SIZE(UkEvLabelList); From 398be9017657523db44b0663ca629b586d02b50d Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 8 May 2026 03:36:02 +0700 Subject: [PATCH 15/42] clang fmt Signed-off-by: Zebra2711 --- src/lotus.h | 62 +- unikey/LotusUnikeyEngine.cpp | 102 +- unikey/LotusUnikeyEngine.hpp | 70 +- unikey/core/byteio.cpp | 166 +- unikey/core/byteio.h | 146 +- unikey/core/charset.cpp | 583 +++---- unikey/core/charset.h | 244 ++- unikey/core/convert.cpp | 53 +- unikey/core/data.cpp | 599 +++---- unikey/core/data.h | 9 +- unikey/core/inputproc.cpp | 173 +- unikey/core/inputproc.h | 51 +- unikey/core/keycons.h | 31 +- unikey/core/mactab.cpp | 121 +- unikey/core/mactab.h | 38 +- unikey/core/pattern.cpp | 12 +- unikey/core/pattern.h | 31 +- unikey/core/ukengine.cpp | 2409 ++++++++++------------------ unikey/core/ukengine.h | 129 +- unikey/core/unikeyinputcontext.cpp | 53 +- unikey/core/unikeyinputcontext.h | 44 +- unikey/core/usrkeymap.cpp | 106 +- unikey/core/usrkeymap.h | 3 +- unikey/core/vnconv.h | 60 +- 24 files changed, 2139 insertions(+), 3156 deletions(-) diff --git a/src/lotus.h b/src/lotus.h index 96fc6df1..49f6947c 100644 --- a/src/lotus.h +++ b/src/lotus.h @@ -15,50 +15,48 @@ namespace fcitx { -class LotusEngine; -class LotusState; + class LotusEngine; + class LotusState; -class Object { -public: - Object() noexcept = default; + class Object { + public: + Object() noexcept = default; - explicit Object(uintptr_t value) noexcept - : value_(value) {} + explicit Object(uintptr_t value) noexcept : value_(value) {} - ~Object() = default; + ~Object() = default; - Object(const Object&) = delete; - Object& operator=(const Object&) = delete; + Object(const Object&) = delete; + Object& operator=(const Object&) = delete; - Object(Object&& other) noexcept - : value_(std::exchange(other.value_, 0)) {} + Object(Object&& other) noexcept : value_(std::exchange(other.value_, 0)) {} - Object& operator=(Object&& other) noexcept { - if (this != &other) { - value_ = std::exchange(other.value_, 0); + Object& operator=(Object&& other) noexcept { + if (this != &other) { + value_ = std::exchange(other.value_, 0); + } + return *this; } - return *this; - } - void reset(uintptr_t value = 0) noexcept { - value_ = value; - } + void reset(uintptr_t value = 0) noexcept { + value_ = value; + } - [[nodiscard]] uintptr_t handle() const noexcept { - return value_; - } + [[nodiscard]] uintptr_t handle() const noexcept { + return value_; + } - [[nodiscard]] uintptr_t release() noexcept { - return std::exchange(value_, 0); - } + [[nodiscard]] uintptr_t release() noexcept { + return std::exchange(value_, 0); + } - explicit operator bool() const noexcept { - return value_ != 0; - } + explicit operator bool() const noexcept { + return value_ != 0; + } -private: - uintptr_t value_ = 0; -}; + private: + uintptr_t value_ = 0; + }; } // namespace fcitx diff --git a/unikey/LotusUnikeyEngine.cpp b/unikey/LotusUnikeyEngine.cpp index ed0a77da..09d615a1 100644 --- a/unikey/LotusUnikeyEngine.cpp +++ b/unikey/LotusUnikeyEngine.cpp @@ -9,74 +9,72 @@ namespace fcitx::lotus { -LotusUnikeyEngine::LotusUnikeyEngine() - : im_(std::make_unique()) - , uic_(std::make_unique(im_.get())) {} + LotusUnikeyEngine::LotusUnikeyEngine() : im_(std::make_unique()), uic_(std::make_unique(im_.get())) {} -LotusUnikeyEngine::~LotusUnikeyEngine() = default; + LotusUnikeyEngine::~LotusUnikeyEngine() = default; -void LotusUnikeyEngine::setInputMethod(UkInputMethod im) { - im_->setInputMethod(im); -} + void LotusUnikeyEngine::setInputMethod(UkInputMethod im) { + im_->setInputMethod(im); + } -void LotusUnikeyEngine::setOutputCharset(int charsetId) { - im_->setOutputCharset(charsetId); -} + void LotusUnikeyEngine::setOutputCharset(int charsetId) { + im_->setOutputCharset(charsetId); + } -void LotusUnikeyEngine::setOptions(UnikeyOptions* opt) { - im_->setOptions(opt); -} + void LotusUnikeyEngine::setOptions(UnikeyOptions* opt) { + im_->setOptions(opt); + } -void LotusUnikeyEngine::resetBuf() { - uic_->resetBuf(); -} + void LotusUnikeyEngine::resetBuf() { + uic_->resetBuf(); + } -void LotusUnikeyEngine::setCapsState(int shiftPressed, int capsLockOn) { - uic_->setCapsState(shiftPressed, capsLockOn); -} + void LotusUnikeyEngine::setCapsState(int shiftPressed, int capsLockOn) { + uic_->setCapsState(shiftPressed, capsLockOn); + } -void LotusUnikeyEngine::filter(std::uint32_t unikeyKeyCode) { - uic_->filter(unikeyKeyCode); -} + void LotusUnikeyEngine::filter(std::uint32_t unikeyKeyCode) { + uic_->filter(unikeyKeyCode); + } -void LotusUnikeyEngine::putChar(std::uint32_t ch) { - uic_->putChar(ch); -} + void LotusUnikeyEngine::putChar(std::uint32_t ch) { + uic_->putChar(ch); + } -void LotusUnikeyEngine::rebuildChar(VnLexiName ch) { - uic_->rebuildChar(ch); -} + void LotusUnikeyEngine::rebuildChar(VnLexiName ch) { + uic_->rebuildChar(ch); + } -void LotusUnikeyEngine::backspacePress() { - uic_->backspacePress(); -} + void LotusUnikeyEngine::backspacePress() { + uic_->backspacePress(); + } -void LotusUnikeyEngine::restoreKeyStrokes() { - uic_->restoreKeyStrokes(); -} + void LotusUnikeyEngine::restoreKeyStrokes() { + uic_->restoreKeyStrokes(); + } -bool LotusUnikeyEngine::isAtWordBeginning() const { - return uic_->isAtWordBeginning(); -} + bool LotusUnikeyEngine::isAtWordBeginning() const { + return uic_->isAtWordBeginning(); + } -int LotusUnikeyEngine::backspaces() const { - return uic_->backspaces(); -} + int LotusUnikeyEngine::backspaces() const { + return uic_->backspaces(); + } -int LotusUnikeyEngine::bufChars() const { - return uic_->bufChars(); -} + int LotusUnikeyEngine::bufChars() const { + return uic_->bufChars(); + } -const unsigned char* LotusUnikeyEngine::buf() const { - return uic_->buf(); -} + const unsigned char* LotusUnikeyEngine::buf() const { + return uic_->buf(); + } -UnikeyInputMethod* LotusUnikeyEngine::inputMethod() { - return im_.get(); -} + UnikeyInputMethod* LotusUnikeyEngine::inputMethod() { + return im_.get(); + } -UnikeyInputContext* LotusUnikeyEngine::context() { - return uic_.get(); -} + UnikeyInputContext* LotusUnikeyEngine::context() { + return uic_.get(); + } } // namespace fcitx::lotus diff --git a/unikey/LotusUnikeyEngine.hpp b/unikey/LotusUnikeyEngine.hpp index 0055ba53..f70d917e 100644 --- a/unikey/LotusUnikeyEngine.hpp +++ b/unikey/LotusUnikeyEngine.hpp @@ -21,41 +21,41 @@ class UnikeyInputContext; namespace fcitx::lotus { -class LotusUnikeyEngine { -public: - LotusUnikeyEngine(); - ~LotusUnikeyEngine(); - - LotusUnikeyEngine(const LotusUnikeyEngine&) = delete; - LotusUnikeyEngine& operator=(const LotusUnikeyEngine&) = delete; - LotusUnikeyEngine(LotusUnikeyEngine&&) = delete; - LotusUnikeyEngine& operator=(LotusUnikeyEngine&&) = delete; - - void setInputMethod(UkInputMethod im); - void setOutputCharset(int charsetId); - void setOptions(UnikeyOptions* opt); - - void resetBuf(); - void setCapsState(int shiftPressed, int capsLockOn); - void filter(std::uint32_t unikeyKeyCode); - void putChar(std::uint32_t ch); - void rebuildChar(VnLexiName ch); - void backspacePress(); - void restoreKeyStrokes(); - - bool isAtWordBeginning() const; - - int backspaces() const; - int bufChars() const; - const unsigned char* buf() const; - - UnikeyInputMethod* inputMethod(); - UnikeyInputContext* context(); - -private: - std::unique_ptr im_; - std::unique_ptr uic_; -}; + class LotusUnikeyEngine { + public: + LotusUnikeyEngine(); + ~LotusUnikeyEngine(); + + LotusUnikeyEngine(const LotusUnikeyEngine&) = delete; + LotusUnikeyEngine& operator=(const LotusUnikeyEngine&) = delete; + LotusUnikeyEngine(LotusUnikeyEngine&&) = delete; + LotusUnikeyEngine& operator=(LotusUnikeyEngine&&) = delete; + + void setInputMethod(UkInputMethod im); + void setOutputCharset(int charsetId); + void setOptions(UnikeyOptions* opt); + + void resetBuf(); + void setCapsState(int shiftPressed, int capsLockOn); + void filter(std::uint32_t unikeyKeyCode); + void putChar(std::uint32_t ch); + void rebuildChar(VnLexiName ch); + void backspacePress(); + void restoreKeyStrokes(); + + bool isAtWordBeginning() const; + + int backspaces() const; + int bufChars() const; + const unsigned char* buf() const; + + UnikeyInputMethod* inputMethod(); + UnikeyInputContext* context(); + + private: + std::unique_ptr im_; + std::unique_ptr uic_; + }; } // namespace fcitx::lotus diff --git a/unikey/core/byteio.cpp b/unikey/core/byteio.cpp index 81fb773c..12c1397b 100644 --- a/unikey/core/byteio.cpp +++ b/unikey/core/byteio.cpp @@ -7,14 +7,14 @@ #include //------------------------------------------------ -StringBIStream::StringBIStream(UKBYTE *data, int len, int elementSize) { +StringBIStream::StringBIStream(UKBYTE* data, int len, int elementSize) { m_data = m_current = data; m_len = m_left = len; if (len == -1) { if (elementSize == 2) - m_eos = (*(UKWORD *)data == 0); + m_eos = (*(UKWORD*)data == 0); else if (elementSize == 4) - m_eos = (*(UKDWORD *)data == 4); + m_eos = (*(UKDWORD*)data == 4); else m_eos = (*data == 0); } else @@ -23,10 +23,12 @@ StringBIStream::StringBIStream(UKBYTE *data, int len, int elementSize) { } //------------------------------------------------ -int StringBIStream::eos() { return m_eos; } +int StringBIStream::eos() { + return m_eos; +} //------------------------------------------------ -int StringBIStream::getNext(UKBYTE &b) { +int StringBIStream::getNext(UKBYTE& b) { if (m_eos) return 0; b = *m_current++; @@ -43,7 +45,7 @@ int StringBIStream::getNext(UKBYTE &b) { int StringBIStream::unget(UKBYTE b) { if (m_current != m_data) { *--m_current = b; - m_eos = 0; + m_eos = 0; if (m_len != -1) m_left++; } @@ -51,10 +53,10 @@ int StringBIStream::unget(UKBYTE b) { } //------------------------------------------------ -int StringBIStream::getNextW(UKWORD &w) { +int StringBIStream::getNextW(UKWORD& w) { if (m_eos) return 0; - w = *((UKWORD *)m_current); + w = *((UKWORD*)m_current); m_current += 2; if (m_len == -1) m_eos = (w == 0); @@ -66,11 +68,11 @@ int StringBIStream::getNextW(UKWORD &w) { } //------------------------------------------------ -int StringBIStream::getNextDW(UKDWORD &dw) { +int StringBIStream::getNextDW(UKDWORD& dw) { if (m_eos) return 0; - dw = *((UKDWORD *)m_current); + dw = *((UKDWORD*)m_current); m_current += 4; if (m_len == -1) m_eos = (dw == 0); @@ -82,7 +84,7 @@ int StringBIStream::getNextDW(UKDWORD &dw) { } //------------------------------------------------ -int StringBIStream::peekNext(UKBYTE &b) { +int StringBIStream::peekNext(UKBYTE& b) { if (m_eos) return 0; b = *m_current; @@ -90,10 +92,10 @@ int StringBIStream::peekNext(UKBYTE &b) { } //------------------------------------------------ -int StringBIStream::peekNextW(UKWORD &w) { +int StringBIStream::peekNextW(UKWORD& w) { if (m_eos) return 0; - w = *((UKWORD *)m_current); + w = *((UKWORD*)m_current); return 1; } @@ -111,7 +113,7 @@ int StringBIStream::peekNextDW(UKDWORD & dw) //------------------------------------------------ void StringBIStream::reopen() { m_current = m_data; - m_left = m_len; + m_left = m_len; if (m_len == -1) m_eos = (m_data == 0); else @@ -121,12 +123,12 @@ void StringBIStream::reopen() { //------------------------------------------------ int StringBIStream::bookmark() { - m_didBookmark = 1; + m_didBookmark = 1; m_bookmark.current = m_current; - m_bookmark.data = m_data; - m_bookmark.eos = m_eos; - m_bookmark.left = m_left; - m_bookmark.len = m_len; + m_bookmark.data = m_data; + m_bookmark.eos = m_eos; + m_bookmark.left = m_left; + m_bookmark.len = m_len; return 1; } @@ -135,26 +137,28 @@ int StringBIStream::gotoBookmark() { if (!m_didBookmark) return 0; m_current = m_bookmark.current; - m_data = m_bookmark.data; - m_eos = m_bookmark.eos; - m_left = m_bookmark.left; - m_len = m_bookmark.len; + m_data = m_bookmark.data; + m_eos = m_bookmark.eos; + m_left = m_bookmark.left; + m_len = m_bookmark.len; return 1; } //------------------------------------------------ -int StringBIStream::close() { return 1; }; +int StringBIStream::close() { + return 1; +}; ////////////////////////////////////////////////// // Class StringBOStream ////////////////////////////////////////////////// //------------------------------------------------ -StringBOStream::StringBOStream(UKBYTE *buf, int len) { +StringBOStream::StringBOStream(UKBYTE* buf, int len) { m_current = m_buf = buf; - m_len = len; - m_out = 0; - m_bad = 0; + m_len = len; + m_out = 0; + m_bad = 0; } //------------------------------------------------ @@ -188,7 +192,7 @@ int StringBOStream::putW(UKWORD w) { if (m_bad) return 0; if (m_out <= m_len) { - *((UKWORD *)m_current) = w; + *((UKWORD*)m_current) = w; m_current += 2; return 1; } @@ -197,7 +201,7 @@ int StringBOStream::putW(UKWORD w) { } //------------------------------------------------ -int StringBOStream::puts(const char *s, int size) { +int StringBOStream::puts(const char* s, int size) { if (size == -1) { while (*s) { m_out++; @@ -228,26 +232,28 @@ int StringBOStream::puts(const char *s, int size) { //------------------------------------------------ void StringBOStream::reopen() { m_current = m_buf; - m_out = 0; - m_bad = 0; + m_out = 0; + m_bad = 0; } //------------------------------------------------ -int StringBOStream::isOK() { return !m_bad; } +int StringBOStream::isOK() { + return !m_bad; +} //////////////////////////////////////////////////// // Class FileBIStream // //////////////////////////////////////////////////// //---------------------------------------------------- -FileBIStream::FileBIStream(int bufSize, char *buf) { - m_file = NULL; - m_buf = buf; - m_bufSize = bufSize; - m_own = 1; +FileBIStream::FileBIStream(int bufSize, char* buf) { + m_file = NULL; + m_buf = buf; + m_bufSize = bufSize; + m_own = 1; m_didBookmark = 0; - m_readAhead = 0; + m_readAhead = 0; m_lastIsAhead = 0; } @@ -258,13 +264,13 @@ FileBIStream::~FileBIStream() { } //---------------------------------------------------- -int FileBIStream::open(const char *fileName) { +int FileBIStream::open(const char* fileName) { m_file = fopen(fileName, "rb"); if (m_file == NULL) return 0; setvbuf(m_file, m_buf, _IOFBF, m_bufSize); - m_own = 0; - m_readAhead = 0; + m_own = 0; + m_readAhead = 0; m_lastIsAhead = 0; return 1; } @@ -279,10 +285,10 @@ int FileBIStream::close() { } //---------------------------------------------------- -void FileBIStream::attach(FILE *f) { - m_file = f; - m_own = 0; - m_readAhead = 0; +void FileBIStream::attach(FILE* f) { + m_file = f; + m_own = 0; + m_readAhead = 0; m_lastIsAhead = 0; } @@ -294,21 +300,21 @@ int FileBIStream::eos() { } //---------------------------------------------------- -int FileBIStream::getNext(UKBYTE &b) { +int FileBIStream::getNext(UKBYTE& b) { if (m_readAhead) { - m_readAhead = 0; - b = m_readByte; + m_readAhead = 0; + b = m_readByte; m_lastIsAhead = 1; return 1; } m_lastIsAhead = 0; - b = fgetc(m_file); + b = fgetc(m_file); return (!feof(m_file)); } //---------------------------------------------------- -int FileBIStream::peekNext(UKBYTE &b) { +int FileBIStream::peekNext(UKBYTE& b) { if (m_readAhead) { b = m_readByte; return 1; @@ -325,8 +331,8 @@ int FileBIStream::peekNext(UKBYTE &b) { int FileBIStream::unget(UKBYTE b) { if (m_lastIsAhead) { m_lastIsAhead = 0; - m_readAhead = 1; - m_readByte = b; + m_readAhead = 1; + m_readByte = b; return 1; } @@ -335,13 +341,13 @@ int FileBIStream::unget(UKBYTE b) { } //---------------------------------------------------- -int FileBIStream::getNextW(UKWORD &w) { +int FileBIStream::getNextW(UKWORD& w) { UKBYTE b1, b2; if (getNext(b1)) { if (getNext(b2)) { - *((UKBYTE *)&w) = b1; - *(((UKBYTE *)&w) + 1) = b2; + *((UKBYTE*)&w) = b1; + *(((UKBYTE*)&w) + 1) = b2; return 1; } } @@ -349,33 +355,33 @@ int FileBIStream::getNextW(UKWORD &w) { } //---------------------------------------------------- -int FileBIStream::getNextDW(UKDWORD &dw) { +int FileBIStream::getNextDW(UKDWORD& dw) { UKWORD w1, w2; if (getNextW(w1)) { if (getNextW(w2)) { - *((UKWORD *)&dw) = w1; - *(((UKWORD *)&dw) + 1) = w2; + *((UKWORD*)&dw) = w1; + *(((UKWORD*)&dw) + 1) = w2; return 1; } } return 0; } //---------------------------------------------------- -int FileBIStream::peekNextW(UKWORD &w) { +int FileBIStream::peekNextW(UKWORD& w) { UKBYTE hi, low; if (getNext(low)) { if (getNext(hi)) { unget(hi); - w = hi; - w = (w << 8) + low; - m_readAhead = 1; - m_readByte = low; + w = hi; + w = (w << 8) + low; + m_readAhead = 1; + m_readByte = low; m_lastIsAhead = 0; return 1; } - m_readAhead = 1; - m_readByte = low; + m_readAhead = 1; + m_readByte = low; m_lastIsAhead = 0; return 0; } @@ -384,7 +390,7 @@ int FileBIStream::peekNextW(UKWORD &w) { //---------------------------------------------------- int FileBIStream::bookmark() { - m_didBookmark = 1; + m_didBookmark = 1; m_bookmark.pos = ftell(m_file); return 1; } @@ -401,12 +407,12 @@ int FileBIStream::gotoBookmark() { // Class FileBOStream // //////////////////////////////////////////////////// //---------------------------------------------------- -FileBOStream::FileBOStream(int bufSize, char *buf) { - m_file = NULL; - m_buf = buf; +FileBOStream::FileBOStream(int bufSize, char* buf) { + m_file = NULL; + m_buf = buf; m_bufSize = bufSize; - m_own = 1; - m_bad = 1; + m_own = 1; + m_bad = 1; } //---------------------------------------------------- @@ -416,7 +422,7 @@ FileBOStream::~FileBOStream() { } //---------------------------------------------------- -int FileBOStream::open(const char *fileName) { +int FileBOStream::open(const char* fileName) { m_file = fopen(fileName, "wb"); if (m_file == NULL) return 0; @@ -427,10 +433,10 @@ int FileBOStream::open(const char *fileName) { } //---------------------------------------------------- -void FileBOStream::attach(FILE *f) { +void FileBOStream::attach(FILE* f) { m_file = f; - m_own = 0; - m_bad = 0; + m_own = 0; + m_bad = 0; } //---------------------------------------------------- @@ -463,7 +469,7 @@ int FileBOStream::putW(UKWORD w) { } //---------------------------------------------------- -int FileBOStream::puts(const char *s, int size) { +int FileBOStream::puts(const char* s, int size) { if (m_bad) return 0; if (size == -1) { @@ -471,9 +477,11 @@ int FileBOStream::puts(const char *s, int size) { return (!m_bad); } int out = fwrite(s, 1, size, m_file); - m_bad = (out != size); + m_bad = (out != size); return (!m_bad); } //---------------------------------------------------- -int FileBOStream::isOK() { return !m_bad; } +int FileBOStream::isOK() { + return !m_bad; +} diff --git a/unikey/core/byteio.h b/unikey/core/byteio.h index 9b3582d7..24cbf046 100644 --- a/unikey/core/byteio.h +++ b/unikey/core/byteio.h @@ -9,73 +9,75 @@ // #include "vnconv.h" #include -typedef unsigned char UKBYTE; +typedef unsigned char UKBYTE; typedef unsigned short UKWORD; -typedef unsigned int UKDWORD; +typedef unsigned int UKDWORD; //---------------------------------------------------- class ByteStream { -public: + public: virtual ~ByteStream() {} }; //---------------------------------------------------- class ByteInStream : public ByteStream { -public: - virtual int getNext(UKBYTE &b) = 0; - virtual int peekNext(UKBYTE &b) = 0; - virtual int unget(UKBYTE b) = 0; + public: + virtual int getNext(UKBYTE& b) = 0; + virtual int peekNext(UKBYTE& b) = 0; + virtual int unget(UKBYTE b) = 0; - virtual int getNextW(UKWORD &w) = 0; - virtual int peekNextW(UKWORD &w) = 0; + virtual int getNextW(UKWORD& w) = 0; + virtual int peekNextW(UKWORD& w) = 0; - virtual int getNextDW(UKDWORD &dw) = 0; + virtual int getNextDW(UKDWORD& dw) = 0; virtual int bookmark() // no support for bookmark by default { return 0; } - virtual int gotoBookmark() { return 0; } + virtual int gotoBookmark() { + return 0; + } - virtual int eos() = 0; // end of stream + virtual int eos() = 0; // end of stream virtual int close() = 0; }; //---------------------------------------------------- class ByteOutStream : public ByteStream { -public: - virtual int putB(UKBYTE b) = 0; - virtual int putW(UKWORD w) = 0; - virtual int puts(const char *s, int size = -1) = 0; // write an 8-bit string - virtual int isOK() = 0; // get current stream state + public: + virtual int putB(UKBYTE b) = 0; + virtual int putW(UKWORD w) = 0; + virtual int puts(const char* s, int size = -1) = 0; // write an 8-bit string + virtual int isOK() = 0; // get current stream state }; //---------------------------------------------------- class StringBIStream : public ByteInStream { -protected: - int m_eos; + protected: + int m_eos; UKBYTE *m_data, *m_current; - int m_len, m_left; + int m_len, m_left; struct { - int eos; + int eos; UKBYTE *data, *current; - int len, left; + int len, left; } m_bookmark; int m_didBookmark; -public: - StringBIStream(UKBYTE *data, int len, int elementSize = 1); - virtual int getNext(UKBYTE &b); - virtual int peekNext(UKBYTE &b); + public: + StringBIStream(UKBYTE* data, int len, int elementSize = 1); + virtual int getNext(UKBYTE& b); + virtual int peekNext(UKBYTE& b); virtual int unget(UKBYTE b); - virtual int getNextW(UKWORD &w); - virtual int peekNextW(UKWORD &w); + virtual int getNextW(UKWORD& w); + virtual int peekNextW(UKWORD& w); - virtual int getNextDW(UKDWORD &dw); + virtual int getNextDW(UKDWORD& dw); virtual int eos(); // end of stream virtual int close(); @@ -83,18 +85,20 @@ class StringBIStream : public ByteInStream { virtual int bookmark(); virtual int gotoBookmark(); - void reopen(); - int left() { return m_left; } + void reopen(); + int left() { + return m_left; + } }; //---------------------------------------------------- class FileBIStream : public ByteInStream { -protected: - FILE *m_file; - int m_bufSize; - char *m_buf; - int m_own; - int m_didBookmark; + protected: + FILE* m_file; + int m_bufSize; + char* m_buf; + int m_own; + int m_didBookmark; struct { long pos; @@ -103,25 +107,25 @@ class FileBIStream : public ByteInStream { // some systems don't have wide char IO functions // we have to use this variables to implement that UKBYTE m_readByte; - int m_readAhead; - int m_lastIsAhead; + int m_readAhead; + int m_lastIsAhead; -public: - FileBIStream(int bufsize = 8192, char *buf = NULL); + public: + FileBIStream(int bufsize = 8192, char* buf = NULL); // FileBIStream(char *fileName, int bufsize = 8192, void *buf = NULL); - int open(const char *fileName); - void attach(FILE *f); + int open(const char* fileName); + void attach(FILE* f); virtual int close(); - virtual int getNext(UKBYTE &b); - virtual int peekNext(UKBYTE &b); + virtual int getNext(UKBYTE& b); + virtual int peekNext(UKBYTE& b); virtual int unget(UKBYTE b); - virtual int getNextW(UKWORD &w); - virtual int peekNextW(UKWORD &w); + virtual int getNextW(UKWORD& w); + virtual int peekNextW(UKWORD& w); - virtual int getNextDW(UKDWORD &dw); + virtual int getNextDW(UKDWORD& dw); virtual int eos(); // end of stream @@ -133,45 +137,49 @@ class FileBIStream : public ByteInStream { //---------------------------------------------------- class StringBOStream : public ByteOutStream { -protected: + protected: UKBYTE *m_buf, *m_current; - int m_out; - int m_len; - int m_bad; + int m_out; + int m_len; + int m_bad; -public: - StringBOStream(UKBYTE *buf, int len); + public: + StringBOStream(UKBYTE* buf, int len); virtual int putB(UKBYTE b); virtual int putW(UKWORD w); - virtual int puts(const char *s, int size = -1); + virtual int puts(const char* s, int size = -1); virtual int isOK(); // get current stream state - virtual int close() { return 1; }; + virtual int close() { + return 1; + }; void reopen(); - int getOutBytes() { return m_out; } + int getOutBytes() { + return m_out; + } }; //---------------------------------------------------- class FileBOStream : public ByteOutStream { -protected: - FILE *m_file; - int m_bufSize; - char *m_buf; - int m_own; - int m_bad; - -public: - FileBOStream(int bufsize = 8192, char *buf = NULL); + protected: + FILE* m_file; + int m_bufSize; + char* m_buf; + int m_own; + int m_bad; + + public: + FileBOStream(int bufsize = 8192, char* buf = NULL); // FileBOStream(char *fileName, int bufsize = 8192, void *buf = NULL); - int open(const char *fileName); - void attach(FILE *); + int open(const char* fileName); + void attach(FILE*); virtual int close(); virtual int putB(UKBYTE b); virtual int putW(UKWORD w); - virtual int puts(const char *s, int size = -1); + virtual int puts(const char* s, int size = -1); virtual int isOK(); // get current stream state virtual ~FileBOStream(); }; diff --git a/unikey/core/charset.cpp b/unikey/core/charset.cpp index e6719a4b..3666fb0f 100644 --- a/unikey/core/charset.cpp +++ b/unikey/core/charset.cpp @@ -16,23 +16,22 @@ int LoVowel['z' - 'a' + 1]; int HiVowel['Z' - 'A' + 1]; -#define IS_VOWEL(x) \ - ((x >= 'a' && x <= 'z' && LoVowel[x - 'a']) || \ - (x >= 'A' && x <= 'Z' && HiVowel[x - 'A'])) +#define IS_VOWEL(x) ((x >= 'a' && x <= 'z' && LoVowel[x - 'a']) || (x >= 'A' && x <= 'Z' && HiVowel[x - 'A'])) -SingleByteCharset *SgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; -DoubleByteCharset *DbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; +SingleByteCharset* SgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; +DoubleByteCharset* DbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; DllExport CVnCharsetLib VnCharsetLibObj; ////////////////////////////////////////////////////// // Generic VnCharset class ////////////////////////////////////////////////////// -int VnCharset::elementSize() { return 1; } +int VnCharset::elementSize() { + return 1; +} //------------------------------------------- -int VnInternalCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int VnInternalCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { if (!is.getNextDW(stdChar)) { bytesRead = 0; return 0; @@ -42,81 +41,74 @@ int VnInternalCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //------------------------------------------- -int VnInternalCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { +int VnInternalCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { outLen = sizeof(StdVnChar); os.putW((UKWORD)stdChar); return os.putW((UKWORD)(stdChar >> (sizeof(UKWORD) * 8))); } //------------------------------------------- -int VnInternalCharset::elementSize() { return 4; } +int VnInternalCharset::elementSize() { + return 4; +} //------------------------------------------- -SingleByteCharset::SingleByteCharset(unsigned char *vnChars) { +SingleByteCharset::SingleByteCharset(unsigned char* vnChars) { int i; m_vnChars = vnChars; memset(m_stdMap, 0, 256 * sizeof(UKWORD)); for (i = 0; i < TOTAL_VNCHARS; i++) { - if (vnChars[i] != 0 && - (i == TOTAL_VNCHARS - 1 || vnChars[i] != vnChars[i + 1])) + if (vnChars[i] != 0 && (i == TOTAL_VNCHARS - 1 || vnChars[i] != vnChars[i + 1])) m_stdMap[vnChars[i]] = i + 1; } } //------------------------------------------- -int SingleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int SingleByteCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { unsigned char ch; if (!is.getNext(ch)) { bytesRead = 0; return 0; } - stdChar = (m_stdMap[ch]) ? (VnStdCharOffset + m_stdMap[ch] - 1) : ch; + stdChar = (m_stdMap[ch]) ? (VnStdCharOffset + m_stdMap[ch] - 1) : ch; bytesRead = 1; return 1; } //------------------------------------------- -int SingleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { - int ret; +int SingleByteCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { + int ret; unsigned char ch; if (stdChar >= VnStdCharOffset) { outLen = 1; - ch = m_vnChars[stdChar - VnStdCharOffset]; + ch = m_vnChars[stdChar - VnStdCharOffset]; if (ch == 0) - ch = (stdChar == StdStartQuote) - ? PadStartQuote - : ((stdChar == StdEndQuote) - ? PadEndQuote - : ((stdChar == StdEllipsis) ? PadEllipsis - : PadChar)); + ch = (stdChar == StdStartQuote) ? PadStartQuote : ((stdChar == StdEndQuote) ? PadEndQuote : ((stdChar == StdEllipsis) ? PadEllipsis : PadChar)); ret = os.putB(ch); } else { if (stdChar > 255 || m_stdMap[stdChar]) { // this character is missing in the charset // output padding character outLen = 1; - ret = os.putB(PadChar); + ret = os.putB(PadChar); } else { outLen = 1; - ret = os.putB((UKBYTE)stdChar); + ret = os.putB((UKBYTE)stdChar); } } return ret; } //------------------------------------------- -int wideCharCompare(const void *ele1, const void *ele2) { - UKWORD ch1 = LOWORD(*((UKDWORD *)ele1)); - UKWORD ch2 = LOWORD(*((UKDWORD *)ele2)); +int wideCharCompare(const void* ele1, const void* ele2) { + UKWORD ch1 = LOWORD(*((UKDWORD*)ele1)); + UKWORD ch2 = LOWORD(*((UKDWORD*)ele2)); return (ch1 == ch2) ? 0 : ((ch1 > ch2) ? 1 : -1); } //------------------------------------------- -UnicodeCharset::UnicodeCharset(UnicodeChar *vnChars) { +UnicodeCharset::UnicodeCharset(UnicodeChar* vnChars) { UKDWORD i; m_toUnicode = vnChars; for (i = 0; i < TOTAL_VNCHARS; i++) @@ -125,17 +117,15 @@ UnicodeCharset::UnicodeCharset(UnicodeChar *vnChars) { } //------------------------------------------- -int UnicodeCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int UnicodeCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { UnicodeChar uniCh; if (!is.getNextW(uniCh)) { bytesRead = 0; return 0; } - bytesRead = sizeof(UnicodeChar); - UKDWORD key = uniCh; - UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, - sizeof(UKDWORD), wideCharCompare); + bytesRead = sizeof(UnicodeChar); + UKDWORD key = uniCh; + UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -144,31 +134,30 @@ int UnicodeCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //------------------------------------------- -int UnicodeCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { +int UnicodeCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { outLen = sizeof(UnicodeChar); - return os.putW((stdChar >= VnStdCharOffset) - ? m_toUnicode[stdChar - VnStdCharOffset] - : (UnicodeChar)stdChar); + return os.putW((stdChar >= VnStdCharOffset) ? m_toUnicode[stdChar - VnStdCharOffset] : (UnicodeChar)stdChar); } //------------------------------------------- -int UnicodeCharset::elementSize() { return 2; } +int UnicodeCharset::elementSize() { + return 2; +} //////////////////////////////////////// // Unicode decomposed //////////////////////////////////////// //------------------------------------------- -int uniCompInfoCompare(const void *ele1, const void *ele2) { - UKDWORD ch1 = ((UniCompCharInfo *)ele1)->compChar; - UKDWORD ch2 = ((UniCompCharInfo *)ele2)->compChar; +int uniCompInfoCompare(const void* ele1, const void* ele2) { + UKDWORD ch1 = ((UniCompCharInfo*)ele1)->compChar; + UKDWORD ch2 = ((UniCompCharInfo*)ele2)->compChar; return (ch1 == ch2) ? 0 : ((ch1 > ch2) ? 1 : -1); } -UnicodeCompCharset::UnicodeCompCharset(UnicodeChar *uniChars, - UKDWORD *uniCompChars) { +UnicodeCompCharset::UnicodeCompCharset(UnicodeChar* uniChars, UKDWORD* uniCompChars) { int i, k; m_uniCompChars = uniCompChars; - m_totalChars = 0; + m_totalChars = 0; for (i = 0; i < TOTAL_VNCHARS; i++) { m_info[i].compChar = uniCompChars[i]; m_info[i].stdIndex = i; @@ -187,22 +176,19 @@ UnicodeCompCharset::UnicodeCompCharset(UnicodeChar *uniChars, } //--------------------------------------------- -int UnicodeCompCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int UnicodeCompCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { // read first char UniCompCharInfo key; - UKWORD w; + UKWORD w; if (!is.getNextW(w)) { bytesRead = 0; return 0; } key.compChar = w; - bytesRead = 2; + bytesRead = 2; - UniCompCharInfo *pInfo = - (UniCompCharInfo *)bsearch(&key, m_info, m_totalChars, - sizeof(UniCompCharInfo), uniCompInfoCompare); + UniCompCharInfo* pInfo = (UniCompCharInfo*)bsearch(&key, m_info, m_totalChars, sizeof(UniCompCharInfo), uniCompInfoCompare); if (!pInfo) stdChar = key.compChar; else { @@ -211,9 +197,7 @@ int UnicodeCompCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, UKDWORD hi = w; if (hi > 0) { key.compChar += hi << 16; - pInfo = (UniCompCharInfo *)bsearch(&key, m_info, m_totalChars, - sizeof(UniCompCharInfo), - uniCompInfoCompare); + pInfo = (UniCompCharInfo*)bsearch(&key, m_info, m_totalChars, sizeof(UniCompCharInfo), uniCompInfoCompare); if (pInfo) { stdChar = pInfo->stdIndex + VnStdCharOffset; bytesRead += 2; @@ -226,36 +210,36 @@ int UnicodeCompCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //--------------------------------------------- -int UnicodeCompCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { +int UnicodeCompCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { int ret; if (stdChar >= VnStdCharOffset) { UKDWORD uniCompCh = m_uniCompChars[stdChar - VnStdCharOffset]; - UKWORD lo = LOWORD(uniCompCh); - UKWORD hi = HIWORD(uniCompCh); - outLen = 2; - ret = os.putW(lo); + UKWORD lo = LOWORD(uniCompCh); + UKWORD hi = HIWORD(uniCompCh); + outLen = 2; + ret = os.putW(lo); if (hi > 0) { outLen += 2; ret = os.putW(hi); } } else { outLen = 2; - ret = os.putW((UKWORD)stdChar); + ret = os.putW((UKWORD)stdChar); } return ret; } //------------------------------------------- -int UnicodeCompCharset::elementSize() { return 2; } +int UnicodeCompCharset::elementSize() { + return 2; +} //////////////////////////////// // Unicode UTF-8 // //////////////////////////////// -int UnicodeUTF8Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { - UKWORD w1, w2, w3; - UKBYTE first, second, third; +int UnicodeUTF8Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { + UKWORD w1, w2, w3; + UKBYTE first, second, third; UnicodeChar uniCh; bytesRead = 0; @@ -275,9 +259,9 @@ int UnicodeUTF8Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, } is.getNext(second); bytesRead = 2; - w1 = first; - w2 = second; - uniCh = ((w1 & 0x001F) << 6) | (w2 & 0x3F); + w1 = first; + w2 = second; + uniCh = ((w1 & 0x001F) << 6) | (w2 & 0x3F); } else if ((first & 0xF0) == 0xE0) { // 3-byte sequence if (!is.peekNext(second)) @@ -296,19 +280,18 @@ int UnicodeUTF8Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, } is.getNext(third); bytesRead = 3; - w1 = first; - w2 = second; - w3 = third; - uniCh = ((w1 & 0x000F) << 12) | ((w2 & 0x003F) << 6) | (w3 & 0x003F); + w1 = first; + w2 = second; + w3 = third; + uniCh = ((w1 & 0x000F) << 12) | ((w2 & 0x003F) << 6) | (w3 & 0x003F); } else { stdChar = INVALID_STD_CHAR; return 1; } // translate to StdVnChar - UKDWORD key = uniCh; - UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, - sizeof(UKDWORD), wideCharCompare); + UKDWORD key = uniCh; + UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -317,15 +300,12 @@ int UnicodeUTF8Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //------------------------------------------- -int UnicodeUTF8Charset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) - ? (UnicodeChar)stdChar - : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeUTF8Charset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 0x0080) { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else if (uChar < 0x0800) { outLen = 2; os.putB(0xC0 | (UKBYTE)(uChar >> 6)); @@ -353,15 +333,14 @@ int hexDigitValue(unsigned char digit) { } //-------------------------------------- -int UnicodeRefCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int UnicodeRefCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { unsigned char ch; - UnicodeChar uniCh; + UnicodeChar uniCh; bytesRead = 0; if (!is.getNext(ch)) return 0; bytesRead = 1; - uniCh = ch; + uniCh = ch; if (ch == '&') { if (is.peekNext(ch) && ch == '#') { is.getNext(ch); @@ -369,8 +348,8 @@ int UnicodeRefCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, if (!is.eos()) { is.peekNext(ch); if (ch != 'x' && ch != 'X') { - UKWORD code = 0; - int digits = 0; + UKWORD code = 0; + int digits = 0; while (is.peekNext(ch) && isdigit(ch) && digits < 5) { is.getNext(ch); bytesRead++; @@ -385,8 +364,8 @@ int UnicodeRefCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } else { is.getNext(ch); bytesRead++; - UKWORD code = 0; - int digits = 0; + UKWORD code = 0; + int digits = 0; while (is.peekNext(ch) && isxdigit(ch) && digits < 4) { is.getNext(ch); bytesRead++; @@ -404,9 +383,8 @@ int UnicodeRefCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } // translate to StdVnChar - UKDWORD key = uniCh; - UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, - sizeof(UKDWORD), wideCharCompare); + UKDWORD key = uniCh; + UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -415,15 +393,12 @@ int UnicodeRefCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //-------------------------------- -int UnicodeRefCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) - ? (UnicodeChar)stdChar - : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeRefCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 128) { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else { outLen = 2; os.putB((UKBYTE)'&'); @@ -451,15 +426,12 @@ int UnicodeRefCharset::putChar(ByteOutStream &os, StdVnChar stdChar, #define HEX_DIGIT(x) ((x < 10) ? ('0' + x) : ('A' + x - 10)) //-------------------------------- -int UnicodeHexCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) - ? (UnicodeChar)stdChar - : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeHexCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 256) { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else { outLen = 3; os.putB('&'); @@ -467,7 +439,7 @@ int UnicodeHexCharset::putChar(ByteOutStream &os, StdVnChar stdChar, os.putB('x'); int i, digit; - int prev = 0; + int prev = 0; int shifts = 12; for (i = 0; i < 4; i++) { @@ -488,24 +460,25 @@ int UnicodeHexCharset::putChar(ByteOutStream &os, StdVnChar stdChar, ///////////////////////////////// // Class UnicodeCStringCharset / ///////////////////////////////// -void UnicodeCStringCharset::startInput() { m_prevIsHex = 0; } +void UnicodeCStringCharset::startInput() { + m_prevIsHex = 0; +} //---------------------------------------- -int UnicodeCStringCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int UnicodeCStringCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { unsigned char ch; - UnicodeChar uniCh; + UnicodeChar uniCh; bytesRead = 0; if (!is.getNext(ch)) return 0; bytesRead = 1; - uniCh = ch; + uniCh = ch; if (ch == '\\') { if (is.peekNext(ch) && (ch == 'x' || ch == 'X')) { is.getNext(ch); bytesRead++; - UKWORD code = 0; - int digits = 0; + UKWORD code = 0; + int digits = 0; while (is.peekNext(ch) && isxdigit(ch) && digits < 4) { is.getNext(ch); bytesRead++; @@ -517,9 +490,8 @@ int UnicodeCStringCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } // translate to StdVnChar - UKDWORD key = uniCh; - UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, - sizeof(UKDWORD), wideCharCompare); + UKDWORD key = uniCh; + UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -528,22 +500,19 @@ int UnicodeCStringCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //------------------------------------ -int UnicodeCStringCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) - ? (UnicodeChar)stdChar - : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeCStringCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 128 && !isxdigit(uChar) && uChar != 'x' && uChar != 'X') { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else { outLen = 2; os.putB('\\'); os.putB('x'); int i, digit; - int prev = 0; + int prev = 0; int shifts = 12; for (i = 0; i < 4; i++) { @@ -555,7 +524,7 @@ int UnicodeCStringCharset::putChar(ByteOutStream &os, StdVnChar stdChar, } shifts -= 4; } - ret = os.isOK(); + ret = os.isOK(); m_prevIsHex = 1; } return ret; @@ -564,7 +533,7 @@ int UnicodeCStringCharset::putChar(ByteOutStream &os, StdVnChar stdChar, ///////////////////////////////// // Double-byte charsets // ///////////////////////////////// -DoubleByteCharset::DoubleByteCharset(UKWORD *vnChars) { +DoubleByteCharset::DoubleByteCharset(UKWORD* vnChars) { m_toDoubleChar = vnChars; memset(m_stdMap, 0, 256 * sizeof(UKWORD)); for (int i = 0; i < TOTAL_VNCHARS; i++) { @@ -572,15 +541,13 @@ DoubleByteCharset::DoubleByteCharset(UKWORD *vnChars) { m_stdMap[vnChars[i] >> 8] = 0xFFFF; // INVALID_STD_CHAR; else if (m_stdMap[vnChars[i]] == 0) m_stdMap[vnChars[i]] = i + 1; - m_vnChars[i] = - (i << 16) + vnChars[i]; // high word is used for StdChar index + m_vnChars[i] = (i << 16) + vnChars[i]; // high word is used for StdChar index } qsort(m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); } //--------------------------------------------- -int DoubleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int DoubleByteCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { unsigned char ch; // read first byte @@ -588,7 +555,7 @@ int DoubleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, if (!is.getNext(ch)) return 0; bytesRead = 1; - stdChar = m_stdMap[ch]; + stdChar = m_stdMap[ch]; if (stdChar == 0) stdChar = ch; else if (stdChar == 0xFFFF) @@ -598,12 +565,10 @@ int DoubleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, UKBYTE hi; if (is.peekNext(hi) && hi > 0) { // test if a double-byte character is encountered - UKDWORD key = MAKEWORD(ch, hi); - UKDWORD *pChar = - (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, - sizeof(UKDWORD), wideCharCompare); + UKDWORD key = MAKEWORD(ch, hi); + UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); if (pChar) { - stdChar = VnStdCharOffset + HIWORD(*pChar); + stdChar = VnStdCharOffset + HIWORD(*pChar); bytesRead = 2; is.getNext(hi); } @@ -613,8 +578,7 @@ int DoubleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //--------------------------------------------- -int DoubleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { +int DoubleByteCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { int ret; if (stdChar >= VnStdCharOffset) { UKWORD wCh = m_toDoubleChar[stdChar - VnStdCharOffset]; @@ -628,7 +592,7 @@ int DoubleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, if (m_stdMap[b] == 0xFFFF) b = PadChar; outLen = 1; - ret = os.putB(b); + ret = os.putB(b); } /* outLen = 1; @@ -641,10 +605,10 @@ int DoubleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, } else { if (stdChar > 255 || m_stdMap[stdChar]) { outLen = 1; - ret = os.putB((UKBYTE)PadChar); + ret = os.putB((UKBYTE)PadChar); } else { outLen = 1; - ret = os.putB((UKBYTE)stdChar); + ret = os.putB((UKBYTE)stdChar); } } return ret; @@ -656,14 +620,13 @@ int DoubleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, unsigned char VIQRTones[] = {'\'', '`', '?', '~', '.'}; -const char *VIQREscapes[] = { - "://", "/", "@", "mailto:", "email:", "news:", "www", "ftp"}; +const char* VIQREscapes[] = {"://", "/", "@", "mailto:", "email:", "news:", "www", "ftp"}; -const int VIQREscCount = sizeof(VIQREscapes) / sizeof(char *); +const int VIQREscCount = sizeof(VIQREscapes) / sizeof(char*); -VIQRCharset::VIQRCharset(UKDWORD *vnChars) { +VIQRCharset::VIQRCharset(UKDWORD* vnChars) { memset(m_stdMap, 0, 256 * sizeof(UKWORD)); - int i; + int i; UKDWORD dw; m_vnChars = vnChars; for (i = 0; i < TOTAL_VNCHARS; i++) { @@ -676,11 +639,11 @@ VIQRCharset::VIQRCharset(UKDWORD *vnChars) { // set offset from base characters according to tone marks m_stdMap[(unsigned char)'\''] = 2; - m_stdMap[(unsigned char)'`'] = 4; - m_stdMap[(unsigned char)'?'] = 6; - m_stdMap[(unsigned char)'~'] = 8; - m_stdMap[(unsigned char)'.'] = 10; - m_stdMap[(unsigned char)'^'] = 12; + m_stdMap[(unsigned char)'`'] = 4; + m_stdMap[(unsigned char)'?'] = 6; + m_stdMap[(unsigned char)'~'] = 8; + m_stdMap[(unsigned char)'.'] = 10; + m_stdMap[(unsigned char)'^'] = 12; m_stdMap[(unsigned char)'('] = 24; m_stdMap[(unsigned char)'+'] = 26; @@ -689,24 +652,23 @@ VIQRCharset::VIQRCharset(UKDWORD *vnChars) { //--------------------------------------------------- void VIQRCharset::startInput() { - m_suspicious = 0; + m_suspicious = 0; m_atWordBeginning = 1; - m_gotTone = 0; - m_escAll = 0; + m_gotTone = 0; + m_escAll = 0; if (VnCharsetLibObj.m_options.viqrEsc) VnCharsetLibObj.m_VIQREscPatterns.reset(); } //--------------------------------------------------- -int VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { unsigned char ch1; bytesRead = 0; if (!is.getNext(ch1)) return 0; bytesRead = 1; - stdChar = m_stdMap[ch1]; + stdChar = m_stdMap[ch1]; if (VnCharsetLibObj.m_options.viqrEsc) { if (VnCharsetLibObj.m_VIQREscPatterns.foundAtNextChar(ch1) != -1) { @@ -732,40 +694,29 @@ int VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, unsigned char ch2; is.peekNext(ch2); unsigned char upper = toupper(ch1); - if ((!VnCharsetLibObj.m_options.smartViqr || m_atWordBeginning) && - upper == 'D' && (ch2 == 'd' || ch2 == 'D')) { + if ((!VnCharsetLibObj.m_options.smartViqr || m_atWordBeginning) && upper == 'D' && (ch2 == 'd' || ch2 == 'D')) { is.getNext(ch2); bytesRead++; stdChar += 2; // dd is 2 positions after d. } else { StdVnChar index = m_stdMap[ch2]; - int cond; + int cond; if (m_suspicious) { - cond = - IS_VOWEL(ch1) && - (index == 2 || index == 4 || - index == 8 || // not accepting ? . in suspicious mode - (index == 12 && - (upper == 'A' || upper == 'E' || upper == 'O')) || - (m_stdMap[ch2] == 24 && upper == 'A') || + cond = IS_VOWEL(ch1) && + (index == 2 || index == 4 || index == 8 || // not accepting ? . in suspicious mode + (index == 12 && (upper == 'A' || upper == 'E' || upper == 'O')) || (m_stdMap[ch2] == 24 && upper == 'A') || (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); if (cond) m_suspicious = 0; } else - cond = - IS_VOWEL(ch1) && - ((index <= 10 && index > 0 && - (!m_gotTone || (index != 6 && index != 10))) || - (index == 12 && - (upper == 'A' || upper == 'E' || upper == 'O')) || - (m_stdMap[ch2] == 24 && upper == 'A') || - (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); + cond = IS_VOWEL(ch1) && + ((index <= 10 && index > 0 && (!m_gotTone || (index != 6 && index != 10))) || (index == 12 && (upper == 'A' || upper == 'E' || upper == 'O')) || + (m_stdMap[ch2] == 24 && upper == 'A') || (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); if (cond) { if (index > 0) - m_gotTone = - 1; // we have a tone/breve/hook in the current word + m_gotTone = 1; // we have a tone/breve/hook in the current word // ok, take this byte is.getNext(ch2); @@ -778,8 +729,7 @@ int VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, stdChar += offset; // check next byte if (is.peekNext(ch2)) { - if (index > 10 && m_stdMap[ch2] > 0 && - m_stdMap[ch2] <= 10) { + if (index > 10 && m_stdMap[ch2] > 0 && m_stdMap[ch2] <= 10) { // ok, take one more byte is.getNext(ch2); bytesRead++; @@ -791,8 +741,7 @@ int VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } m_atWordBeginning = (stdChar < 256); if (stdChar < 256) { - m_gotTone = - 0; // reset this flag because we are at the beginning of a new word + m_gotTone = 0; // reset this flag because we are at the beginning of a new word } // adjust stdChar @@ -807,22 +756,22 @@ void VIQRCharset::startOutput() { m_escapeRoof = 0; m_escapeHook = 0; m_escapeTone = 0; - m_noOutEsc = 0; + m_noOutEsc = 0; VnCharsetLibObj.m_VIQROutEscPatterns.reset(); } //--------------------------------------------------- -int VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { - int ret; +int VIQRCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { + int ret; UKBYTE b; if (stdChar >= VnStdCharOffset) { - outLen = 1; - UKDWORD dw = m_vnChars[stdChar - VnStdCharOffset]; + outLen = 1; + UKDWORD dw = m_vnChars[stdChar - VnStdCharOffset]; - unsigned char first = (unsigned char)dw; + unsigned char first = (unsigned char)dw; unsigned char firstUpper = toupper(first); - b = (UKBYTE)dw; + b = (UKBYTE)dw; ret = os.putB(b); if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar(b) != -1) m_noOutEsc = 1; @@ -839,7 +788,7 @@ int VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { if (dw & 0x00FF0000) { // third byte is present outLen++; - ret = os.putB((UKBYTE)(dw >> 16)); + ret = os.putB((UKBYTE)(dw >> 16)); m_escapeTone = 0; } else { UKWORD index = m_stdMap[second]; @@ -855,34 +804,28 @@ int VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { m_escapeTone = IS_VOWEL(first); m_escapeBowl = (firstUpper == 'A'); m_escapeHook = (firstUpper == 'U' || firstUpper == 'O'); - m_escapeRoof = - (firstUpper == 'A' || firstUpper == 'E' || firstUpper == 'O'); + m_escapeRoof = (firstUpper == 'A' || firstUpper == 'E' || firstUpper == 'O'); } } else { if (stdChar > 255) { outLen = 1; - ret = os.putB((UKBYTE)PadChar); - if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar( - (UKBYTE)PadChar) != -1) + ret = os.putB((UKBYTE)PadChar); + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar((UKBYTE)PadChar) != -1) m_noOutEsc = 1; } else { - outLen = 1; + outLen = 1; UKWORD index = m_stdMap[stdChar]; if (!VnCharsetLibObj.m_options.viqrMixed && !m_noOutEsc && - (stdChar == '\\' || - (index > 0 && index <= 10 && m_escapeTone) || - (index == 12 && m_escapeRoof) || - (index == 24 && m_escapeBowl) || + (stdChar == '\\' || (index > 0 && index <= 10 && m_escapeTone) || (index == 12 && m_escapeRoof) || (index == 24 && m_escapeBowl) || (index == 26 && m_escapeHook))) { //(m_stdMap[stdChar] > 0 && m_stdMap[stdChar] <= 26)) { // tone mark, needs an escape character outLen++; ret = os.putB('\\'); - if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar( - '\\') != -1) + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar('\\') != -1) m_noOutEsc = 1; } - b = (UKBYTE)stdChar; + b = (UKBYTE)stdChar; ret = os.putB(b); if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar(b) != -1) m_noOutEsc = 1; @@ -903,8 +846,8 @@ int VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { ///////////////////////////////////////////// //----------------------------------------- -UTF8VIQRCharset::UTF8VIQRCharset(UnicodeUTF8Charset *pUtf, VIQRCharset *pViqr) { - m_pUtf = pUtf; +UTF8VIQRCharset::UTF8VIQRCharset(UnicodeUTF8Charset* pUtf, VIQRCharset* pViqr) { + m_pUtf = pUtf; m_pViqr = pViqr; } @@ -921,8 +864,7 @@ void UTF8VIQRCharset::startOutput() { } //----------------------------------------- -int UTF8VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int UTF8VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { UKBYTE ch; if (!is.peekNext(ch)) @@ -938,8 +880,7 @@ int UTF8VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, } //----------------------------------------- -int UTF8VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { +int UTF8VIQRCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { return m_pViqr->putChar(os, stdChar, outLen); } @@ -964,15 +905,15 @@ CVnCharsetLib::CVnCharsetLib() { HiVowel['U' - 'A'] = 1; HiVowel['Y' - 'A'] = 1; - m_pUniCharset = NULL; + m_pUniCharset = NULL; m_pUniCompCharset = NULL; - m_pUniUTF8 = NULL; - m_pUniRef = NULL; - m_pUniHex = NULL; - m_pVIQRCharObj = NULL; - m_pUVIQRCharObj = NULL; - m_pWinCP1258 = NULL; - m_pVnIntCharset = NULL; + m_pUniUTF8 = NULL; + m_pUniRef = NULL; + m_pUniHex = NULL; + m_pVIQRCharObj = NULL; + m_pUVIQRCharObj = NULL; + m_pWinCP1258 = NULL; + m_pVnIntCharset = NULL; int i; for (i = 0; i < CONV_TOTAL_SINGLE_CHARSETS; i++) @@ -982,8 +923,8 @@ CVnCharsetLib::CVnCharsetLib() { m_dbCharsets[i] = NULL; VnConvResetOptions(&m_options); - m_VIQREscPatterns.init((char **)VIQREscapes, VIQREscCount); - m_VIQROutEscPatterns.init((char **)VIQREscapes, VIQREscCount); + m_VIQREscPatterns.init((char**)VIQREscapes, VIQREscCount); + m_VIQROutEscPatterns.init((char**)VIQREscapes, VIQREscCount); } //----------------------------------------- @@ -1018,106 +959,104 @@ CVnCharsetLib::~CVnCharsetLib() { } //----------------------------------------- -VnCharset *CVnCharsetLib::getVnCharset(int charsetIdx) { +VnCharset* CVnCharsetLib::getVnCharset(int charsetIdx) { switch (charsetIdx) { - case CONV_CHARSET_UNICODE: - if (m_pUniCharset == NULL) - m_pUniCharset = new UnicodeCharset(UnicodeTable); - return m_pUniCharset; - case CONV_CHARSET_UNIDECOMPOSED: - if (m_pUniCompCharset == NULL) - m_pUniCompCharset = - new UnicodeCompCharset(UnicodeTable, UnicodeComposite); - return m_pUniCompCharset; - case CONV_CHARSET_UNIUTF8: - case CONV_CHARSET_XUTF8: - if (m_pUniUTF8 == NULL) - m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); - return m_pUniUTF8; - - case CONV_CHARSET_UNIREF: - if (m_pUniRef == NULL) - m_pUniRef = new UnicodeRefCharset(UnicodeTable); - return m_pUniRef; - - case CONV_CHARSET_UNIREF_HEX: - if (m_pUniHex == NULL) - m_pUniHex = new UnicodeHexCharset(UnicodeTable); - return m_pUniHex; - - case CONV_CHARSET_UNI_CSTRING: - if (m_pUniCString == NULL) - m_pUniCString = new UnicodeCStringCharset(UnicodeTable); - return m_pUniCString; - - case CONV_CHARSET_WINCP1258: - if (m_pWinCP1258 == NULL) - m_pWinCP1258 = new WinCP1258Charset(WinCP1258, WinCP1258Pre); - return m_pWinCP1258; - - case CONV_CHARSET_VIQR: - if (m_pVIQRCharObj == NULL) - m_pVIQRCharObj = new VIQRCharset(VIQRTable); - return m_pVIQRCharObj; - - case CONV_CHARSET_VNSTANDARD: - if (m_pVnIntCharset == NULL) - m_pVnIntCharset = new VnInternalCharset(); - return m_pVnIntCharset; - - case CONV_CHARSET_UTF8VIQR: - if (m_pUVIQRCharObj == NULL) { + case CONV_CHARSET_UNICODE: + if (m_pUniCharset == NULL) + m_pUniCharset = new UnicodeCharset(UnicodeTable); + return m_pUniCharset; + case CONV_CHARSET_UNIDECOMPOSED: + if (m_pUniCompCharset == NULL) + m_pUniCompCharset = new UnicodeCompCharset(UnicodeTable, UnicodeComposite); + return m_pUniCompCharset; + case CONV_CHARSET_UNIUTF8: + case CONV_CHARSET_XUTF8: + if (m_pUniUTF8 == NULL) + m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); + return m_pUniUTF8; + + case CONV_CHARSET_UNIREF: + if (m_pUniRef == NULL) + m_pUniRef = new UnicodeRefCharset(UnicodeTable); + return m_pUniRef; + + case CONV_CHARSET_UNIREF_HEX: + if (m_pUniHex == NULL) + m_pUniHex = new UnicodeHexCharset(UnicodeTable); + return m_pUniHex; + + case CONV_CHARSET_UNI_CSTRING: + if (m_pUniCString == NULL) + m_pUniCString = new UnicodeCStringCharset(UnicodeTable); + return m_pUniCString; + + case CONV_CHARSET_WINCP1258: + if (m_pWinCP1258 == NULL) + m_pWinCP1258 = new WinCP1258Charset(WinCP1258, WinCP1258Pre); + return m_pWinCP1258; + + case CONV_CHARSET_VIQR: if (m_pVIQRCharObj == NULL) m_pVIQRCharObj = new VIQRCharset(VIQRTable); + return m_pVIQRCharObj; - if (m_pUniUTF8 == NULL) - m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); - m_pUVIQRCharObj = new UTF8VIQRCharset(m_pUniUTF8, m_pVIQRCharObj); - } - return m_pUVIQRCharObj; - - default: - if (IS_SINGLE_BYTE_CHARSET(charsetIdx)) { - int i = charsetIdx - CONV_CHARSET_TCVN3; - if (m_sgCharsets[i] == NULL) - m_sgCharsets[i] = new SingleByteCharset(SingleByteTables[i]); - return m_sgCharsets[i]; - } else if (IS_DOUBLE_BYTE_CHARSET(charsetIdx)) { - int i = charsetIdx - CONV_CHARSET_VNIWIN; - if (m_dbCharsets[i] == NULL) - m_dbCharsets[i] = new DoubleByteCharset(DoubleByteTables[i]); - return m_dbCharsets[i]; - } + case CONV_CHARSET_VNSTANDARD: + if (m_pVnIntCharset == NULL) + m_pVnIntCharset = new VnInternalCharset(); + return m_pVnIntCharset; + + case CONV_CHARSET_UTF8VIQR: + if (m_pUVIQRCharObj == NULL) { + if (m_pVIQRCharObj == NULL) + m_pVIQRCharObj = new VIQRCharset(VIQRTable); + + if (m_pUniUTF8 == NULL) + m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); + m_pUVIQRCharObj = new UTF8VIQRCharset(m_pUniUTF8, m_pVIQRCharObj); + } + return m_pUVIQRCharObj; + + default: + if (IS_SINGLE_BYTE_CHARSET(charsetIdx)) { + int i = charsetIdx - CONV_CHARSET_TCVN3; + if (m_sgCharsets[i] == NULL) + m_sgCharsets[i] = new SingleByteCharset(SingleByteTables[i]); + return m_sgCharsets[i]; + } else if (IS_DOUBLE_BYTE_CHARSET(charsetIdx)) { + int i = charsetIdx - CONV_CHARSET_VNIWIN; + if (m_dbCharsets[i] == NULL) + m_dbCharsets[i] = new DoubleByteCharset(DoubleByteTables[i]); + return m_dbCharsets[i]; + } } return NULL; } //------------------------------------------------- -DllExport void VnConvSetOptions(VnConvOptions *pOptions) { +DllExport void VnConvSetOptions(VnConvOptions* pOptions) { VnCharsetLibObj.m_options = *pOptions; } //------------------------------------------------- -DllExport void VnConvGetOptions(VnConvOptions *pOptions) { +DllExport void VnConvGetOptions(VnConvOptions* pOptions) { *pOptions = VnCharsetLibObj.m_options; } //------------------------------------------------- -DllExport void VnConvResetOptions(VnConvOptions *pOptions) { - pOptions->viqrEsc = 1; - pOptions->viqrMixed = 0; - pOptions->toUpper = 0; - pOptions->toLower = 0; +DllExport void VnConvResetOptions(VnConvOptions* pOptions) { + pOptions->viqrEsc = 1; + pOptions->viqrMixed = 0; + pOptions->toUpper = 0; + pOptions->toLower = 0; pOptions->removeTone = 0; - pOptions->smartViqr = 1; + pOptions->smartViqr = 1; } ///////////////////////////////////////////// // Class WinCP1258Charset ///////////////////////////////////////////// -WinCP1258Charset::WinCP1258Charset(UKWORD *compositeChars, - UKWORD *precomposedChars) { +WinCP1258Charset::WinCP1258Charset(UKWORD* compositeChars, UKWORD* precomposedChars) { int i, k; m_toDoubleChar = compositeChars; memset(m_stdMap, 0, 256 * sizeof(UKWORD)); @@ -1129,8 +1068,7 @@ WinCP1258Charset::WinCP1258Charset(UKWORD *compositeChars, else if (m_stdMap[compositeChars[i]] == 0) m_stdMap[compositeChars[i]] = i + 1; - m_vnChars[i] = (i << 16) + - compositeChars[i]; // high word is used for StdChar index + m_vnChars[i] = (i << 16) + compositeChars[i]; // high word is used for StdChar index } m_totalChars = TOTAL_VNCHARS; @@ -1138,9 +1076,8 @@ WinCP1258Charset::WinCP1258Charset(UKWORD *compositeChars, // add precomposed chars to the table for (k = 0, i = TOTAL_VNCHARS; k < TOTAL_VNCHARS; k++) if (precomposedChars[k] != compositeChars[k]) { - if (precomposedChars[k] >> 8) // a 2-byte character - m_stdMap[precomposedChars[k] >> 8] = - 0xFFFF; // INVALID_STD_CHAR; + if (precomposedChars[k] >> 8) // a 2-byte character + m_stdMap[precomposedChars[k] >> 8] = 0xFFFF; // INVALID_STD_CHAR; else if (m_stdMap[precomposedChars[k]] == 0) m_stdMap[precomposedChars[k]] = k + 1; @@ -1156,8 +1093,7 @@ WinCP1258Charset::WinCP1258Charset(UKWORD *compositeChars, // This fuction is basically the same as that of DoubleByteCharset // with m_totalChars is used instead of constant TOTAL_VNCHARS //--------------------------------------------------------------------- -int WinCP1258Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) { +int WinCP1258Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { unsigned char ch; // read first byte @@ -1165,7 +1101,7 @@ int WinCP1258Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, if (!is.getNext(ch)) return 0; bytesRead = 1; - stdChar = m_stdMap[ch]; + stdChar = m_stdMap[ch]; if (stdChar == 0) stdChar = ch; else if (stdChar == 0xFFFF) @@ -1175,12 +1111,10 @@ int WinCP1258Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, UKBYTE hi; if (is.peekNext(hi) && hi > 0) { // test if a double-byte character is encountered - UKDWORD key = MAKEWORD(ch, hi); - UKDWORD *pChar = - (UKDWORD *)bsearch(&key, m_vnChars, m_totalChars, - sizeof(UKDWORD), wideCharCompare); + UKDWORD key = MAKEWORD(ch, hi); + UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, m_totalChars, sizeof(UKDWORD), wideCharCompare); if (pChar) { - stdChar = VnStdCharOffset + HIWORD(*pChar); + stdChar = VnStdCharOffset + HIWORD(*pChar); bytesRead = 2; is.getNext(hi); } @@ -1192,8 +1126,7 @@ int WinCP1258Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, //--------------------------------------------------------------------- // This fuction is exactly the same as that of DoubleByteCharset //--------------------------------------------------------------------- -int WinCP1258Charset::putChar(ByteOutStream &os, StdVnChar stdChar, - int &outLen) { +int WinCP1258Charset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { int ret; if (stdChar >= VnStdCharOffset) { UKWORD wCh = m_toDoubleChar[stdChar - VnStdCharOffset]; @@ -1207,34 +1140,32 @@ int WinCP1258Charset::putChar(ByteOutStream &os, StdVnChar stdChar, if (m_stdMap[b] == 0xFFFF) b = PadChar; outLen = 1; - ret = os.putB(b); + ret = os.putB(b); } } else { if (stdChar > 255 || m_stdMap[stdChar]) { outLen = 1; - ret = os.putB((UKBYTE)PadChar); + ret = os.putB((UKBYTE)PadChar); } else { outLen = 1; - ret = os.putB((UKBYTE)stdChar); + ret = os.putB((UKBYTE)stdChar); } } return ret; } -#define IS_ODD(x) (x & 1) +#define IS_ODD(x) (x & 1) #define IS_EVEN(x) (!(x & 1)) StdVnChar StdVnToUpper(StdVnChar ch) { - if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && - IS_ODD(ch)) + if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(ch)) ch -= 1; return ch; } //---------------------------------------- StdVnChar StdVnToLower(StdVnChar ch) { - if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && - IS_EVEN(ch)) + if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(ch)) ch += 1; return ch; } diff --git a/unikey/core/charset.h b/unikey/core/charset.h index f40d32b6..f588a680 100644 --- a/unikey/core/charset.h +++ b/unikey/core/charset.h @@ -27,7 +27,7 @@ #include "pattern.h" #include "vnconv.h" -#define TOTAL_VNCHARS 213 +#define TOTAL_VNCHARS 213 #define TOTAL_ALPHA_VNCHARS 186 #if defined(_WIN32) @@ -60,22 +60,21 @@ typedef uint32_t UKDWORD; #define MAKEWORD(a, b) ((UKWORD)(((UKBYTE)(a)) | ((UKWORD)((UKBYTE)(b))) << 8)) #endif -const StdVnChar VnStdCharOffset = 0x10000; +const StdVnChar VnStdCharOffset = 0x10000; const StdVnChar INVALID_STD_CHAR = 0xFFFFFFFF; // const unsigned char PadChar = '?'; //? is used for VIQR charset -const unsigned char PadChar = '#'; +const unsigned char PadChar = '#'; const unsigned char PadStartQuote = '\"'; -const unsigned char PadEndQuote = '\"'; -const unsigned char PadEllipsis = '.'; +const unsigned char PadEndQuote = '\"'; +const unsigned char PadEllipsis = '.'; -class DllInterface VnCharset { -public: +class DllInterface VnCharset { + public: virtual void startInput() {} virtual void startOutput() {} // virtual UKBYTE *nextInput(UKBYTE *input, int inLen, StdVnChar & stdChar, // int & bytesRead) = 0; - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, - int &bytesRead) = 0; + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) = 0; //------------------------------------------------------------------------ // put a character to the output after converting it @@ -86,208 +85,207 @@ class DllInterface VnCharset { // maxAvail[in]: max length available. // Returns: next position in output //------------------------------------------------------------------------ - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) = 0; + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) = 0; virtual int elementSize(); virtual ~VnCharset() {} }; //-------------------------------------------------- class SingleByteCharset : public VnCharset { -protected: - UKWORD m_stdMap[256]; - unsigned char *m_vnChars; - -public: - SingleByteCharset(unsigned char *vnChars); - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + protected: + UKWORD m_stdMap[256]; + unsigned char* m_vnChars; + + public: + SingleByteCharset(unsigned char* vnChars); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- class VnInternalCharset : public VnCharset { -public: + public: VnInternalCharset() {} - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); virtual int elementSize(); }; //-------------------------------------------------- class UnicodeCharset : public VnCharset { -protected: - UKDWORD m_vnChars[TOTAL_VNCHARS]; - UnicodeChar *m_toUnicode; - -public: - UnicodeCharset(UnicodeChar *vnChars); - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + protected: + UKDWORD m_vnChars[TOTAL_VNCHARS]; + UnicodeChar* m_toUnicode; + + public: + UnicodeCharset(UnicodeChar* vnChars); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); virtual int elementSize(); }; //-------------------------------------------------- class DoubleByteCharset : public VnCharset { -protected: - UKWORD m_stdMap[256]; + protected: + UKWORD m_stdMap[256]; UKDWORD m_vnChars[TOTAL_VNCHARS]; - UKWORD *m_toDoubleChar; + UKWORD* m_toDoubleChar; -public: - DoubleByteCharset(UKWORD *vnChars); - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + public: + DoubleByteCharset(UKWORD* vnChars); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- class UnicodeUTF8Charset : public UnicodeCharset { -public: - UnicodeUTF8Charset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} + public: + UnicodeUTF8Charset(UnicodeChar* vnChars) : UnicodeCharset(vnChars) {} - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- class UnicodeRefCharset : public UnicodeCharset { -public: - UnicodeRefCharset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} + public: + UnicodeRefCharset(UnicodeChar* vnChars) : UnicodeCharset(vnChars) {} - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- class UnicodeHexCharset : public UnicodeRefCharset { -public: - UnicodeHexCharset(UnicodeChar *vnChars) : UnicodeRefCharset(vnChars) {} - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + public: + UnicodeHexCharset(UnicodeChar* vnChars) : UnicodeRefCharset(vnChars) {} + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- class UnicodeCStringCharset : public UnicodeCharset { -protected: + protected: int m_prevIsHex; -public: - UnicodeCStringCharset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + public: + UnicodeCStringCharset(UnicodeChar* vnChars) : UnicodeCharset(vnChars) {} + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); virtual void startInput(); }; //-------------------------------------------------- class WinCP1258Charset : public VnCharset { -protected: - UKWORD m_stdMap[256]; + protected: + UKWORD m_stdMap[256]; UKDWORD m_vnChars[TOTAL_VNCHARS * 2]; - UKWORD *m_toDoubleChar; - int m_totalChars; + UKWORD* m_toDoubleChar; + int m_totalChars; -public: - WinCP1258Charset(UKWORD *compositeChars, UKWORD *precomposedChars); - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + public: + WinCP1258Charset(UKWORD* compositeChars, UKWORD* precomposedChars); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- struct UniCompCharInfo { UKDWORD compChar; - int stdIndex; + int stdIndex; }; class UnicodeCompCharset : public VnCharset { -protected: + protected: UniCompCharInfo m_info[TOTAL_VNCHARS * 2]; - UKDWORD *m_uniCompChars; - int m_totalChars; + UKDWORD* m_uniCompChars; + int m_totalChars; -public: - UnicodeCompCharset(UnicodeChar *uniChars, UKDWORD *uniCompChars); - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + public: + UnicodeCompCharset(UnicodeChar* uniChars, UKDWORD* uniCompChars); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); virtual int elementSize(); }; //-------------------------------------------------- class VIQRCharset : public VnCharset { -protected: - UKDWORD *m_vnChars; - UKWORD m_stdMap[256]; - int m_atWordBeginning; - int m_escapeBowl; - int m_escapeRoof; - int m_escapeHook; - int m_escapeTone; - int m_gotTone; - int m_escAll; - int m_noOutEsc; - -public: + protected: + UKDWORD* m_vnChars; + UKWORD m_stdMap[256]; + int m_atWordBeginning; + int m_escapeBowl; + int m_escapeRoof; + int m_escapeHook; + int m_escapeTone; + int m_gotTone; + int m_escAll; + int m_noOutEsc; + + public: int m_suspicious; - VIQRCharset(UKDWORD *vnChars); + VIQRCharset(UKDWORD* vnChars); virtual void startInput(); virtual void startOutput(); - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- class UTF8VIQRCharset : public VnCharset { -protected: - VIQRCharset *m_pViqr; - UnicodeUTF8Charset *m_pUtf; + protected: + VIQRCharset* m_pViqr; + UnicodeUTF8Charset* m_pUtf; -public: - UTF8VIQRCharset(UnicodeUTF8Charset *pUtf, VIQRCharset *pViqr); + public: + UTF8VIQRCharset(UnicodeUTF8Charset* pUtf, VIQRCharset* pViqr); virtual void startInput(); virtual void startOutput(); - virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); - virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); + virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); + virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); }; //-------------------------------------------------- class DllInterface CVnCharsetLib { -protected: - SingleByteCharset *m_sgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; - DoubleByteCharset *m_dbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; - UnicodeCharset *m_pUniCharset; - UnicodeCompCharset *m_pUniCompCharset; - UnicodeUTF8Charset *m_pUniUTF8; - UnicodeRefCharset *m_pUniRef; - UnicodeHexCharset *m_pUniHex; - VIQRCharset *m_pVIQRCharObj; - UTF8VIQRCharset *m_pUVIQRCharObj; - WinCP1258Charset *m_pWinCP1258; - UnicodeCStringCharset *m_pUniCString; - VnInternalCharset *m_pVnIntCharset; - -public: - PatternList m_VIQREscPatterns, m_VIQROutEscPatterns; + protected: + SingleByteCharset* m_sgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; + DoubleByteCharset* m_dbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; + UnicodeCharset* m_pUniCharset; + UnicodeCompCharset* m_pUniCompCharset; + UnicodeUTF8Charset* m_pUniUTF8; + UnicodeRefCharset* m_pUniRef; + UnicodeHexCharset* m_pUniHex; + VIQRCharset* m_pVIQRCharObj; + UTF8VIQRCharset* m_pUVIQRCharObj; + WinCP1258Charset* m_pWinCP1258; + UnicodeCStringCharset* m_pUniCString; + VnInternalCharset* m_pVnIntCharset; + + public: + PatternList m_VIQREscPatterns, m_VIQROutEscPatterns; VnConvOptions m_options; CVnCharsetLib(); ~CVnCharsetLib(); - VnCharset *getVnCharset(int charsetIdx); + VnCharset* getVnCharset(int charsetIdx); }; -extern unsigned char SingleByteTables[][TOTAL_VNCHARS]; -extern UKWORD DoubleByteTables[][TOTAL_VNCHARS]; -extern UnicodeChar UnicodeTable[TOTAL_VNCHARS]; -extern UKDWORD VIQRTable[TOTAL_VNCHARS]; -extern UKDWORD UnicodeComposite[TOTAL_VNCHARS]; -extern UKWORD WinCP1258[TOTAL_VNCHARS]; -extern UKWORD WinCP1258Pre[TOTAL_VNCHARS]; +extern unsigned char SingleByteTables[][TOTAL_VNCHARS]; +extern UKWORD DoubleByteTables[][TOTAL_VNCHARS]; +extern UnicodeChar UnicodeTable[TOTAL_VNCHARS]; +extern UKDWORD VIQRTable[TOTAL_VNCHARS]; +extern UKDWORD UnicodeComposite[TOTAL_VNCHARS]; +extern UKWORD WinCP1258[TOTAL_VNCHARS]; +extern UKWORD WinCP1258Pre[TOTAL_VNCHARS]; extern DllInterface CVnCharsetLib VnCharsetLibObj; -extern VnConvOptions VnConvGlobalOptions; -extern int StdVnNoTone[TOTAL_VNCHARS]; -extern int StdVnRootChar[TOTAL_VNCHARS]; +extern VnConvOptions VnConvGlobalOptions; +extern int StdVnNoTone[TOTAL_VNCHARS]; +extern int StdVnRootChar[TOTAL_VNCHARS]; -DllInterface int genConvert(VnCharset &incs, VnCharset &outcs, - ByteInStream &input, ByteOutStream &output); +DllInterface int genConvert(VnCharset& incs, VnCharset& outcs, ByteInStream& input, ByteOutStream& output); -StdVnChar StdVnToUpper(StdVnChar ch); -StdVnChar StdVnToLower(StdVnChar ch); -StdVnChar StdVnGetRoot(StdVnChar ch); +StdVnChar StdVnToUpper(StdVnChar ch); +StdVnChar StdVnToLower(StdVnChar ch); +StdVnChar StdVnGetRoot(StdVnChar ch); #endif diff --git a/unikey/core/convert.cpp b/unikey/core/convert.cpp index 79ca8623..34ceed08 100644 --- a/unikey/core/convert.cpp +++ b/unikey/core/convert.cpp @@ -16,12 +16,11 @@ #include "vnconv.h" -int vnFileStreamConvert(int inCharset, int outCharset, FILE *inf, FILE *outf); +int vnFileStreamConvert(int inCharset, int outCharset, FILE* inf, FILE* outf); -DllExport int genConvert(VnCharset &incs, VnCharset &outcs, ByteInStream &input, - ByteOutStream &output) { +DllExport int genConvert(VnCharset& incs, VnCharset& outcs, ByteInStream& input, ByteOutStream& output) { StdVnChar stdChar; - int bytesRead, bytesWritten; + int bytesRead, bytesWritten; incs.startInput(); outcs.startOutput(); @@ -65,19 +64,18 @@ DllExport int genConvert(VnCharset &incs, VnCharset &outcs, ByteInStream &input, // int VnConvert(int inCharset, int outCharset, UKBYTE *input, UKBYTE *output, // int & inLen, int & maxOutLen) -DllExport int VnConvert(int inCharset, int outCharset, UKBYTE *input, - UKBYTE *output, int *pInLen, int *pMaxOutLen) { +DllExport int VnConvert(int inCharset, int outCharset, UKBYTE* input, UKBYTE* output, int* pInLen, int* pMaxOutLen) { int inLen, maxOutLen; int ret = -1; - inLen = *pInLen; + inLen = *pInLen; maxOutLen = *pMaxOutLen; if (inLen != -1 && inLen < 0) // invalid inLen return ret; - VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset); - VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); + VnCharset* pInCharset = VnCharsetLibObj.getVnCharset(inCharset); + VnCharset* pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); if (!pInCharset || !pOutCharset) return VNCONV_INVALID_CHARSET; @@ -85,9 +83,9 @@ DllExport int VnConvert(int inCharset, int outCharset, UKBYTE *input, StringBIStream is(input, inLen, pInCharset->elementSize()); StringBOStream os(output, maxOutLen); - ret = genConvert(*pInCharset, *pOutCharset, is, os); + ret = genConvert(*pInCharset, *pOutCharset, is, os); *pMaxOutLen = os.getOutBytes(); - *pInLen = is.left(); + *pInLen = is.left(); return ret; } @@ -99,12 +97,11 @@ DllExport int VnConvert(int inCharset, int outCharset, UKBYTE *input, // 0: successful // errCode: if failed //--------------------------------------- -DllExport int VnFileConvert(int inCharset, int outCharset, const char *inFile, - const char *outFile) { - FILE *inf = NULL; - FILE *outf = NULL; - int ret = 0; - char tmpName[32]; +DllExport int VnFileConvert(int inCharset, int outCharset, const char* inFile, const char* outFile) { + FILE* inf = NULL; + FILE* outf = NULL; + int ret = 0; + char tmpName[32]; if (inFile == NULL) { inf = stdin; @@ -128,9 +125,9 @@ DllExport int VnFileConvert(int inCharset, int outCharset, const char *inFile, strcpy(outDir, outFile); #if defined(_WIN32) - char *p = strrchr(outDir, '\\'); + char* p = strrchr(outDir, '\\'); #else - char *p = strrchr(outDir, '/'); + char* p = strrchr(outDir, '/'); #endif if (p == NULL) @@ -193,9 +190,9 @@ DllExport int VnFileConvert(int inCharset, int outCharset, const char *inFile, // 0: successful // errCode: if failed //--------------------------------------- -int vnFileStreamConvert(int inCharset, int outCharset, FILE *inf, FILE *outf) { - VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset); - VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); +int vnFileStreamConvert(int inCharset, int outCharset, FILE* inf, FILE* outf) { + VnCharset* pInCharset = VnCharsetLibObj.getVnCharset(inCharset); + VnCharset* pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); if (!pInCharset || !pOutCharset) return VNCONV_INVALID_CHARSET; @@ -214,17 +211,11 @@ int vnFileStreamConvert(int inCharset, int outCharset, FILE *inf, FILE *outf) { return genConvert(*pInCharset, *pOutCharset, is, os); } -const char *ErrTable[VNCONV_LAST_ERROR] = { - "No error", - "Unknown error", - "Invalid charset", - "Error opening input file", - "Error opening output file", - "Error writing to output stream", - "Not enough memory", +const char* ErrTable[VNCONV_LAST_ERROR] = { + "No error", "Unknown error", "Invalid charset", "Error opening input file", "Error opening output file", "Error writing to output stream", "Not enough memory", }; -DllExport const char *VnConvErrMsg(int errCode) { +DllExport const char* VnConvErrMsg(int errCode) { if (errCode < 0 || errCode >= VNCONV_LAST_ERROR) errCode = VNCONV_UNKNOWN_ERROR; return ErrTable[errCode]; diff --git a/unikey/core/data.cpp b/unikey/core/data.cpp index 2e3539d5..c4b99eb1 100644 --- a/unikey/core/data.cpp +++ b/unikey/core/data.cpp @@ -39,29 +39,17 @@ Steps to add a 2-byte charset: low byte is base character, high byte is tone mark (if present). */ extern CharsetNameId CharsetIdMap[]; -extern const int CharsetCount; +extern const int CharsetCount; -CharsetNameId CharsetIdMap[] = {{"BKHCM1", CONV_CHARSET_BKHCM1}, - {"BKHCM2", CONV_CHARSET_BKHCM2}, - {"ISC", CONV_CHARSET_ISC}, - {"NCR-DEC", CONV_CHARSET_UNIREF}, - {"NCR-HEX", CONV_CHARSET_UNIREF_HEX}, - {"TCVN3", CONV_CHARSET_TCVN3}, - {"UNI-COMP", CONV_CHARSET_UNIDECOMPOSED}, - {"UNICODE", CONV_CHARSET_UNICODE}, - {"UTF-8", CONV_CHARSET_UNIUTF8}, - {"UTF8", CONV_CHARSET_UNIUTF8}, - {"UVIQR", CONV_CHARSET_UTF8VIQR}, - {"VIETWARE-F", CONV_CHARSET_VIETWAREF}, - {"VIETWARE-X", CONV_CHARSET_VIETWAREX}, - {"VIQR", CONV_CHARSET_VIQR}, - {"VISCII", CONV_CHARSET_VISCII}, - {"VNI-MAC", CONV_CHARSET_VNIMAC}, - {"VNI-WIN", CONV_CHARSET_VNIWIN}, - {"VPS", CONV_CHARSET_VPS}, - {"WINCP-1258", CONV_CHARSET_WINCP1258}}; +CharsetNameId CharsetIdMap[] = {{"BKHCM1", CONV_CHARSET_BKHCM1}, {"BKHCM2", CONV_CHARSET_BKHCM2}, {"ISC", CONV_CHARSET_ISC}, + {"NCR-DEC", CONV_CHARSET_UNIREF}, {"NCR-HEX", CONV_CHARSET_UNIREF_HEX}, {"TCVN3", CONV_CHARSET_TCVN3}, + {"UNI-COMP", CONV_CHARSET_UNIDECOMPOSED}, {"UNICODE", CONV_CHARSET_UNICODE}, {"UTF-8", CONV_CHARSET_UNIUTF8}, + {"UTF8", CONV_CHARSET_UNIUTF8}, {"UVIQR", CONV_CHARSET_UTF8VIQR}, {"VIETWARE-F", CONV_CHARSET_VIETWAREF}, + {"VIETWARE-X", CONV_CHARSET_VIETWAREX}, {"VIQR", CONV_CHARSET_VIQR}, {"VISCII", CONV_CHARSET_VISCII}, + {"VNI-MAC", CONV_CHARSET_VNIMAC}, {"VNI-WIN", CONV_CHARSET_VNIWIN}, {"VPS", CONV_CHARSET_VPS}, + {"WINCP-1258", CONV_CHARSET_WINCP1258}}; -const int CharsetCount = sizeof(CharsetIdMap) / sizeof(CharsetNameId); +const int CharsetCount = sizeof(CharsetIdMap) / sizeof(CharsetNameId); /* Western symbols that need to be mapped 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, @@ -1363,268 +1351,172 @@ unsigned char SingleByteTables[][TOTAL_VNCHARS] = UKWORD DoubleByteTables[][TOTAL_VNCHARS] = { // VNI-WIN - {0x0041, 0x0061, 0xd941, 0xf961, 0xd841, 0xf861, 0xdb41, 0xfb61, 0xd541, - 0xf561, 0xcf41, 0xef61, // a - 0xc241, 0xe261, 0xc141, 0xe161, 0xc041, 0xe061, 0xc541, 0xe561, 0xc341, - 0xe361, 0xc441, 0xe461, // a^ - 0xca41, 0xea61, 0xc941, 0xe961, 0xc841, 0xe861, 0xda41, 0xfa61, 0xdc41, - 0xfc61, 0xcb41, 0xeb61, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00d1, 0x00f1, // DD, dd - 0x0045, 0x0065, 0xd945, 0xf965, 0xd845, 0xf865, 0xdb45, 0xfb65, 0xd545, - 0xf565, 0xcf45, 0xef65, // e - 0xc245, 0xe265, 0xc145, 0xe165, 0xc045, 0xe065, 0xc545, 0xe565, 0xc345, - 0xe365, 0xc445, 0xe465, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x00c6, 0x00e6, 0x00d3, - 0x00f3, 0x00d2, 0x00f2, // i + {0x0041, 0x0061, 0xd941, 0xf961, 0xd841, 0xf861, 0xdb41, 0xfb61, 0xd541, 0xf561, 0xcf41, 0xef61, // a + 0xc241, 0xe261, 0xc141, 0xe161, 0xc041, 0xe061, 0xc541, 0xe561, 0xc341, 0xe361, 0xc441, 0xe461, // a^ + 0xca41, 0xea61, 0xc941, 0xe961, 0xc841, 0xe861, 0xda41, 0xfa61, 0xdc41, 0xfc61, 0xcb41, 0xeb61, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d1, 0x00f1, // DD, dd + 0x0045, 0x0065, 0xd945, 0xf965, 0xd845, 0xf865, 0xdb45, 0xfb65, 0xd545, 0xf565, 0xcf45, 0xef65, // e + 0xc245, 0xe265, 0xc145, 0xe165, 0xc045, 0xe065, 0xc545, 0xe565, 0xc345, 0xe365, 0xc445, 0xe465, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x00c6, 0x00e6, 0x00d3, 0x00f3, 0x00d2, 0x00f2, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0xd94f, 0xf96f, 0xd84f, 0xf86f, 0xdb4f, 0xfb6f, 0xd54f, - 0xf56f, 0xcf4f, 0xef6f, // o - 0xc24f, 0xe26f, 0xc14f, 0xe16f, 0xc04f, 0xe06f, 0xc54f, 0xe56f, 0xc34f, - 0xe36f, 0xc44f, 0xe46f, // o^ - 0x00d4, 0x00f4, 0xd9d4, 0xf9f4, 0xd8d4, 0xf8f4, 0xdbd4, 0xfbf4, 0xd5d4, - 0xf5f4, 0xcfd4, 0xeff4, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xd94f, 0xf96f, 0xd84f, 0xf86f, 0xdb4f, 0xfb6f, 0xd54f, 0xf56f, 0xcf4f, 0xef6f, // o + 0xc24f, 0xe26f, 0xc14f, 0xe16f, 0xc04f, 0xe06f, 0xc54f, 0xe56f, 0xc34f, 0xe36f, 0xc44f, 0xe46f, // o^ + 0x00d4, 0x00f4, 0xd9d4, 0xf9f4, 0xd8d4, 0xf8f4, 0xdbd4, 0xfbf4, 0xd5d4, 0xf5f4, 0xcfd4, 0xeff4, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xd955, 0xf975, 0xd855, 0xf875, 0xdb55, 0xfb75, 0xd555, - 0xf575, 0xcf55, 0xef75, // u - 0x00d6, 0x00f6, 0xd9d6, 0xf9f6, 0xd8d6, 0xf8f6, 0xdbd6, 0xfbf6, 0xd5d6, - 0xf5f6, 0xcfd6, 0xeff6, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xd959, 0xf979, 0xd859, 0xf879, 0xdb59, 0xfb79, 0xd559, - 0xf579, 0x00ce, 0x00ee, // y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, - 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, - 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xd955, 0xf975, 0xd855, 0xf875, 0xdb55, 0xfb75, 0xd555, 0xf575, 0xcf55, 0xef75, // u + 0x00d6, 0x00f6, 0xd9d6, 0xf9f6, 0xd8d6, 0xf8f6, 0xdbd6, 0xfbf6, 0xd5d6, 0xf5f6, 0xcfd6, 0xeff6, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xd959, 0xf979, 0xd859, 0xf879, 0xdb59, 0xfb79, 0xd559, 0xf579, 0x00ce, 0x00ee, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, + 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, // BKHCM2 - {0x0041, 0x0061, 0xC141, 0xe161, 0xC241, 0xe261, 0xC341, - 0xe361, 0xC441, 0xe461, 0xC541, 0xe561, // a - 0x00CA, 0x00EA, 0xCBCA, 0xEBEA, 0xCCCA, 0xECEA, 0xCDCA, - 0xEDEA, 0xCECA, 0xEEEA, 0xC5CA, 0xE5EA, // a^ - 0x00D9, 0x00F9, 0xC6D9, 0xE6F9, 0xC7D9, 0xE7F9, 0xC8D9, - 0xE8F9, 0xC9D9, 0xE9F9, 0xC5D9, 0xE5F9, 0x0042, 0x0062, - 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00C0, 0x00E0, 0x0045, 0x0065, 0xC145, 0xE165, 0xC245, - 0xE265, 0xC345, 0xE365, 0xC445, 0xE465, 0xC545, 0xE565, // e - 0x00CF, 0x00EF, 0xCBCF, 0xEBEF, 0xCCCF, 0xECEF, 0xCDCF, - 0xEDEF, 0xCECF, 0xEEEF, 0xE5CF, 0xE5EF, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00D1, 0x00F1, 0x00D2, 0x00F2, 0x00D3, - 0x00F3, 0x00D4, 0x00F4, 0x00D5, 0x00F5, // i - 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, - 0x006d, 0x004e, 0x006e, // J j K k L l M m N n - 0x004F, 0x006F, 0xC14F, 0xE16F, 0xC24F, 0xE26F, 0xC34F, - 0xE36F, 0xC44F, 0xE46F, 0xC54F, 0xE56F, // o - 0x00D6, 0x00F6, 0xCBD6, 0xEBF6, 0xCCD6, 0xECF6, 0xCDD6, - 0xEDF6, 0xCED6, 0xEEF6, 0xC5D6, 0xE5F6, // o^ - 0x00DA, 0x00FA, 0xC1DA, 0xE1FA, 0xC2DA, 0xE2FA, 0xC3DA, - 0xE3FA, 0xC4DA, 0xE4FA, 0xC5DA, 0xE5FA, // o+ - 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, - 0x0073, 0x0054, 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xC155, 0xE175, 0xC255, 0xE275, 0xC355, - 0xE375, 0xC455, 0xE475, 0xC555, 0xE575, // u - 0x00DB, 0x00FB, 0xC1DB, 0xE1FB, 0xC2DB, 0xE2FB, 0xC3DB, - 0xE3FB, 0xC4DB, 0xE4FB, 0xC5DB, 0xE5FB, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xC159, 0xE179, 0xC259, 0xE279, 0xC359, - 0xE379, 0xC459, 0xE479, 0xC559, 0xE579, 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, - 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, - 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + {0x0041, 0x0061, 0xC141, 0xe161, 0xC241, 0xe261, 0xC341, 0xe361, 0xC441, 0xe461, 0xC541, 0xe561, // a + 0x00CA, 0x00EA, 0xCBCA, 0xEBEA, 0xCCCA, 0xECEA, 0xCDCA, 0xEDEA, 0xCECA, 0xEEEA, 0xC5CA, 0xE5EA, // a^ + 0x00D9, 0x00F9, 0xC6D9, 0xE6F9, 0xC7D9, 0xE7F9, 0xC8D9, 0xE8F9, 0xC9D9, 0xE9F9, 0xC5D9, 0xE5F9, 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00C0, 0x00E0, 0x0045, 0x0065, 0xC145, 0xE165, 0xC245, 0xE265, 0xC345, 0xE365, 0xC445, 0xE465, 0xC545, 0xE565, // e + 0x00CF, 0x00EF, 0xCBCF, 0xEBEF, 0xCCCF, 0xECEF, 0xCDCF, 0xEDEF, 0xCECF, 0xEEEF, 0xE5CF, 0xE5EF, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00D1, 0x00F1, 0x00D2, 0x00F2, 0x00D3, 0x00F3, 0x00D4, 0x00F4, 0x00D5, 0x00F5, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, 0x006e, // J j K k L l M m N n + 0x004F, 0x006F, 0xC14F, 0xE16F, 0xC24F, 0xE26F, 0xC34F, 0xE36F, 0xC44F, 0xE46F, 0xC54F, 0xE56F, // o + 0x00D6, 0x00F6, 0xCBD6, 0xEBF6, 0xCCD6, 0xECF6, 0xCDD6, 0xEDF6, 0xCED6, 0xEEF6, 0xC5D6, 0xE5F6, // o^ + 0x00DA, 0x00FA, 0xC1DA, 0xE1FA, 0xC2DA, 0xE2FA, 0xC3DA, 0xE3FA, 0xC4DA, 0xE4FA, 0xC5DA, 0xE5FA, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xC155, 0xE175, 0xC255, 0xE275, 0xC355, 0xE375, 0xC455, 0xE475, 0xC555, 0xE575, // u + 0x00DB, 0x00FB, 0xC1DB, 0xE1FB, 0xC2DB, 0xE2FB, 0xC3DB, 0xE3FB, 0xC4DB, 0xE4FB, 0xC5DB, 0xE5FB, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xC159, 0xE179, 0xC259, 0xE279, 0xC359, 0xE379, 0xC459, 0xE479, 0xC559, 0xE579, 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, // VIETWARE-X - {0x0041, 0x0061, 0xCF41, 0xEF61, 0xCC41, 0xEC61, 0xCD41, 0xED61, 0xCE41, - 0xEE61, 0xDB41, 0xFB61, // a - 0x00C1, 0x00E1, 0xDAC1, 0xFAE1, 0xD6C1, 0xF6E1, 0xD8C1, 0xF8E1, 0xD9C1, - 0xF9E1, 0xDBC1, 0xFBE1, // a^ - 0x00C0, 0x00E0, 0xD5C0, 0xF5E0, 0xD2C0, 0xF2E0, 0xD3C0, 0xF3E0, 0xD4C0, - 0xF4E0, 0xDBC0, 0xFBE0, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00C2, 0x00E2, 0x0045, 0x0065, 0xCF45, 0xEF65, 0xCC45, 0xEC65, 0xCD45, - 0xED65, 0xCE45, 0xEE65, 0xDB45, 0xFB65, // e - 0x00C3, 0x00E3, 0xDAC3, 0xFAE3, 0xD6C3, 0xF6E3, 0xD8C3, 0xF8E3, 0xD9C3, - 0xF9E3, 0xDBC3, 0xFBE3, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00CA, 0x00EA, 0x00C7, 0x00E7, 0x00C8, 0x00E8, 0x00C9, - 0x00E9, 0x00CB, 0x00EB, // i + {0x0041, 0x0061, 0xCF41, 0xEF61, 0xCC41, 0xEC61, 0xCD41, 0xED61, 0xCE41, 0xEE61, 0xDB41, 0xFB61, // a + 0x00C1, 0x00E1, 0xDAC1, 0xFAE1, 0xD6C1, 0xF6E1, 0xD8C1, 0xF8E1, 0xD9C1, 0xF9E1, 0xDBC1, 0xFBE1, // a^ + 0x00C0, 0x00E0, 0xD5C0, 0xF5E0, 0xD2C0, 0xF2E0, 0xD3C0, 0xF3E0, 0xD4C0, 0xF4E0, 0xDBC0, 0xFBE0, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00C2, 0x00E2, 0x0045, 0x0065, 0xCF45, 0xEF65, 0xCC45, 0xEC65, 0xCD45, 0xED65, 0xCE45, 0xEE65, 0xDB45, 0xFB65, // e + 0x00C3, 0x00E3, 0xDAC3, 0xFAE3, 0xD6C3, 0xF6E3, 0xD8C3, 0xF8E3, 0xD9C3, 0xF9E3, 0xDBC3, 0xFBE3, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00CA, 0x00EA, 0x00C7, 0x00E7, 0x00C8, 0x00E8, 0x00C9, 0x00E9, 0x00CB, 0x00EB, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004F, 0x006F, 0xCF4F, 0xEF6F, 0xCC4F, 0xEC6F, 0xCD4F, 0xED6F, 0xCE4F, - 0xEE6F, 0xDC4F, 0xFC6F, // o - 0x00C4, 0x00E4, 0xDAC4, 0xFAE4, 0xD6C4, 0xF6E4, 0xD8C4, 0xF8E4, 0xD9C4, - 0xF9E4, 0xDCC4, 0xFCE4, // o^ - 0x00C5, 0x00E5, 0xCFC5, 0xEFE5, 0xCCC5, 0xECE5, 0xCDC5, 0xEDE5, 0xCEC5, - 0xEEE5, 0xDCC5, 0xFCE5, // o+ + 0x006e, // J j K k L l M m N n + 0x004F, 0x006F, 0xCF4F, 0xEF6F, 0xCC4F, 0xEC6F, 0xCD4F, 0xED6F, 0xCE4F, 0xEE6F, 0xDC4F, 0xFC6F, // o + 0x00C4, 0x00E4, 0xDAC4, 0xFAE4, 0xD6C4, 0xF6E4, 0xD8C4, 0xF8E4, 0xD9C4, 0xF9E4, 0xDCC4, 0xFCE4, // o^ + 0x00C5, 0x00E5, 0xCFC5, 0xEFE5, 0xCCC5, 0xECE5, 0xCDC5, 0xEDE5, 0xCEC5, 0xEEE5, 0xDCC5, 0xFCE5, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xCF55, 0xEF75, 0xCC55, 0xEC75, 0xCD55, 0xED75, 0xCE55, - 0xEE75, 0xDB55, 0xFB75, // u - 0x00C6, 0x00E6, 0xCFC6, 0xEFE6, 0xCCC6, 0xECE6, 0xCDC6, 0xEDE6, 0xCEC6, - 0xEEE6, 0xDBC6, 0xFBE6, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xCF59, 0xEF79, 0xCC59, 0xEC79, 0xCD59, 0xED79, 0xCE59, - 0xEE79, 0xD159, 0xF179, // Y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, - 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, - 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xCF55, 0xEF75, 0xCC55, 0xEC75, 0xCD55, 0xED75, 0xCE55, 0xEE75, 0xDB55, 0xFB75, // u + 0x00C6, 0x00E6, 0xCFC6, 0xEFE6, 0xCCC6, 0xECE6, 0xCDC6, 0xEDE6, 0xCEC6, 0xEEE6, 0xDBC6, 0xFBE6, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xCF59, 0xEF79, 0xCC59, 0xEC79, 0xCD59, 0xED79, 0xCE59, 0xEE79, 0xD159, 0xF179, // Y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, + 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, // VNI-MAC - {0x0041, 0x0061, 0xf441, 0x9d61, 0xaf41, 0xbf61, 0xf341, 0x9e61, 0xcd41, - 0x9b61, 0xec41, 0x9561, // a - 0xe541, 0x8961, 0xe741, 0x8761, 0xcb41, 0x8861, 0x8141, 0x8c61, 0xcc41, - 0x8b61, 0x8041, 0x8a61, // a^ - 0xe641, 0x9061, 0x8341, 0x8e61, 0xe941, 0x8f61, 0xf241, 0x9c61, 0x8641, - 0x9f61, 0xe841, 0x9161, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x0084, 0x0096, // DD, dd - 0x0045, 0x0065, 0xf445, 0x9d65, 0xaf45, 0xbf65, 0xf345, 0x9e65, 0xcd45, - 0x9b65, 0xec45, 0x9565, // e - 0xe545, 0x8965, 0xe745, 0x8765, 0xcb45, 0x8865, 0x8145, 0x8c65, 0xcc45, - 0x8b65, 0x8045, 0x8a65, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00ea, 0x0092, 0x00ed, 0x0093, 0x00ae, 0x00be, 0x00ee, - 0x0097, 0x00f1, 0x0098, // i + {0x0041, 0x0061, 0xf441, 0x9d61, 0xaf41, 0xbf61, 0xf341, 0x9e61, 0xcd41, 0x9b61, 0xec41, 0x9561, // a + 0xe541, 0x8961, 0xe741, 0x8761, 0xcb41, 0x8861, 0x8141, 0x8c61, 0xcc41, 0x8b61, 0x8041, 0x8a61, // a^ + 0xe641, 0x9061, 0x8341, 0x8e61, 0xe941, 0x8f61, 0xf241, 0x9c61, 0x8641, 0x9f61, 0xe841, 0x9161, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x0084, 0x0096, // DD, dd + 0x0045, 0x0065, 0xf445, 0x9d65, 0xaf45, 0xbf65, 0xf345, 0x9e65, 0xcd45, 0x9b65, 0xec45, 0x9565, // e + 0xe545, 0x8965, 0xe745, 0x8765, 0xcb45, 0x8865, 0x8145, 0x8c65, 0xcc45, 0x8b65, 0x8045, 0x8a65, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00ea, 0x0092, 0x00ed, 0x0093, 0x00ae, 0x00be, 0x00ee, 0x0097, 0x00f1, 0x0098, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0xf44f, 0x9d6f, 0xaf4f, 0xbf6f, 0xf34f, 0x9e6f, 0xcd4f, - 0x9b6f, 0xec4f, 0x956f, // o - 0xe54f, 0x896f, 0xe74f, 0x876f, 0xcb4f, 0x886f, 0x814f, 0x8c6f, 0xcc4f, - 0x8b6f, 0x804f, 0x8a6f, // o^ - 0x00ef, 0x0099, 0xf4ef, 0x9d99, 0xafef, 0xbf99, 0xf3ef, 0x9e99, 0xcdef, - 0x9b99, 0xecef, 0x9599, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xf44f, 0x9d6f, 0xaf4f, 0xbf6f, 0xf34f, 0x9e6f, 0xcd4f, 0x9b6f, 0xec4f, 0x956f, // o + 0xe54f, 0x896f, 0xe74f, 0x876f, 0xcb4f, 0x886f, 0x814f, 0x8c6f, 0xcc4f, 0x8b6f, 0x804f, 0x8a6f, // o^ + 0x00ef, 0x0099, 0xf4ef, 0x9d99, 0xafef, 0xbf99, 0xf3ef, 0x9e99, 0xcdef, 0x9b99, 0xecef, 0x9599, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xf455, 0x9d75, 0xaf55, 0xbf75, 0xf355, 0x9e75, 0xcd55, - 0x9b75, 0xec55, 0x9575, // u - 0x0085, 0x009a, 0xf485, 0x9d9a, 0xaf85, 0xbf9a, 0xf385, 0x9e9a, 0xcd85, - 0x9b9a, 0xec85, 0x959a, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xf459, 0x9d79, 0xaf59, 0xbf79, 0xf359, 0x9e79, 0xcd59, - 0x9b79, 0x00eb, 0x0094, // y - 0x005a, 0x007a, // Z z - 0x00db, 0x00e2, 0x00c4, 0x00e3, 0x00c9, 0x00a0, 0x00e0, 0x00f6, 0x00e4, - 0x003f, 0x00dc, 0x00ce, 0x003f, 0x00d4, 0x00d5, 0x00d2, 0x00d3, 0x00a5, - 0x00d0, 0x00d1, 0x00f7, 0x00aa, 0x003f, 0x00dd, 0x00cf, 0x003f, 0x00d9}}; + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xf455, 0x9d75, 0xaf55, 0xbf75, 0xf355, 0x9e75, 0xcd55, 0x9b75, 0xec55, 0x9575, // u + 0x0085, 0x009a, 0xf485, 0x9d9a, 0xaf85, 0xbf9a, 0xf385, 0x9e9a, 0xcd85, 0x9b9a, 0xec85, 0x959a, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xf459, 0x9d79, 0xaf59, 0xbf79, 0xf359, 0x9e79, 0xcd59, 0x9b79, 0x00eb, 0x0094, // y + 0x005a, 0x007a, // Z z + 0x00db, 0x00e2, 0x00c4, 0x00e3, 0x00c9, 0x00a0, 0x00e0, 0x00f6, 0x00e4, 0x003f, 0x00dc, 0x00ce, 0x003f, 0x00d4, + 0x00d5, 0x00d2, 0x00d3, 0x00a5, 0x00d0, 0x00d1, 0x00f7, 0x00aa, 0x003f, 0x00dd, 0x00cf, 0x003f, 0x00d9}}; UKWORD WinCP1258[TOTAL_VNCHARS] = // Windows CP 1258 - {0x0041, 0x0061, 0xec41, 0xec61, 0xcc41, 0xcc61, 0xd241, 0xd261, 0xde41, - 0xde61, 0xf241, 0xf261, // a - 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, - 0xdee2, 0xf2c2, 0xf2e2, // a^ - 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, - 0xdee3, 0xf2c3, 0xf2e3, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00d0, 0x00f0, // DD, dd - 0x0045, 0x0065, 0xec45, 0xec65, 0xcc45, 0xcc65, 0xd245, 0xd265, 0xde45, - 0xde65, 0xf245, 0xf265, // e - 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, - 0xdeea, 0xf2ca, 0xf2ea, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0xec49, 0xec69, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, - 0xde69, 0xf249, 0xf269, // i + {0x0041, 0x0061, 0xec41, 0xec61, 0xcc41, 0xcc61, 0xd241, 0xd261, 0xde41, 0xde61, 0xf241, 0xf261, // a + 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, 0xdee2, 0xf2c2, 0xf2e2, // a^ + 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, 0xdee3, 0xf2c3, 0xf2e3, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d0, 0x00f0, // DD, dd + 0x0045, 0x0065, 0xec45, 0xec65, 0xcc45, 0xcc65, 0xd245, 0xd265, 0xde45, 0xde65, 0xf245, 0xf265, // e + 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, 0xdeea, 0xf2ca, 0xf2ea, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0xec49, 0xec69, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, 0xde69, 0xf249, 0xf269, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0xec4f, 0xec6f, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, - 0xde6f, 0xf24f, 0xf26f, // o - 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, - 0xdef4, 0xf2d4, 0xf2f4, // o^ - 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, - 0xdef5, 0xf2d5, 0xf2f5, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xec4f, 0xec6f, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, 0xde6f, 0xf24f, 0xf26f, // o + 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, 0xdef4, 0xf2d4, 0xf2f4, // o^ + 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, 0xdef5, 0xf2d5, 0xf2f5, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xec55, 0xec75, 0xcc55, 0xcc75, 0xd255, 0xd275, 0xde55, - 0xde75, 0xf255, 0xf275, // u - 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, - 0xdefd, 0xf2dd, 0xf2fd, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, - 0xde79, 0xf259, 0xf279, // y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, - 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, - 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xec55, 0xec75, 0xcc55, 0xcc75, 0xd255, 0xd275, 0xde55, 0xde75, 0xf255, 0xf275, // u + 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, 0xdefd, 0xf2dd, 0xf2fd, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, 0xde79, 0xf259, 0xf279, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, + 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; UKWORD WinCP1258Pre[TOTAL_VNCHARS] = // Windows CP1258 - with some more precomposed characters - {0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0xd241, 0xd261, 0xde41, - 0xde61, 0xf241, 0xf261, // a - 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, - 0xdee2, 0xf2c2, 0xf2e2, // a^ - 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, - 0xdee3, 0xf2c3, 0xf2e3, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00d0, 0x00f0, // DD, dd - 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0xd245, 0xd265, 0xde45, - 0xde65, 0xf245, 0xf265, // e - 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, - 0xdeea, 0xf2ca, 0xf2ea, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00cd, 0x00ed, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, - 0xde69, 0xf249, 0xf269, // i + {0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0xd241, 0xd261, 0xde41, 0xde61, 0xf241, 0xf261, // a + 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, 0xdee2, 0xf2c2, 0xf2e2, // a^ + 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, 0xdee3, 0xf2c3, 0xf2e3, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d0, 0x00f0, // DD, dd + 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0xd245, 0xd265, 0xde45, 0xde65, 0xf245, 0xf265, // e + 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, 0xdeea, 0xf2ca, 0xf2ea, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, 0xde69, 0xf249, 0xf269, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0x00d3, 0x00f3, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, - 0xde6f, 0xf24f, 0xf26f, // o - 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, - 0xdef4, 0xf2d4, 0xf2f4, // o^ - 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, - 0xdef5, 0xf2d5, 0xf2f5, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0x00d3, 0x00f3, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, 0xde6f, 0xf24f, 0xf26f, // o + 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, 0xdef4, 0xf2d4, 0xf2f4, // o^ + 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, 0xdef5, 0xf2d5, 0xf2f5, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0xd255, 0xd275, 0xde55, - 0xde75, 0xf255, 0xf275, // u - 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, - 0xdefd, 0xf2dd, 0xf2fd, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, - 0xde79, 0xf259, 0xf279, // y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, - 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, - 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0xd255, 0xd275, 0xde55, 0xde75, 0xf255, 0xf275, // u + 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, 0xdefd, 0xf2dd, 0xf2fd, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, 0xde79, 0xf259, 0xf279, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, + 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; -UnicodeChar UnicodeTable[TOTAL_VNCHARS] = { - 0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0x1ea2, 0x1ea3, 0x00c3, - 0x00e3, 0x1ea0, 0x1ea1, // a - 0x00c2, 0x00e2, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7, 0x1ea8, 0x1ea9, 0x1eaa, - 0x1eab, 0x1eac, 0x1ead, // a^ - 0x0102, 0x0103, 0x1eae, 0x1eaf, 0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, - 0x1eb5, 0x1eb6, 0x1eb7, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x0110, 0x0111, // DD, dd - 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0x1eba, 0x1ebb, 0x1ebc, - 0x1ebd, 0x1eb8, 0x1eb9, // e - 0x00ca, 0x00ea, 0x1ebe, 0x1ebf, 0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, - 0x1ec5, 0x1ec6, 0x1ec7, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x1ec8, 0x1ec9, 0x0128, - 0x0129, 0x1eca, 0x1ecb, // i - 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0x00d3, 0x00f3, 0x00d2, 0x00f2, 0x1ece, 0x1ecf, 0x00d5, - 0x00f5, 0x1ecc, 0x1ecd, // o - 0x00d4, 0x00f4, 0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, - 0x1ed7, 0x1ed8, 0x1ed9, // o^ - 0x01a0, 0x01a1, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf, 0x1ee0, - 0x1ee1, 0x1ee2, 0x1ee3, // o+ - 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0x1ee6, 0x1ee7, 0x0168, - 0x0169, 0x1ee4, 0x1ee5, // u - 0x01af, 0x01b0, 0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, - 0x1eef, 0x1ef0, 0x1ef1, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0x00dd, 0x00fd, 0x1ef2, 0x1ef3, 0x1ef6, 0x1ef7, 0x1ef8, - 0x1ef9, 0x1ef4, 0x1ef5, // y - 0x005a, 0x007a, // Z z - // Symbols that have different code points in Unicode and Western charsets - 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, - 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, - 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; +UnicodeChar UnicodeTable[TOTAL_VNCHARS] = {0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0x1ea2, 0x1ea3, 0x00c3, 0x00e3, 0x1ea0, 0x1ea1, // a + 0x00c2, 0x00e2, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7, 0x1ea8, 0x1ea9, 0x1eaa, 0x1eab, 0x1eac, 0x1ead, // a^ + 0x0102, 0x0103, 0x1eae, 0x1eaf, 0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, 0x1eb5, 0x1eb6, 0x1eb7, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x0110, 0x0111, // DD, dd + 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0x1eba, 0x1ebb, 0x1ebc, 0x1ebd, 0x1eb8, 0x1eb9, // e + 0x00ca, 0x00ea, 0x1ebe, 0x1ebf, 0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, 0x1ec5, 0x1ec6, 0x1ec7, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x1ec8, 0x1ec9, 0x0128, 0x0129, 0x1eca, 0x1ecb, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0x00d3, 0x00f3, 0x00d2, 0x00f2, 0x1ece, 0x1ecf, 0x00d5, 0x00f5, 0x1ecc, 0x1ecd, // o + 0x00d4, 0x00f4, 0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, 0x1ed7, 0x1ed8, 0x1ed9, // o^ + 0x01a0, 0x01a1, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf, 0x1ee0, 0x1ee1, 0x1ee2, 0x1ee3, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0x1ee6, 0x1ee7, 0x0168, 0x0169, 0x1ee4, 0x1ee5, // u + 0x01af, 0x01b0, 0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, 0x1eef, 0x1ef0, 0x1ef1, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0x00dd, 0x00fd, 0x1ef2, 0x1ef3, 0x1ef6, 0x1ef7, 0x1ef8, 0x1ef9, 0x1ef4, 0x1ef5, // y + 0x005a, 0x007a, // Z z + // Symbols that have different code points in Unicode and Western charsets + 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, + 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; /* unsigned char WesternSymbols[] = @@ -1646,147 +1538,114 @@ unsigned char WesternSymbols[] = + 0x2b */ -UKDWORD VIQRTable[TOTAL_VNCHARS] = { - 0x41, 0x61, 0x2741, 0x2761, 0x6041, 0x6061, 0x3f41, - 0x3f61, 0x7e41, 0x7e61, 0x2e41, 0x2e61, // a - 0x5e41, 0x5e61, 0x275e41, 0x275e61, 0x605e41, 0x605e61, 0x3f5e41, - 0x3f5e61, 0x7e5e41, 0x7e5e61, 0x2e5e41, 0x2e5e61, // a^ - 0x2841, 0x2861, 0x272841, 0x272861, 0x602841, 0x602861, 0x3f2841, - 0x3f2861, 0x7e2841, 0x7e2861, 0x2e2841, 0x2e2861, // a( - 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, // B b C c D d - 0x4444, 0x6464, // DD, dd - 0x45, 0x65, 0x2745, 0x2765, 0x6045, 0x6065, 0x3f45, - 0x3f65, 0x7e45, 0x7e65, 0x2e45, 0x2e65, // e - 0x5e45, 0x5e65, 0x275e45, 0x275e65, 0x605e45, 0x605e65, 0x3f5e45, - 0x3f5e65, 0x7e5e45, 0x7e5e65, 0x2e5e45, 0x2e5e65, // e^ - 0x46, 0x66, 0x47, 0x67, 0x48, 0x68, // F f G g H h - 0x49, 0x69, 0x2749, 0x2769, 0x6049, 0x6069, 0x3f49, - 0x3f69, 0x7e49, 0x7e69, 0x2e49, 0x2e69, // i - 0x4a, 0x6a, 0x4b, 0x6b, 0x4c, 0x6c, 0x4d, - 0x6d, 0x4e, 0x6e, // J j K k L l M m N n - 0x4f, 0x6f, 0x274f, 0x276f, 0x604f, 0x606f, 0x3f4f, - 0x3f6f, 0x7e4f, 0x7e6f, 0x2e4f, 0x2e6f, // o - 0x5e4f, 0x5e6f, 0x275e4f, 0x275e6f, 0x605e4f, 0x605e6f, 0x3f5e4f, - 0x3f5e6f, 0x7e5e4f, 0x7e5e6f, 0x2e5e4f, 0x2e5e6f, // o^ - 0x2b4f, 0x2b6f, 0x272b4f, 0x272b6f, 0x602b4f, 0x602b6f, 0x3f2b4f, - 0x3f2b6f, 0x7e2b4f, 0x7e2b6f, 0x2e2b4f, 0x2e2b6f, // o+ - 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, - 0x73, 0x54, 0x74, // P p Q q R r S s T t - 0x55, 0x75, 0x2755, 0x2775, 0x6055, 0x6075, 0x3f55, - 0x3f75, 0x7e55, 0x7e75, 0x2e55, 0x2e75, // u - 0x2b55, 0x2b75, 0x272b55, 0x272b75, 0x602b55, 0x602b75, 0x3f2b55, - 0x3f2b75, 0x7e2b55, 0x7e2b75, 0x2e2b55, 0x2e2b75, // u+ - 0x56, 0x76, 0x57, 0x77, 0x58, 0x78, // V v W w X x - 0x59, 0x79, 0x2759, 0x2779, 0x6059, 0x6079, 0x3f59, - 0x3f79, 0x7e59, 0x7e79, 0x2e59, 0x2e79, 0x5a, 0x7a, // Z z - 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F}; +UKDWORD VIQRTable[TOTAL_VNCHARS] = {0x41, 0x61, 0x2741, 0x2761, 0x6041, 0x6061, 0x3f41, 0x3f61, 0x7e41, 0x7e61, 0x2e41, 0x2e61, // a + 0x5e41, 0x5e61, 0x275e41, 0x275e61, 0x605e41, 0x605e61, 0x3f5e41, 0x3f5e61, 0x7e5e41, 0x7e5e61, 0x2e5e41, 0x2e5e61, // a^ + 0x2841, 0x2861, 0x272841, 0x272861, 0x602841, 0x602861, 0x3f2841, 0x3f2861, 0x7e2841, 0x7e2861, 0x2e2841, 0x2e2861, // a( + 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, // B b C c D d + 0x4444, 0x6464, // DD, dd + 0x45, 0x65, 0x2745, 0x2765, 0x6045, 0x6065, 0x3f45, 0x3f65, 0x7e45, 0x7e65, 0x2e45, 0x2e65, // e + 0x5e45, 0x5e65, 0x275e45, 0x275e65, 0x605e45, 0x605e65, 0x3f5e45, 0x3f5e65, 0x7e5e45, 0x7e5e65, 0x2e5e45, 0x2e5e65, // e^ + 0x46, 0x66, 0x47, 0x67, 0x48, 0x68, // F f G g H h + 0x49, 0x69, 0x2749, 0x2769, 0x6049, 0x6069, 0x3f49, 0x3f69, 0x7e49, 0x7e69, 0x2e49, 0x2e69, // i + 0x4a, 0x6a, 0x4b, 0x6b, 0x4c, 0x6c, 0x4d, 0x6d, 0x4e, 0x6e, // J j K k L l M m N n + 0x4f, 0x6f, 0x274f, 0x276f, 0x604f, 0x606f, 0x3f4f, 0x3f6f, 0x7e4f, 0x7e6f, 0x2e4f, 0x2e6f, // o + 0x5e4f, 0x5e6f, 0x275e4f, 0x275e6f, 0x605e4f, 0x605e6f, 0x3f5e4f, 0x3f5e6f, 0x7e5e4f, 0x7e5e6f, 0x2e5e4f, 0x2e5e6f, // o^ + 0x2b4f, 0x2b6f, 0x272b4f, 0x272b6f, 0x602b4f, 0x602b6f, 0x3f2b4f, 0x3f2b6f, 0x7e2b4f, 0x7e2b6f, 0x2e2b4f, 0x2e2b6f, // o+ + 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, // P p Q q R r S s T t + 0x55, 0x75, 0x2755, 0x2775, 0x6055, 0x6075, 0x3f55, 0x3f75, 0x7e55, 0x7e75, 0x2e55, 0x2e75, // u + 0x2b55, 0x2b75, 0x272b55, 0x272b75, 0x602b55, 0x602b75, 0x3f2b55, 0x3f2b75, 0x7e2b55, 0x7e2b75, 0x2e2b55, 0x2e2b75, // u+ + 0x56, 0x76, 0x57, 0x77, 0x58, 0x78, // V v W w X x + 0x59, 0x79, 0x2759, 0x2779, 0x6059, 0x6079, 0x3f59, 0x3f79, 0x7e59, 0x7e79, 0x2e59, 0x2e79, 0x5a, 0x7a, // Z z + 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F}; UKDWORD UnicodeComposite[TOTAL_VNCHARS] = { 0x00000041, 0x00000061, 0x03010041, 0x03010061, 0x03000041, 0x03000061, // a 0x03090041, 0x03090061, 0x03030041, 0x03030061, 0x03230041, 0x03230061, // a - 0x000000c2, 0x000000e2, 0x030100c2, 0x030100e2, 0x030000c2, 0x030000e2, - 0x030900c2, 0x030900e2, 0x030300c2, 0x030300e2, 0x032300c2, + 0x000000c2, 0x000000e2, 0x030100c2, 0x030100e2, 0x030000c2, 0x030000e2, 0x030900c2, 0x030900e2, 0x030300c2, 0x030300e2, 0x032300c2, 0x032300e2, // a^ - 0x00000102, 0x00000103, 0x03010102, 0x03010103, 0x03000102, 0x03000103, - 0x03090102, 0x03090103, 0x03030102, 0x03030103, 0x03230102, + 0x00000102, 0x00000103, 0x03010102, 0x03010103, 0x03000102, 0x03000103, 0x03090102, 0x03090103, 0x03030102, 0x03030103, 0x03230102, 0x03230103, // a( 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d 0x0110, 0x0111, // 0x00d1, 0x00f1, //DD, dd - 0x00000045, 0x00000065, 0x03010045, 0x03010065, 0x03000045, 0x03000065, - 0x03090045, 0x03090065, 0x03030045, 0x03030065, 0x03230045, 0x03230065, // e + 0x00000045, 0x00000065, 0x03010045, 0x03010065, 0x03000045, 0x03000065, 0x03090045, 0x03090065, 0x03030045, 0x03030065, 0x03230045, 0x03230065, // e - 0x000000ca, 0x000000ea, 0x030100ca, 0x030100ea, 0x030000ca, 0x030000ea, - 0x030900ca, 0x030900ea, 0x030300ca, 0x030300ea, 0x032300ca, + 0x000000ca, 0x000000ea, 0x030100ca, 0x030100ea, 0x030000ca, 0x030000ea, 0x030900ca, 0x030900ea, 0x030300ca, 0x030300ea, 0x032300ca, 0x032300ea, // e^ 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x00000049, 0x00000069, 0x03010049, 0x03010069, 0x03000049, 0x03000069, - 0x03090049, 0x03090069, 0x03030049, 0x03030069, 0x03230049, 0x03230069, // i + 0x00000049, 0x00000069, 0x03010049, 0x03010069, 0x03000049, 0x03000069, 0x03090049, 0x03090069, 0x03030049, 0x03030069, 0x03230049, 0x03230069, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, 0x006e, // J j K k L l M m N n - 0x0000004f, 0x0000006f, 0x0301004f, 0x0301006f, 0x0300004f, 0x0300006f, - 0x0309004f, 0x0309006f, 0x0303004f, 0x0303006f, 0x0323004f, 0x0323006f, // o + 0x0000004f, 0x0000006f, 0x0301004f, 0x0301006f, 0x0300004f, 0x0300006f, 0x0309004f, 0x0309006f, 0x0303004f, 0x0303006f, 0x0323004f, 0x0323006f, // o - 0x000000d4, 0x000000f4, 0x030100d4, 0x030100f4, 0x030000d4, 0x030000f4, - 0x030900d4, 0x030900f4, 0x030300d4, 0x030300f4, 0x032300d4, + 0x000000d4, 0x000000f4, 0x030100d4, 0x030100f4, 0x030000d4, 0x030000f4, 0x030900d4, 0x030900f4, 0x030300d4, 0x030300f4, 0x032300d4, 0x032300f4, // o^ - 0x000001a0, 0x000001a1, 0x030101a0, 0x030101a1, 0x030001a0, 0x030001a1, - 0x030901a0, 0x030901a1, 0x030301a0, 0x030301a1, 0x032301a0, + 0x000001a0, 0x000001a1, 0x030101a0, 0x030101a1, 0x030001a0, 0x030001a1, 0x030901a0, 0x030901a1, 0x030301a0, 0x030301a1, 0x032301a0, 0x032301a1, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, 0x0074, // P p Q q R r S s T t - 0x00000055, 0x00000075, 0x03010055, 0x03010075, 0x03000055, 0x03000075, - 0x03090055, 0x03090075, 0x03030055, 0x03030075, 0x03230055, 0x03230075, // u + 0x00000055, 0x00000075, 0x03010055, 0x03010075, 0x03000055, 0x03000075, 0x03090055, 0x03090075, 0x03030055, 0x03030075, 0x03230055, 0x03230075, // u - 0x000001af, 0x000001b0, 0x030101af, 0x030101b0, 0x030001af, 0x030001b0, - 0x030901af, 0x030901b0, 0x030301af, 0x030301b0, 0x032301af, + 0x000001af, 0x000001b0, 0x030101af, 0x030101b0, 0x030001af, 0x030001b0, 0x030901af, 0x030901b0, 0x030301af, 0x030301b0, 0x032301af, 0x032301b0, // u+ 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x00000059, 0x00000079, 0x03010059, 0x03010079, 0x03000059, 0x03000079, - 0x03090059, 0x03090079, 0x03030059, 0x03030079, 0x03230059, 0x03230079, // y - 0x005a, 0x007a, // Z z + 0x00000059, 0x00000079, 0x03010059, 0x03010079, 0x03000059, 0x03000079, 0x03090059, 0x03090079, 0x03030059, 0x03030079, 0x03230059, 0x03230079, // y + 0x005a, 0x007a, // Z z // Symbols that have different code points in Unicode and Western charsets - 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, - 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, - 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; + 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, + 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; -int StdVnRootChar[TOTAL_VNCHARS] = { - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a^ -> a - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a( -> a - 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] - 40, 41, // DD dd [mapped to D, d] - 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] - 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 4: e^ -> e - 68, 69, 70, 71, 72, 73, // fgh - 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 7: o^ -> o - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 8: o+ -> o - 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst - 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] - 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 10: u+ -> u - 166, 167, 168, 169, 170, 171, // vwx - 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] - 184, 185, // z - 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; +int StdVnRootChar[TOTAL_VNCHARS] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a^ -> a + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a( -> a + 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] + 40, 41, // DD dd [mapped to D, d] + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 4: e^ -> e + 68, 69, 70, 71, 72, 73, // fgh + 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 7: o^ -> o + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 8: o+ -> o + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 10: u+ -> u + 166, 167, 168, 169, 170, 171, // vwx + 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] + 184, 185, // z + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; -int StdVnNoTone[TOTAL_VNCHARS] = { - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] - 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, // a^ - 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, // a( - 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] - 42, 43, // DD dd - 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] - 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, // 4: e^ - 68, 69, 70, 71, 72, 73, // fgh - 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] - 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, // 7: o^ - 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, // 8: o+ - 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst - 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] - 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, // 10: u+ - 166, 167, 168, 169, 170, 171, // vwx - 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] - 184, 185, // z - 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; +int StdVnNoTone[TOTAL_VNCHARS] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, // a^ + 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, // a( + 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] + 42, 43, // DD dd + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] + 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, // 4: e^ + 68, 69, 70, 71, 72, 73, // fgh + 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] + 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, // 7: o^ + 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, // 8: o+ + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] + 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, // 10: u+ + 166, 167, 168, 169, 170, 171, // vwx + 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] + 184, 185, // z + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; diff --git a/unikey/core/data.h b/unikey/core/data.h index fa8e97b3..e402ab4d 100644 --- a/unikey/core/data.h +++ b/unikey/core/data.h @@ -7,12 +7,9 @@ #define VIETNAMESE_CHARSET_DATA_H // This header defines some special characters -const StdVnChar StdStartQuote = - (VnStdCharOffset + 201); // 0x93 in the Western charset +const StdVnChar StdStartQuote = (VnStdCharOffset + 201); // 0x93 in the Western charset // 201 is the offset of character 0x93 (start quote) in Vn charsets -const StdVnChar StdEndQuote = - (VnStdCharOffset + 202); // 0x94 in the Western charset -const StdVnChar StdEllipsis = - (VnStdCharOffset + 190); // 0x85 in Western charet. +const StdVnChar StdEndQuote = (VnStdCharOffset + 202); // 0x94 in the Western charset +const StdVnChar StdEllipsis = (VnStdCharOffset + 190); // 0x85 in Western charet. #endif diff --git a/unikey/core/inputproc.cpp b/unikey/core/inputproc.cpp index dbc9cc5b..bc8f46f4 100644 --- a/unikey/core/inputproc.cpp +++ b/unikey/core/inputproc.cpp @@ -18,112 +18,57 @@ unsigned char WordBreakSyms[] = { */ constexpr UkKeyEvName lexi(VnLexiName v) { - return static_cast( - static_cast(vneCount) + static_cast(v)); + return static_cast(static_cast(vneCount) + static_cast(v)); } -const std::unordered_set WordBreakSyms = { - ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', '<', - '>', '=', '+', '-', '*', '/', '\\', '_', '@', '#', - '$', '%', '&', '(', ')', '{', '}', '[', ']', '|'}; // we excluded ~, `, ^ +const std::unordered_set WordBreakSyms = {',', ';', ':', '.', '\"', '\'', '!', '?', ' ', '<', '>', '=', '+', '-', '*', + '/', '\\', '_', '@', '#', '$', '%', '&', '(', ')', '{', '}', '[', ']', '|'}; // we excluded ~, `, ^ -VnLexiName AZLexiUpper[] = {vnl_A, vnl_B, vnl_C, vnl_D, vnl_E, vnl_F, vnl_G, - vnl_H, vnl_I, vnl_J, vnl_K, vnl_L, vnl_M, vnl_N, - vnl_O, vnl_P, vnl_Q, vnl_R, vnl_S, vnl_T, vnl_U, - vnl_V, vnl_W, vnl_X, vnl_Y, vnl_Z}; +VnLexiName AZLexiUpper[] = {vnl_A, vnl_B, vnl_C, vnl_D, vnl_E, vnl_F, vnl_G, vnl_H, vnl_I, vnl_J, vnl_K, vnl_L, vnl_M, + vnl_N, vnl_O, vnl_P, vnl_Q, vnl_R, vnl_S, vnl_T, vnl_U, vnl_V, vnl_W, vnl_X, vnl_Y, vnl_Z}; -VnLexiName AZLexiLower[] = {vnl_a, vnl_b, vnl_c, vnl_d, vnl_e, vnl_f, vnl_g, - vnl_h, vnl_i, vnl_j, vnl_k, vnl_l, vnl_m, vnl_n, - vnl_o, vnl_p, vnl_q, vnl_r, vnl_s, vnl_t, vnl_u, - vnl_v, vnl_w, vnl_x, vnl_y, vnl_z}; +VnLexiName AZLexiLower[] = {vnl_a, vnl_b, vnl_c, vnl_d, vnl_e, vnl_f, vnl_g, vnl_h, vnl_i, vnl_j, vnl_k, vnl_l, vnl_m, + vnl_n, vnl_o, vnl_p, vnl_q, vnl_r, vnl_s, vnl_t, vnl_u, vnl_v, vnl_w, vnl_x, vnl_y, vnl_z}; -UkCharType UkcMap[256]; +UkCharType UkcMap[256]; struct _ascVnLexi { - int asc; + int asc; VnLexiName lexi; }; // List of western characters outside range A-Z that are // also Vietnamese characters -_ascVnLexi AscVnLexiList[] = { - {0xC0, vnl_A2}, {0xC1, vnl_A1}, {0xC2, vnl_Ar}, {0xC2, vnl_A4}, - {0xC8, vnl_E2}, {0xC9, vnl_E1}, {0xCA, vnl_Er}, {0xCC, vnl_I2}, - {0xCD, vnl_I1}, {0xD2, vnl_O2}, {0xD3, vnl_O1}, {0xD4, vnl_Or}, - {0xD5, vnl_O4}, {0xD9, vnl_U2}, {0xDA, vnl_U1}, {0xDD, vnl_Y1}, - {0xE0, vnl_a2}, {0xE1, vnl_a1}, {0xE2, vnl_ar}, {0xE3, vnl_a4}, - {0xE8, vnl_e2}, {0xE9, vnl_e1}, {0xEA, vnl_er}, {0xEC, vnl_i2}, - {0xED, vnl_i1}, {0xF2, vnl_o2}, {0xF3, vnl_o1}, {0xF4, vnl_or}, - {0xF5, vnl_o4}, {0xF9, vnl_u2}, {0xFA, vnl_u1}, {0xFD, vnl_y1}, - {0x00, vnl_nonVnChar}}; +_ascVnLexi AscVnLexiList[] = {{0xC0, vnl_A2}, {0xC1, vnl_A1}, {0xC2, vnl_Ar}, {0xC2, vnl_A4}, {0xC8, vnl_E2}, {0xC9, vnl_E1}, {0xCA, vnl_Er}, {0xCC, vnl_I2}, {0xCD, vnl_I1}, + {0xD2, vnl_O2}, {0xD3, vnl_O1}, {0xD4, vnl_Or}, {0xD5, vnl_O4}, {0xD9, vnl_U2}, {0xDA, vnl_U1}, {0xDD, vnl_Y1}, {0xE0, vnl_a2}, {0xE1, vnl_a1}, + {0xE2, vnl_ar}, {0xE3, vnl_a4}, {0xE8, vnl_e2}, {0xE9, vnl_e1}, {0xEA, vnl_er}, {0xEC, vnl_i2}, {0xED, vnl_i1}, {0xF2, vnl_o2}, {0xF3, vnl_o1}, + {0xF4, vnl_or}, {0xF5, vnl_o4}, {0xF9, vnl_u2}, {0xFA, vnl_u1}, {0xFD, vnl_y1}, {0x00, vnl_nonVnChar}}; VnLexiName IsoVnLexiMap[256]; -bool ClassifierTableInitialized = false; - -DllExport UkKeyMapping TelexMethodMapping[] = {{'Z', vneTone0}, - {'S', vneTone1}, - {'F', vneTone2}, - {'R', vneTone3}, - {'X', vneTone4}, - {'J', vneTone5}, - {'W', vne_telex_w}, - {'A', vneRoof_a}, - {'E', vneRoof_e}, - {'O', vneRoof_o}, - {'D', vneDd}, - {'[', lexi(vnl_oh)}, - {']', lexi(vnl_uh)}, - {'{', lexi(vnl_Oh)}, - {'}', lexi(vnl_Uh)}, - {0, vneNormal}}; - -DllExport UkKeyMapping SimpleTelexMethodMapping[] = { - {'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, - {'X', vneTone4}, {'J', vneTone5}, {'W', vneHookAll}, {'A', vneRoof_a}, - {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; - -DllExport UkKeyMapping SimpleTelex2MethodMapping[] = { - {'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, - {'X', vneTone4}, {'J', vneTone5}, {'W', vne_telex_w}, {'A', vneRoof_a}, - {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; - -DllExport UkKeyMapping VniMethodMapping[] = { - {'0', vneTone0}, {'1', vneTone1}, {'2', vneTone2}, {'3', vneTone3}, - {'4', vneTone4}, {'5', vneTone5}, {'6', vneRoofAll}, {'7', vneHook_uo}, - {'8', vneBowl}, {'9', vneDd}, {0, vneNormal}}; - -DllExport UkKeyMapping VIQRMethodMapping[] = { - {'0', vneTone0}, {'\'', vneTone1}, {'`', vneTone2}, {'?', vneTone3}, - {'~', vneTone4}, {'.', vneTone5}, {'^', vneRoofAll}, {'+', vneHook_uo}, - {'*', vneHook_uo}, {'(', vneBowl}, {'D', vneDd}, {'\\', vneEscChar}, - {0, vneNormal}}; - -DllExport UkKeyMapping MsViMethodMapping[] = {{'5', vneTone2}, - {'%', vneTone2}, - {'6', vneTone3}, - {'^', vneTone3}, - {'7', vneTone4}, - {'&', vneTone4}, - {'8', vneTone1}, - {'*', vneTone1}, - {'9', vneTone5}, - {'(', vneTone5}, - {'1', lexi(vnl_ab)}, - {'!', lexi(vnl_Ab)}, - {'2', lexi(vnl_ar)}, - {'@', lexi(vnl_Ar)}, - {'3', lexi(vnl_er)}, - {'#', lexi(vnl_Er)}, - {'4', lexi(vnl_or)}, - {'$', lexi(vnl_Or)}, - {'0', lexi(vnl_dd)}, - {')', lexi(vnl_DD)}, - {'[', lexi(vnl_uh)}, - {']', lexi(vnl_oh)}, - {'{', lexi(vnl_Uh)}, - {'}', lexi(vnl_Oh)}, - {0, vneNormal}}; +bool ClassifierTableInitialized = false; + +DllExport UkKeyMapping TelexMethodMapping[] = {{'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, {'X', vneTone4}, {'J', vneTone5}, + {'W', vne_telex_w}, {'A', vneRoof_a}, {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {'[', lexi(vnl_oh)}, + {']', lexi(vnl_uh)}, {'{', lexi(vnl_Oh)}, {'}', lexi(vnl_Uh)}, {0, vneNormal}}; + +DllExport UkKeyMapping SimpleTelexMethodMapping[] = {{'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, {'X', vneTone4}, {'J', vneTone5}, + {'W', vneHookAll}, {'A', vneRoof_a}, {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping SimpleTelex2MethodMapping[] = {{'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, {'X', vneTone4}, {'J', vneTone5}, + {'W', vne_telex_w}, {'A', vneRoof_a}, {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping VniMethodMapping[] = {{'0', vneTone0}, {'1', vneTone1}, {'2', vneTone2}, {'3', vneTone3}, {'4', vneTone4}, {'5', vneTone5}, + {'6', vneRoofAll}, {'7', vneHook_uo}, {'8', vneBowl}, {'9', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping VIQRMethodMapping[] = {{'0', vneTone0}, {'\'', vneTone1}, {'`', vneTone2}, {'?', vneTone3}, {'~', vneTone4}, {'.', vneTone5}, {'^', vneRoofAll}, + {'+', vneHook_uo}, {'*', vneHook_uo}, {'(', vneBowl}, {'D', vneDd}, {'\\', vneEscChar}, {0, vneNormal}}; + +DllExport UkKeyMapping MsViMethodMapping[] = {{'5', vneTone2}, {'%', vneTone2}, {'6', vneTone3}, {'^', vneTone3}, {'7', vneTone4}, + {'&', vneTone4}, {'8', vneTone1}, {'*', vneTone1}, {'9', vneTone5}, {'(', vneTone5}, + {'1', lexi(vnl_ab)}, {'!', lexi(vnl_Ab)}, {'2', lexi(vnl_ar)}, {'@', lexi(vnl_Ar)}, {'3', lexi(vnl_er)}, + {'#', lexi(vnl_Er)}, {'4', lexi(vnl_or)}, {'$', lexi(vnl_Or)}, {'0', lexi(vnl_dd)}, {')', lexi(vnl_DD)}, + {'[', lexi(vnl_uh)}, {']', lexi(vnl_oh)}, {'{', lexi(vnl_Uh)}, {'}', lexi(vnl_Oh)}, {0, vneNormal}}; //------------------------------------------- void SetupInputClassifierTable() { @@ -131,7 +76,7 @@ void SetupInputClassifierTable() { ClassifierTableInitialized = true; } unsigned int c; - int i; + int i; for (c = 0; c <= 32; c++) { UkcMap[c] = ukcReset; @@ -193,27 +138,13 @@ void UkInputProcessor::init() { int UkInputProcessor::setIM(UkInputMethod im) { m_im = im; switch (im) { - case UkTelex: - useBuiltIn(TelexMethodMapping); - break; - case UkSimpleTelex: - useBuiltIn(SimpleTelexMethodMapping); - break; - case UkSimpleTelex2: - useBuiltIn(SimpleTelex2MethodMapping); - break; - case UkVni: - useBuiltIn(VniMethodMapping); - break; - case UkViqr: - useBuiltIn(VIQRMethodMapping); - break; - case UkMsVi: - useBuiltIn(MsViMethodMapping); - break; - default: - m_im = UkTelex; - useBuiltIn(TelexMethodMapping); + case UkTelex: useBuiltIn(TelexMethodMapping); break; + case UkSimpleTelex: useBuiltIn(SimpleTelexMethodMapping); break; + case UkSimpleTelex2: useBuiltIn(SimpleTelex2MethodMapping); break; + case UkVni: useBuiltIn(VniMethodMapping); break; + case UkViqr: useBuiltIn(VIQRMethodMapping); break; + case UkMsVi: useBuiltIn(MsViMethodMapping); break; + default: m_im = UkTelex; useBuiltIn(TelexMethodMapping); } return 1; } @@ -235,7 +166,7 @@ void UkResetKeyMap(int keyMap[256]) { } //------------------------------------------- -void UkInputProcessor::useBuiltIn(UkKeyMapping *map) { +void UkInputProcessor::useBuiltIn(UkKeyMapping* map) { UkResetKeyMap(m_keyMap); for (int i = 0; map[i].key; i++) { m_keyMap[map[i].key] = map[i].action; @@ -250,15 +181,15 @@ void UkInputProcessor::useBuiltIn(UkKeyMapping *map) { } //------------------------------------------- -void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev) { +void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent& ev) { ev.keyCode = keyCode; if (keyCode == 0) { ev.evType = vneNormal; - ev.vnSym = vnl_nonVnChar; + ev.vnSym = vnl_nonVnChar; ev.chType = ukcWordBreak; } else if (keyCode > 255) { ev.evType = vneNormal; - ev.vnSym = IsoToVnLexi(keyCode); + ev.vnSym = IsoToVnLexi(keyCode); ev.chType = (ev.vnSym == vnl_nonVnChar) ? ukcNonVn : ukcVn; } else { ev.chType = UkcMap[keyCode]; @@ -270,7 +201,7 @@ void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev) { if (ev.evType >= vneCount) { ev.chType = ukcVn; - ev.vnSym = (VnLexiName)(ev.evType - vneCount); + ev.vnSym = (VnLexiName)(ev.evType - vneCount); ev.evType = vneMapChar; } else { ev.vnSym = IsoToVnLexi(keyCode); @@ -283,10 +214,10 @@ void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev) { // Key strokes are simply considered character input, not action keys as in // keyCodeToEvent method //---------------------------------------------------------------- -void UkInputProcessor::keyCodeToSymbol(unsigned int keyCode, UkKeyEvent &ev) { +void UkInputProcessor::keyCodeToSymbol(unsigned int keyCode, UkKeyEvent& ev) { ev.keyCode = keyCode; - ev.evType = vneNormal; - ev.vnSym = IsoToVnLexi(keyCode); + ev.evType = vneNormal; + ev.vnSym = IsoToVnLexi(keyCode); if (keyCode > 255) { ev.chType = (ev.vnSym == vnl_nonVnChar) ? ukcNonVn : ukcVn; } else { diff --git a/unikey/core/inputproc.h b/unikey/core/inputproc.h index bf1311f5..5dc71e9c 100644 --- a/unikey/core/inputproc.h +++ b/unikey/core/inputproc.h @@ -48,53 +48,60 @@ enum UkKeyEvName { vneCount // just to count how many event types there are }; -enum UkCharType { ukcVn, ukcWordBreak, ukcNonVn, ukcReset }; +enum UkCharType { + ukcVn, + ukcWordBreak, + ukcNonVn, + ukcReset +}; struct UkKeyEvent { - int evType; - UkCharType chType; - VnLexiName vnSym; // meaningful only when chType==ukcVn + int evType; + UkCharType chType; + VnLexiName vnSym; // meaningful only when chType==ukcVn unsigned int keyCode; - int tone; // meaningful only when this is a vowel + int tone; // meaningful only when this is a vowel }; struct UkKeyMapping { unsigned char key; - int action; + int action; }; /////////////////////////////////////////// class UkInputProcessor { -public: + public: // don't do anything with constructor, because // this object can be allocated in shared memory // Use init method instead // UkInputProcessor(); - void init(); + void init(); - UkInputMethod getIM() const { return m_im; } + UkInputMethod getIM() const { + return m_im; + } - void keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev); - void keyCodeToSymbol(unsigned int keyCode, UkKeyEvent &ev); - int setIM(UkInputMethod im); - int setIM(int map[256]); - void getKeyMap(int map[256]) const; + void keyCodeToEvent(unsigned int keyCode, UkKeyEvent& ev); + void keyCodeToSymbol(unsigned int keyCode, UkKeyEvent& ev); + int setIM(UkInputMethod im); + int setIM(int map[256]); + void getKeyMap(int map[256]) const; UkCharType getCharType(unsigned int keyCode) const; -protected: - static bool m_classInit; + protected: + static bool m_classInit; UkInputMethod m_im; - int m_keyMap[256]; + int m_keyMap[256]; - void useBuiltIn(UkKeyMapping *map); + void useBuiltIn(UkKeyMapping* map); }; -void UkResetKeyMap(int keyMap[256]); -void SetupInputClassifierTable(); +void UkResetKeyMap(int keyMap[256]); +void SetupInputClassifierTable(); DllInterface extern UkKeyMapping TelexMethodMapping[]; DllInterface extern UkKeyMapping SimpleTelexMethodMapping[]; @@ -103,8 +110,8 @@ DllInterface extern UkKeyMapping VniMethodMapping[]; DllInterface extern UkKeyMapping VIQRMethodMapping[]; DllInterface extern UkKeyMapping MsViMethodMapping[]; -extern VnLexiName IsoVnLexiMap[]; -inline VnLexiName IsoToVnLexi(unsigned int keyCode) { +extern VnLexiName IsoVnLexiMap[]; +inline VnLexiName IsoToVnLexi(unsigned int keyCode) { return (keyCode >= 256) ? vnl_nonVnChar : IsoVnLexiMap[keyCode]; } diff --git a/unikey/core/keycons.h b/unikey/core/keycons.h index cdfcb329..0900a168 100644 --- a/unikey/core/keycons.h +++ b/unikey/core/keycons.h @@ -10,8 +10,8 @@ #define MAX_MACRO_KEY_LEN 16 // #define MAX_MACRO_TEXT_LEN 256 #define MAX_MACRO_TEXT_LEN 1024 -#define MAX_MACRO_ITEMS 1024 -#define MAX_MACRO_LINE (MAX_MACRO_TEXT_LEN + MAX_MACRO_KEY_LEN) +#define MAX_MACRO_ITEMS 1024 +#define MAX_MACRO_LINE (MAX_MACRO_TEXT_LEN + MAX_MACRO_KEY_LEN) #define MACRO_MEM_SIZE (1024 * 128) // 128 KB @@ -39,31 +39,34 @@ struct UnikeyOptions { int autoNonVnRestore; }; -#define UKOPT_FLAG_ALL 0xFFFFFFFF +#define UKOPT_FLAG_ALL 0xFFFFFFFF #define UKOPT_FLAG_FREE_STYLE 0x00000001 // #define UKOPT_FLAG_MANUAL_TONE 0x00000002 -#define UKOPT_FLAG_MODERN 0x00000004 -#define UKOPT_FLAG_MACRO_ENABLED 0x00000008 -#define UKOPT_FLAG_USE_CLIPBOARD 0x00000010 -#define UKOPT_FLAG_ALWAYS_MACRO 0x00000020 -#define UKOPT_FLAG_STRICT_SPELL 0x00000040 -#define UKOPT_FLAG_USE_IME 0x00000080 +#define UKOPT_FLAG_MODERN 0x00000004 +#define UKOPT_FLAG_MACRO_ENABLED 0x00000008 +#define UKOPT_FLAG_USE_CLIPBOARD 0x00000010 +#define UKOPT_FLAG_ALWAYS_MACRO 0x00000020 +#define UKOPT_FLAG_STRICT_SPELL 0x00000040 +#define UKOPT_FLAG_USE_IME 0x00000080 #define UKOPT_FLAG_SPELLCHECK_ENABLED 0x00000100 #if defined(WIN32) typedef struct _UnikeySysInfo UnikeySysInfo; struct _UnikeySysInfo { - int switchKey; + int switchKey; HHOOK keyHook; HHOOK mouseHook; - HWND hMainDlg; - UINT iconMsgId; + HWND hMainDlg; + UINT iconMsgId; HICON hVietIcon, hEnIcon; - int unicodePlatform; + int unicodePlatform; DWORD winMajorVersion, winMinorVersion; }; #endif -typedef enum { UkCharOutput, UkKeyOutput } UkOutputType; +typedef enum { + UkCharOutput, + UkKeyOutput +} UkOutputType; #endif diff --git a/unikey/core/mactab.cpp b/unikey/core/mactab.cpp index 990637b2..b54fd536 100644 --- a/unikey/core/mactab.cpp +++ b/unikey/core/mactab.cpp @@ -16,28 +16,22 @@ using namespace std; //--------------------------------------------------------------- void CMacroTable::init() { - m_memSize = MACRO_MEM_SIZE; - m_count = 0; + m_memSize = MACRO_MEM_SIZE; + m_count = 0; m_occupied = 0; } //--------------------------------------------------------------- -char *MacCompareStartMem; +char* MacCompareStartMem; -#define STD_TO_LOWER(x) \ - (((x) >= VnStdCharOffset && \ - (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && !((x) & 1)) \ - ? (x + 1) \ - : (x)) +#define STD_TO_LOWER(x) (((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && !((x) & 1)) ? (x + 1) : (x)) -int macCompare(const void *p1, const void *p2) { - StdVnChar *s1 = - (StdVnChar *)((char *)MacCompareStartMem + ((MacroDef *)p1)->keyOffset); - StdVnChar *s2 = - (StdVnChar *)((char *)MacCompareStartMem + ((MacroDef *)p2)->keyOffset); +int macCompare(const void* p1, const void* p2) { + StdVnChar* s1 = (StdVnChar*)((char*)MacCompareStartMem + ((MacroDef*)p1)->keyOffset); + StdVnChar* s2 = (StdVnChar*)((char*)MacCompareStartMem + ((MacroDef*)p2)->keyOffset); - int i; - StdVnChar ls1, ls2; + int i; + StdVnChar ls1, ls2; for (i = 0; s1[i] != 0 && s2[i] != 0; i++) { ls1 = STD_TO_LOWER(s1[i]); @@ -59,13 +53,12 @@ int macCompare(const void *p1, const void *p2) { } //--------------------------------------------------------------- -int macKeyCompare(const void *key, const void *ele) { - StdVnChar *s1 = (StdVnChar *)key; - StdVnChar *s2 = (StdVnChar *)((char *)MacCompareStartMem + - ((MacroDef *)ele)->keyOffset); +int macKeyCompare(const void* key, const void* ele) { + StdVnChar* s1 = (StdVnChar*)key; + StdVnChar* s2 = (StdVnChar*)((char*)MacCompareStartMem + ((MacroDef*)ele)->keyOffset); - StdVnChar ls1, ls2; - int i; + StdVnChar ls1, ls2; + int i; for (i = 0; s1[i] != 0 && s2[i] != 0; i++) { ls1 = STD_TO_LOWER(s1[i]); ls2 = STD_TO_LOWER(s2[i]); @@ -86,12 +79,11 @@ int macKeyCompare(const void *key, const void *ele) { } //--------------------------------------------------------------- -const StdVnChar *CMacroTable::lookup(StdVnChar *key) { +const StdVnChar* CMacroTable::lookup(StdVnChar* key) { MacCompareStartMem = m_macroMem; - MacroDef *p = (MacroDef *)bsearch(key, m_table, m_count, sizeof(MacroDef), - macKeyCompare); + MacroDef* p = (MacroDef*)bsearch(key, m_table, m_count, sizeof(MacroDef), macKeyCompare); if (p) - return (StdVnChar *)(m_macroMem + p->textOffset); + return (StdVnChar*)(m_macroMem + p->textOffset); return 0; } @@ -102,7 +94,7 @@ const StdVnChar *CMacroTable::lookup(StdVnChar *key) { // // Header format: ;[DO NOT DELETE THIS LINE]***version=n //---------------------------------------------------------------------------- -bool CMacroTable::readHeader(FILE *f, int &version) { +bool CMacroTable::readHeader(FILE* f, int& version) { char line[MAX_MACRO_LINE]; if (!fgets(line, sizeof(line), f)) { if (feof(f)) { @@ -114,10 +106,9 @@ bool CMacroTable::readHeader(FILE *f, int &version) { } // if BOM is available, skip it - char *p = line; + char* p = line; size_t len = strlen(line); - if (len >= 3 && (unsigned char)line[0] == 0xEF && - (unsigned char)line[1] == 0xBB && (unsigned char)line[2] == 0xBF) { + if (len >= 3 && (unsigned char)line[0] == 0xEF && (unsigned char)line[1] == 0xBB && (unsigned char)line[2] == 0xBF) { p += 3; } @@ -138,18 +129,16 @@ bool CMacroTable::readHeader(FILE *f, int &version) { } //---------------------------------------------------------------- -void CMacroTable::writeHeader(FILE *f) { +void CMacroTable::writeHeader(FILE* f) { #if defined(WIN32) - fprintf(f, "\xEF\xBB\xBF;DO NOT DELETE THIS LINE*** version=%d ***\n", - UKMACRO_VERSION_UTF8); + fprintf(f, "\xEF\xBB\xBF;DO NOT DELETE THIS LINE*** version=%d ***\n", UKMACRO_VERSION_UTF8); #else - fprintf(f, "DO NOT DELETE THIS LINE*** version=%d ***\n", - UKMACRO_VERSION_UTF8); + fprintf(f, "DO NOT DELETE THIS LINE*** version=%d ***\n", UKMACRO_VERSION_UTF8); #endif } //--------------------------------------------------------------- -int CMacroTable::loadFromFile(const char *fname) { - FILE *f; +int CMacroTable::loadFromFile(const char* fname) { + FILE* f; #if defined(WIN32) f = _tfopen(fname, _TEXT("rt")); #else @@ -158,7 +147,7 @@ int CMacroTable::loadFromFile(const char *fname) { if (f == NULL) return 0; - char line[MAX_MACRO_LINE]; + char line[MAX_MACRO_LINE]; size_t len; resetContent(); @@ -191,13 +180,13 @@ int CMacroTable::loadFromFile(const char *fname) { } //--------------------------------------------------------------- -int CMacroTable::writeToFile(const char *fname) { - FILE *f; +int CMacroTable::writeToFile(const char* fname) { + FILE* f; f = fopen(fname, "w"); return writeToFp(f); } -int CMacroTable::writeToFp(FILE *f) { +int CMacroTable::writeToFp(FILE* f) { int ret; int inLen, maxOutLen; @@ -210,21 +199,19 @@ int CMacroTable::writeToFp(FILE *f) { writeHeader(f); - UKBYTE *p; + UKBYTE* p; for (int i = 0; i < m_count; i++) { - p = (UKBYTE *)m_macroMem + m_table[i].keyOffset; - inLen = -1; + p = (UKBYTE*)m_macroMem + m_table[i].keyOffset; + inLen = -1; maxOutLen = sizeof(key); - ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, - (UKBYTE *)p, (UKBYTE *)key, &inLen, &maxOutLen); + ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, (UKBYTE*)p, (UKBYTE*)key, &inLen, &maxOutLen); if (ret != 0) continue; - p = (UKBYTE *)m_macroMem + m_table[i].textOffset; - inLen = -1; + p = (UKBYTE*)m_macroMem + m_table[i].textOffset; + inLen = -1; maxOutLen = sizeof(text); - ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, p, - (UKBYTE *)text, &inLen, &maxOutLen); + ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, p, (UKBYTE*)text, &inLen, &maxOutLen); if (ret != 0) continue; if (i < m_count - 1) @@ -239,11 +226,11 @@ int CMacroTable::writeToFp(FILE *f) { } //--------------------------------------------------------------- -int CMacroTable::addItem(const void *key, const void *text, int charset) { - int ret; - int inLen, maxOutLen; - int offset = m_occupied; - char *p = m_macroMem + offset; +int CMacroTable::addItem(const void* key, const void* text, int charset) { + int ret; + int inLen, maxOutLen; + int offset = m_occupied; + char* p = m_macroMem + offset; if (m_count >= MAX_MACRO_ITEMS) return -1; @@ -251,12 +238,11 @@ int CMacroTable::addItem(const void *key, const void *text, int charset) { m_table[m_count].keyOffset = offset; // Convert macro key to VN standard - inLen = -1; // input is null-terminated + inLen = -1; // input is null-terminated maxOutLen = MAX_MACRO_KEY_LEN * sizeof(StdVnChar); if (maxOutLen + offset > m_memSize) maxOutLen = m_memSize - offset; - ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE *)key, - (UKBYTE *)p, &inLen, &maxOutLen); + ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE*)key, (UKBYTE*)p, &inLen, &maxOutLen); if (ret != 0) return -1; @@ -265,12 +251,11 @@ int CMacroTable::addItem(const void *key, const void *text, int charset) { // convert macro text to VN standard m_table[m_count].textOffset = offset; - inLen = -1; // input is null-terminated - maxOutLen = MAX_MACRO_TEXT_LEN * sizeof(StdVnChar); + inLen = -1; // input is null-terminated + maxOutLen = MAX_MACRO_TEXT_LEN * sizeof(StdVnChar); if (maxOutLen + offset > m_memSize) maxOutLen = m_memSize - offset; - ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE *)text, - (UKBYTE *)p, &inLen, &maxOutLen); + ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE*)text, (UKBYTE*)p, &inLen, &maxOutLen); if (ret != 0) return -1; @@ -283,11 +268,11 @@ int CMacroTable::addItem(const void *key, const void *text, int charset) { // add a new macro into the sorted macro table // item format: key:text (key and text are separated by a colon) //--------------------------------------------------------------- -int CMacroTable::addItem(const char *item, int charset) { +int CMacroTable::addItem(const char* item, int charset) { char key[MAX_MACRO_KEY_LEN]; // Parse the input item - char *pos = (char *)strchr(item, ':'); + char* pos = (char*)strchr(item, ':'); if (pos == NULL) return -1; int keyLen = (int)(pos - item); @@ -301,19 +286,19 @@ int CMacroTable::addItem(const char *item, int charset) { //--------------------------------------------------------------- void CMacroTable::resetContent() { m_occupied = 0; - m_count = 0; + m_count = 0; } //--------------------------------------------------------------- -const StdVnChar *CMacroTable::getKey(int idx) const { +const StdVnChar* CMacroTable::getKey(int idx) const { if (idx < 0 || idx >= m_count) return 0; - return (StdVnChar *)(m_macroMem + m_table[idx].keyOffset); + return (StdVnChar*)(m_macroMem + m_table[idx].keyOffset); } //--------------------------------------------------------------- -const StdVnChar *CMacroTable::getText(int idx) const { +const StdVnChar* CMacroTable::getText(int idx) const { if (idx < 0 || idx >= m_count) return 0; - return (StdVnChar *)(m_macroMem + m_table[idx].textOffset); + return (StdVnChar*)(m_macroMem + m_table[idx].textOffset); } diff --git a/unikey/core/mactab.h b/unikey/core/mactab.h index 3fc53e1e..f9ac6ea1 100644 --- a/unikey/core/mactab.h +++ b/unikey/core/mactab.h @@ -32,29 +32,31 @@ typedef char TCHAR; #endif class DllInterface CMacroTable { -public: - void init(); - int loadFromFile(const char *fname); - int writeToFile(const char *fname); - int writeToFp(FILE *f); - - const StdVnChar *lookup(StdVnChar *key); - const StdVnChar *getKey(int idx) const; - const StdVnChar *getText(int idx) const; - int getCount() const { return m_count; } + public: + void init(); + int loadFromFile(const char* fname); + int writeToFile(const char* fname); + int writeToFp(FILE* f); + + const StdVnChar* lookup(StdVnChar* key); + const StdVnChar* getKey(int idx) const; + const StdVnChar* getText(int idx) const; + int getCount() const { + return m_count; + } void resetContent(); - int addItem(const char *item, int charset); - int addItem(const void *key, const void *text, int charset); + int addItem(const char* item, int charset); + int addItem(const void* key, const void* text, int charset); -protected: - bool readHeader(FILE *f, int &version); - void writeHeader(FILE *f); + protected: + bool readHeader(FILE* f, int& version); + void writeHeader(FILE* f); MacroDef m_table[MAX_MACRO_ITEMS]; - char m_macroMem[MACRO_MEM_SIZE]; + char m_macroMem[MACRO_MEM_SIZE]; - int m_count; - int m_memSize, m_occupied; + int m_count; + int m_memSize, m_occupied; }; #endif diff --git a/unikey/core/pattern.cpp b/unikey/core/pattern.cpp index 30adc091..e061f761 100644 --- a/unikey/core/pattern.cpp +++ b/unikey/core/pattern.cpp @@ -12,14 +12,14 @@ //---------------------------- void PatternState::reset() { - m_pos = 0; + m_pos = 0; m_found = 0; } //---------------------------- -void PatternState::init(char *pattern) { - m_pos = 0; - m_found = 0; +void PatternState::init(char* pattern) { + m_pos = 0; + m_found = 0; m_pattern = pattern; int i = 0, j = -1; @@ -45,13 +45,13 @@ int PatternState::foundAtNextChar(char ch) { if (m_pattern[m_pos] == 0) { m_found++; m_pos = m_border[m_pos]; - ret = 1; + ret = 1; } return ret; } //----------------------------------------------------- -void PatternList::init(char **patterns, int count) { +void PatternList::init(char** patterns, int count) { m_count = count; delete[] m_patterns; m_patterns = new PatternState[count]; diff --git a/unikey/core/pattern.h b/unikey/core/pattern.h index 611e43c9..43bfc4b6 100644 --- a/unikey/core/pattern.h +++ b/unikey/core/pattern.h @@ -19,27 +19,26 @@ #define MAX_PATTERN_LEN 40 class DllInterface PatternState { -public: - char *m_pattern; - int m_border[MAX_PATTERN_LEN + 1]; - int m_pos; - int m_found; - void init(char *pattern); - void reset(); - int foundAtNextChar( - char ch); // get next input char, returns 1 if pattern is found. + public: + char* m_pattern; + int m_border[MAX_PATTERN_LEN + 1]; + int m_pos; + int m_found; + void init(char* pattern); + void reset(); + int foundAtNextChar(char ch); // get next input char, returns 1 if pattern is found. }; class DllInterface PatternList { -public: - PatternState *m_patterns; - int m_count; - void init(char **patterns, int count); - int foundAtNextChar(char ch); - void reset(); + public: + PatternState* m_patterns; + int m_count; + void init(char** patterns, int count); + int foundAtNextChar(char ch); + void reset(); PatternList() { - m_count = 0; + m_count = 0; m_patterns = 0; } diff --git a/unikey/core/ukengine.cpp b/unikey/core/ukengine.cpp index 0e619be7..2b4485f7 100644 --- a/unikey/core/ukengine.cpp +++ b/unikey/core/ukengine.cpp @@ -25,803 +25,189 @@ using namespace std; #define ENTER_CHAR 13 -#define IS_ODD(x) (x & 1) +#define IS_ODD(x) (x & 1) #define IS_EVEN(x) (!(x & 1)) -#define IS_STD_VN_LOWER(x) \ - ((x) >= VnStdCharOffset && \ - (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(x)) -#define IS_STD_VN_UPPER(x) \ - ((x) >= VnStdCharOffset && \ - (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(x)) +#define IS_STD_VN_LOWER(x) ((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(x)) +#define IS_STD_VN_UPPER(x) ((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(x)) -bool IsVnVowel[vnl_lastChar]; +bool IsVnVowel[vnl_lastChar]; extern VnLexiName AZLexiUpper[]; // defined in inputproc.cpp extern VnLexiName AZLexiLower[]; // see vnconv/data.cpp for explanation of these characters -unsigned char SpecialWesternChars[] = { - 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, - 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F, 0x00}; +unsigned char SpecialWesternChars[] = {0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F, 0x00}; -StdVnChar IsoStdVnCharMap[256]; +StdVnChar IsoStdVnCharMap[256]; inline StdVnChar IsoToStdVnChar(int keyCode) { return (keyCode < 256) ? IsoStdVnCharMap[keyCode] : keyCode; } struct VowelSeqInfo { - int len; - int complete; - int conSuffix; // allow consonnant suffix + int len; + int complete; + int conSuffix; // allow consonnant suffix VnLexiName v[3]; - VowelSeq sub[3]; + VowelSeq sub[3]; - int roofPos; - VowelSeq withRoof; + int roofPos; + VowelSeq withRoof; - int hookPos; - VowelSeq withHook; // hook & bowl + int hookPos; + VowelSeq withHook; // hook & bowl }; -VowelSeqInfo VSeqList[] = {{1, - 1, - 1, - {vnl_a, vnl_nonVnChar, vnl_nonVnChar}, - {vs_a, vs_nil, vs_nil}, - -1, - vs_ar, - -1, - vs_ab}, - {1, - 1, - 1, - {vnl_ar, vnl_nonVnChar, vnl_nonVnChar}, - {vs_ar, vs_nil, vs_nil}, - 0, - vs_nil, - -1, - vs_ab}, - {1, - 1, - 1, - {vnl_ab, vnl_nonVnChar, vnl_nonVnChar}, - {vs_ab, vs_nil, vs_nil}, - -1, - vs_ar, - 0, - vs_nil}, - {1, - 1, - 1, - {vnl_e, vnl_nonVnChar, vnl_nonVnChar}, - {vs_e, vs_nil, vs_nil}, - -1, - vs_er, - -1, - vs_nil}, - {1, - 1, - 1, - {vnl_er, vnl_nonVnChar, vnl_nonVnChar}, - {vs_er, vs_nil, vs_nil}, - 0, - vs_nil, - -1, - vs_nil}, - {1, - 1, - 1, - {vnl_i, vnl_nonVnChar, vnl_nonVnChar}, - {vs_i, vs_nil, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {1, - 1, - 1, - {vnl_o, vnl_nonVnChar, vnl_nonVnChar}, - {vs_o, vs_nil, vs_nil}, - -1, - vs_or, - -1, - vs_oh}, - {1, - 1, - 1, - {vnl_or, vnl_nonVnChar, vnl_nonVnChar}, - {vs_or, vs_nil, vs_nil}, - 0, - vs_nil, - -1, - vs_oh}, - {1, - 1, - 1, - {vnl_oh, vnl_nonVnChar, vnl_nonVnChar}, - {vs_oh, vs_nil, vs_nil}, - -1, - vs_or, - 0, - vs_nil}, - {1, - 1, - 1, - {vnl_u, vnl_nonVnChar, vnl_nonVnChar}, - {vs_u, vs_nil, vs_nil}, - -1, - vs_nil, - -1, - vs_uh}, - {1, - 1, - 1, - {vnl_uh, vnl_nonVnChar, vnl_nonVnChar}, - {vs_uh, vs_nil, vs_nil}, - -1, - vs_nil, - 0, - vs_nil}, - {1, - 1, - 1, - {vnl_y, vnl_nonVnChar, vnl_nonVnChar}, - {vs_y, vs_nil, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_a, vnl_i, vnl_nonVnChar}, - {vs_a, vs_ai, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_a, vnl_o, vnl_nonVnChar}, - {vs_a, vs_ao, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_a, vnl_u, vnl_nonVnChar}, - {vs_a, vs_au, vs_nil}, - -1, - vs_aru, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_a, vnl_y, vnl_nonVnChar}, - {vs_a, vs_ay, vs_nil}, - -1, - vs_ary, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_ar, vnl_u, vnl_nonVnChar}, - {vs_ar, vs_aru, vs_nil}, - 0, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_ar, vnl_y, vnl_nonVnChar}, - {vs_ar, vs_ary, vs_nil}, - 0, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_e, vnl_o, vnl_nonVnChar}, - {vs_e, vs_eo, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 0, - 0, - {vnl_e, vnl_u, vnl_nonVnChar}, - {vs_e, vs_eu, vs_nil}, - -1, - vs_eru, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_er, vnl_u, vnl_nonVnChar}, - {vs_er, vs_eru, vs_nil}, - 0, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_i, vnl_a, vnl_nonVnChar}, - {vs_i, vs_ia, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 0, - 1, - {vnl_i, vnl_e, vnl_nonVnChar}, - {vs_i, vs_ie, vs_nil}, - -1, - vs_ier, - -1, - vs_nil}, - {2, - 1, - 1, - {vnl_i, vnl_er, vnl_nonVnChar}, - {vs_i, vs_ier, vs_nil}, - 1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_i, vnl_u, vnl_nonVnChar}, - {vs_i, vs_iu, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 1, - {vnl_o, vnl_a, vnl_nonVnChar}, - {vs_o, vs_oa, vs_nil}, - -1, - vs_nil, - -1, - vs_oab}, - {2, - 1, - 1, - {vnl_o, vnl_ab, vnl_nonVnChar}, - {vs_o, vs_oab, vs_nil}, - -1, - vs_nil, - 1, - vs_nil}, - {2, - 1, - 1, - {vnl_o, vnl_e, vnl_nonVnChar}, - {vs_o, vs_oe, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_o, vnl_i, vnl_nonVnChar}, - {vs_o, vs_oi, vs_nil}, - -1, - vs_ori, - -1, - vs_ohi}, - {2, - 1, - 0, - {vnl_or, vnl_i, vnl_nonVnChar}, - {vs_or, vs_ori, vs_nil}, - 0, - vs_nil, - -1, - vs_ohi}, - {2, - 1, - 0, - {vnl_oh, vnl_i, vnl_nonVnChar}, - {vs_oh, vs_ohi, vs_nil}, - -1, - vs_ori, - 0, - vs_nil}, - {2, - 1, - 1, - {vnl_u, vnl_a, vnl_nonVnChar}, - {vs_u, vs_ua, vs_nil}, - -1, - vs_uar, - -1, - vs_uha}, - {2, - 1, - 1, - {vnl_u, vnl_ar, vnl_nonVnChar}, - {vs_u, vs_uar, vs_nil}, - 1, - vs_nil, - -1, - vs_nil}, - {2, - 0, - 1, - {vnl_u, vnl_e, vnl_nonVnChar}, - {vs_u, vs_ue, vs_nil}, - -1, - vs_uer, - -1, - vs_nil}, - {2, - 1, - 1, - {vnl_u, vnl_er, vnl_nonVnChar}, - {vs_u, vs_uer, vs_nil}, - 1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_u, vnl_i, vnl_nonVnChar}, - {vs_u, vs_ui, vs_nil}, - -1, - vs_nil, - -1, - vs_uhi}, - {2, - 0, - 1, - {vnl_u, vnl_o, vnl_nonVnChar}, - {vs_u, vs_uo, vs_nil}, - -1, - vs_uor, - -1, - vs_uho}, - {2, - 1, - 1, - {vnl_u, vnl_or, vnl_nonVnChar}, - {vs_u, vs_uor, vs_nil}, - 1, - vs_nil, - -1, - vs_uoh}, - {2, - 1, - 1, - {vnl_u, vnl_oh, vnl_nonVnChar}, - {vs_u, vs_uoh, vs_nil}, - -1, - vs_uor, - 1, - vs_uhoh}, - {2, - 0, - 0, - {vnl_u, vnl_u, vnl_nonVnChar}, - {vs_u, vs_uu, vs_nil}, - -1, - vs_nil, - -1, - vs_uhu}, - {2, - 1, - 1, - {vnl_u, vnl_y, vnl_nonVnChar}, - {vs_u, vs_uy, vs_nil}, - -1, - vs_nil, - -1, - vs_nil}, - {2, - 1, - 0, - {vnl_uh, vnl_a, vnl_nonVnChar}, - {vs_uh, vs_uha, vs_nil}, - -1, - vs_nil, - 0, - vs_nil}, - {2, - 1, - 0, - {vnl_uh, vnl_i, vnl_nonVnChar}, - {vs_uh, vs_uhi, vs_nil}, - -1, - vs_nil, - 0, - vs_nil}, - {2, - 0, - 1, - {vnl_uh, vnl_o, vnl_nonVnChar}, - {vs_uh, vs_uho, vs_nil}, - -1, - vs_nil, - 0, - vs_uhoh}, - {2, - 1, - 1, - {vnl_uh, vnl_oh, vnl_nonVnChar}, - {vs_uh, vs_uhoh, vs_nil}, - -1, - vs_nil, - 0, - vs_nil}, - {2, - 1, - 0, - {vnl_uh, vnl_u, vnl_nonVnChar}, - {vs_uh, vs_uhu, vs_nil}, - -1, - vs_nil, - 0, - vs_nil}, - {2, - 0, - 1, - {vnl_y, vnl_e, vnl_nonVnChar}, - {vs_y, vs_ye, vs_nil}, - -1, - vs_yer, - -1, - vs_nil}, - {2, - 1, - 1, - {vnl_y, vnl_er, vnl_nonVnChar}, - {vs_y, vs_yer, vs_nil}, - 1, - vs_nil, - -1, - vs_nil}, - {3, - 0, - 0, - {vnl_i, vnl_e, vnl_u}, - {vs_i, vs_ie, vs_ieu}, - -1, - vs_ieru, - -1, - vs_nil}, - {3, - 1, - 0, - {vnl_i, vnl_er, vnl_u}, - {vs_i, vs_ier, vs_ieru}, - 1, - vs_nil, - -1, - vs_nil}, - {3, - 1, - 0, - {vnl_o, vnl_a, vnl_i}, - {vs_o, vs_oa, vs_oai}, - -1, - vs_nil, - -1, - vs_nil}, - {3, - 1, - 0, - {vnl_o, vnl_a, vnl_y}, - {vs_o, vs_oa, vs_oay}, - -1, - vs_nil, - -1, - vs_nil}, - {3, - 1, - 0, - {vnl_o, vnl_e, vnl_o}, - {vs_o, vs_oe, vs_oeo}, - -1, - vs_nil, - -1, - vs_nil}, - {3, - 0, - 0, - {vnl_u, vnl_a, vnl_y}, - {vs_u, vs_ua, vs_uay}, - -1, - vs_uary, - -1, - vs_nil}, - {3, - 1, - 0, - {vnl_u, vnl_ar, vnl_y}, - {vs_u, vs_uar, vs_uary}, - 1, - vs_nil, - -1, - vs_nil}, - {3, - 0, - 0, - {vnl_u, vnl_o, vnl_i}, - {vs_u, vs_uo, vs_uoi}, - -1, - vs_uori, - -1, - vs_uhoi}, - {3, - 0, - 0, - {vnl_u, vnl_o, vnl_u}, - {vs_u, vs_uo, vs_uou}, - -1, - vs_nil, - -1, - vs_uhou}, - {3, - 1, - 0, - {vnl_u, vnl_or, vnl_i}, - {vs_u, vs_uor, vs_uori}, - 1, - vs_nil, - -1, - vs_uohi}, - {3, - 0, - 0, - {vnl_u, vnl_oh, vnl_i}, - {vs_u, vs_uoh, vs_uohi}, - -1, - vs_uori, - 1, - vs_uhohi}, - {3, - 0, - 0, - {vnl_u, vnl_oh, vnl_u}, - {vs_u, vs_uoh, vs_uohu}, - -1, - vs_nil, - 1, - vs_uhohu}, - {3, - 1, - 0, - {vnl_u, vnl_y, vnl_a}, - {vs_u, vs_uy, vs_uya}, - -1, - vs_nil, - -1, - vs_nil}, - {3, - 0, - 1, - {vnl_u, vnl_y, vnl_e}, - {vs_u, vs_uy, vs_uye}, - -1, - vs_uyer, - -1, - vs_nil}, - {3, - 1, - 1, - {vnl_u, vnl_y, vnl_er}, - {vs_u, vs_uy, vs_uyer}, - 2, - vs_nil, - -1, - vs_nil}, - {3, - 1, - 0, - {vnl_u, vnl_y, vnl_u}, - {vs_u, vs_uy, vs_uyu}, - -1, - vs_nil, - -1, - vs_nil}, - {3, - 0, - 0, - {vnl_uh, vnl_o, vnl_i}, - {vs_uh, vs_uho, vs_uhoi}, - -1, - vs_nil, - 0, - vs_uhohi}, - {3, - 0, - 0, - {vnl_uh, vnl_o, vnl_u}, - {vs_uh, vs_uho, vs_uhou}, - -1, - vs_nil, - 0, - vs_uhohu}, - {3, - 1, - 0, - {vnl_uh, vnl_oh, vnl_i}, - {vs_uh, vs_uhoh, vs_uhohi}, - -1, - vs_nil, - 0, - vs_nil}, - {3, - 1, - 0, - {vnl_uh, vnl_oh, vnl_u}, - {vs_uh, vs_uhoh, vs_uhohu}, - -1, - vs_nil, - 0, - vs_nil}, - {3, - 0, - 0, - {vnl_y, vnl_e, vnl_u}, - {vs_y, vs_ye, vs_yeu}, - -1, - vs_yeru, - -1, - vs_nil}, - {3, - 1, - 0, - {vnl_y, vnl_er, vnl_u}, - {vs_y, vs_yer, vs_yeru}, - 1, - vs_nil, - -1, - vs_nil}}; +VowelSeqInfo VSeqList[] = {{1, 1, 1, {vnl_a, vnl_nonVnChar, vnl_nonVnChar}, {vs_a, vs_nil, vs_nil}, -1, vs_ar, -1, vs_ab}, + {1, 1, 1, {vnl_ar, vnl_nonVnChar, vnl_nonVnChar}, {vs_ar, vs_nil, vs_nil}, 0, vs_nil, -1, vs_ab}, + {1, 1, 1, {vnl_ab, vnl_nonVnChar, vnl_nonVnChar}, {vs_ab, vs_nil, vs_nil}, -1, vs_ar, 0, vs_nil}, + {1, 1, 1, {vnl_e, vnl_nonVnChar, vnl_nonVnChar}, {vs_e, vs_nil, vs_nil}, -1, vs_er, -1, vs_nil}, + {1, 1, 1, {vnl_er, vnl_nonVnChar, vnl_nonVnChar}, {vs_er, vs_nil, vs_nil}, 0, vs_nil, -1, vs_nil}, + {1, 1, 1, {vnl_i, vnl_nonVnChar, vnl_nonVnChar}, {vs_i, vs_nil, vs_nil}, -1, vs_nil, -1, vs_nil}, + {1, 1, 1, {vnl_o, vnl_nonVnChar, vnl_nonVnChar}, {vs_o, vs_nil, vs_nil}, -1, vs_or, -1, vs_oh}, + {1, 1, 1, {vnl_or, vnl_nonVnChar, vnl_nonVnChar}, {vs_or, vs_nil, vs_nil}, 0, vs_nil, -1, vs_oh}, + {1, 1, 1, {vnl_oh, vnl_nonVnChar, vnl_nonVnChar}, {vs_oh, vs_nil, vs_nil}, -1, vs_or, 0, vs_nil}, + {1, 1, 1, {vnl_u, vnl_nonVnChar, vnl_nonVnChar}, {vs_u, vs_nil, vs_nil}, -1, vs_nil, -1, vs_uh}, + {1, 1, 1, {vnl_uh, vnl_nonVnChar, vnl_nonVnChar}, {vs_uh, vs_nil, vs_nil}, -1, vs_nil, 0, vs_nil}, + {1, 1, 1, {vnl_y, vnl_nonVnChar, vnl_nonVnChar}, {vs_y, vs_nil, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_a, vnl_i, vnl_nonVnChar}, {vs_a, vs_ai, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_a, vnl_o, vnl_nonVnChar}, {vs_a, vs_ao, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_a, vnl_u, vnl_nonVnChar}, {vs_a, vs_au, vs_nil}, -1, vs_aru, -1, vs_nil}, + {2, 1, 0, {vnl_a, vnl_y, vnl_nonVnChar}, {vs_a, vs_ay, vs_nil}, -1, vs_ary, -1, vs_nil}, + {2, 1, 0, {vnl_ar, vnl_u, vnl_nonVnChar}, {vs_ar, vs_aru, vs_nil}, 0, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_ar, vnl_y, vnl_nonVnChar}, {vs_ar, vs_ary, vs_nil}, 0, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_e, vnl_o, vnl_nonVnChar}, {vs_e, vs_eo, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 0, 0, {vnl_e, vnl_u, vnl_nonVnChar}, {vs_e, vs_eu, vs_nil}, -1, vs_eru, -1, vs_nil}, + {2, 1, 0, {vnl_er, vnl_u, vnl_nonVnChar}, {vs_er, vs_eru, vs_nil}, 0, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_i, vnl_a, vnl_nonVnChar}, {vs_i, vs_ia, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 0, 1, {vnl_i, vnl_e, vnl_nonVnChar}, {vs_i, vs_ie, vs_nil}, -1, vs_ier, -1, vs_nil}, + {2, 1, 1, {vnl_i, vnl_er, vnl_nonVnChar}, {vs_i, vs_ier, vs_nil}, 1, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_i, vnl_u, vnl_nonVnChar}, {vs_i, vs_iu, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 1, 1, {vnl_o, vnl_a, vnl_nonVnChar}, {vs_o, vs_oa, vs_nil}, -1, vs_nil, -1, vs_oab}, + {2, 1, 1, {vnl_o, vnl_ab, vnl_nonVnChar}, {vs_o, vs_oab, vs_nil}, -1, vs_nil, 1, vs_nil}, + {2, 1, 1, {vnl_o, vnl_e, vnl_nonVnChar}, {vs_o, vs_oe, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_o, vnl_i, vnl_nonVnChar}, {vs_o, vs_oi, vs_nil}, -1, vs_ori, -1, vs_ohi}, + {2, 1, 0, {vnl_or, vnl_i, vnl_nonVnChar}, {vs_or, vs_ori, vs_nil}, 0, vs_nil, -1, vs_ohi}, + {2, 1, 0, {vnl_oh, vnl_i, vnl_nonVnChar}, {vs_oh, vs_ohi, vs_nil}, -1, vs_ori, 0, vs_nil}, + {2, 1, 1, {vnl_u, vnl_a, vnl_nonVnChar}, {vs_u, vs_ua, vs_nil}, -1, vs_uar, -1, vs_uha}, + {2, 1, 1, {vnl_u, vnl_ar, vnl_nonVnChar}, {vs_u, vs_uar, vs_nil}, 1, vs_nil, -1, vs_nil}, + {2, 0, 1, {vnl_u, vnl_e, vnl_nonVnChar}, {vs_u, vs_ue, vs_nil}, -1, vs_uer, -1, vs_nil}, + {2, 1, 1, {vnl_u, vnl_er, vnl_nonVnChar}, {vs_u, vs_uer, vs_nil}, 1, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_u, vnl_i, vnl_nonVnChar}, {vs_u, vs_ui, vs_nil}, -1, vs_nil, -1, vs_uhi}, + {2, 0, 1, {vnl_u, vnl_o, vnl_nonVnChar}, {vs_u, vs_uo, vs_nil}, -1, vs_uor, -1, vs_uho}, + {2, 1, 1, {vnl_u, vnl_or, vnl_nonVnChar}, {vs_u, vs_uor, vs_nil}, 1, vs_nil, -1, vs_uoh}, + {2, 1, 1, {vnl_u, vnl_oh, vnl_nonVnChar}, {vs_u, vs_uoh, vs_nil}, -1, vs_uor, 1, vs_uhoh}, + {2, 0, 0, {vnl_u, vnl_u, vnl_nonVnChar}, {vs_u, vs_uu, vs_nil}, -1, vs_nil, -1, vs_uhu}, + {2, 1, 1, {vnl_u, vnl_y, vnl_nonVnChar}, {vs_u, vs_uy, vs_nil}, -1, vs_nil, -1, vs_nil}, + {2, 1, 0, {vnl_uh, vnl_a, vnl_nonVnChar}, {vs_uh, vs_uha, vs_nil}, -1, vs_nil, 0, vs_nil}, + {2, 1, 0, {vnl_uh, vnl_i, vnl_nonVnChar}, {vs_uh, vs_uhi, vs_nil}, -1, vs_nil, 0, vs_nil}, + {2, 0, 1, {vnl_uh, vnl_o, vnl_nonVnChar}, {vs_uh, vs_uho, vs_nil}, -1, vs_nil, 0, vs_uhoh}, + {2, 1, 1, {vnl_uh, vnl_oh, vnl_nonVnChar}, {vs_uh, vs_uhoh, vs_nil}, -1, vs_nil, 0, vs_nil}, + {2, 1, 0, {vnl_uh, vnl_u, vnl_nonVnChar}, {vs_uh, vs_uhu, vs_nil}, -1, vs_nil, 0, vs_nil}, + {2, 0, 1, {vnl_y, vnl_e, vnl_nonVnChar}, {vs_y, vs_ye, vs_nil}, -1, vs_yer, -1, vs_nil}, + {2, 1, 1, {vnl_y, vnl_er, vnl_nonVnChar}, {vs_y, vs_yer, vs_nil}, 1, vs_nil, -1, vs_nil}, + {3, 0, 0, {vnl_i, vnl_e, vnl_u}, {vs_i, vs_ie, vs_ieu}, -1, vs_ieru, -1, vs_nil}, + {3, 1, 0, {vnl_i, vnl_er, vnl_u}, {vs_i, vs_ier, vs_ieru}, 1, vs_nil, -1, vs_nil}, + {3, 1, 0, {vnl_o, vnl_a, vnl_i}, {vs_o, vs_oa, vs_oai}, -1, vs_nil, -1, vs_nil}, + {3, 1, 0, {vnl_o, vnl_a, vnl_y}, {vs_o, vs_oa, vs_oay}, -1, vs_nil, -1, vs_nil}, + {3, 1, 0, {vnl_o, vnl_e, vnl_o}, {vs_o, vs_oe, vs_oeo}, -1, vs_nil, -1, vs_nil}, + {3, 0, 0, {vnl_u, vnl_a, vnl_y}, {vs_u, vs_ua, vs_uay}, -1, vs_uary, -1, vs_nil}, + {3, 1, 0, {vnl_u, vnl_ar, vnl_y}, {vs_u, vs_uar, vs_uary}, 1, vs_nil, -1, vs_nil}, + {3, 0, 0, {vnl_u, vnl_o, vnl_i}, {vs_u, vs_uo, vs_uoi}, -1, vs_uori, -1, vs_uhoi}, + {3, 0, 0, {vnl_u, vnl_o, vnl_u}, {vs_u, vs_uo, vs_uou}, -1, vs_nil, -1, vs_uhou}, + {3, 1, 0, {vnl_u, vnl_or, vnl_i}, {vs_u, vs_uor, vs_uori}, 1, vs_nil, -1, vs_uohi}, + {3, 0, 0, {vnl_u, vnl_oh, vnl_i}, {vs_u, vs_uoh, vs_uohi}, -1, vs_uori, 1, vs_uhohi}, + {3, 0, 0, {vnl_u, vnl_oh, vnl_u}, {vs_u, vs_uoh, vs_uohu}, -1, vs_nil, 1, vs_uhohu}, + {3, 1, 0, {vnl_u, vnl_y, vnl_a}, {vs_u, vs_uy, vs_uya}, -1, vs_nil, -1, vs_nil}, + {3, 0, 1, {vnl_u, vnl_y, vnl_e}, {vs_u, vs_uy, vs_uye}, -1, vs_uyer, -1, vs_nil}, + {3, 1, 1, {vnl_u, vnl_y, vnl_er}, {vs_u, vs_uy, vs_uyer}, 2, vs_nil, -1, vs_nil}, + {3, 1, 0, {vnl_u, vnl_y, vnl_u}, {vs_u, vs_uy, vs_uyu}, -1, vs_nil, -1, vs_nil}, + {3, 0, 0, {vnl_uh, vnl_o, vnl_i}, {vs_uh, vs_uho, vs_uhoi}, -1, vs_nil, 0, vs_uhohi}, + {3, 0, 0, {vnl_uh, vnl_o, vnl_u}, {vs_uh, vs_uho, vs_uhou}, -1, vs_nil, 0, vs_uhohu}, + {3, 1, 0, {vnl_uh, vnl_oh, vnl_i}, {vs_uh, vs_uhoh, vs_uhohi}, -1, vs_nil, 0, vs_nil}, + {3, 1, 0, {vnl_uh, vnl_oh, vnl_u}, {vs_uh, vs_uhoh, vs_uhohu}, -1, vs_nil, 0, vs_nil}, + {3, 0, 0, {vnl_y, vnl_e, vnl_u}, {vs_y, vs_ye, vs_yeu}, -1, vs_yeru, -1, vs_nil}, + {3, 1, 0, {vnl_y, vnl_er, vnl_u}, {vs_y, vs_yer, vs_yeru}, 1, vs_nil, -1, vs_nil}}; struct ConSeqInfo { - int len; + int len; VnLexiName c[3]; - bool suffix; + bool suffix; }; -ConSeqInfo CSeqList[] = {{1, {vnl_b, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_c, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_c, vnl_h, vnl_nonVnChar}, true}, - {1, {vnl_d, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_dd, vnl_nonVnChar, vnl_nonVnChar}, false}, - {2, {vnl_d, vnl_z, vnl_nonVnChar}, false}, - {1, {vnl_g, vnl_nonVnChar, vnl_nonVnChar}, false}, - {2, {vnl_g, vnl_h, vnl_nonVnChar}, false}, - {2, {vnl_g, vnl_i, vnl_nonVnChar}, false}, - {3, {vnl_g, vnl_i, vnl_n}, false}, - {1, {vnl_h, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_k, vnl_nonVnChar, vnl_nonVnChar}, false}, - {2, {vnl_k, vnl_h, vnl_nonVnChar}, false}, - {1, {vnl_l, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_m, vnl_nonVnChar, vnl_nonVnChar}, true}, - {1, {vnl_n, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_n, vnl_g, vnl_nonVnChar}, true}, - {3, {vnl_n, vnl_g, vnl_h}, false}, - {2, {vnl_n, vnl_h, vnl_nonVnChar}, true}, - {1, {vnl_p, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_p, vnl_h, vnl_nonVnChar}, false}, - {1, {vnl_q, vnl_nonVnChar, vnl_nonVnChar}, false}, - {2, {vnl_q, vnl_u, vnl_nonVnChar}, false}, - {1, {vnl_r, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_s, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_t, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_t, vnl_h, vnl_nonVnChar}, false}, - {2, {vnl_t, vnl_r, vnl_nonVnChar}, false}, - {1, {vnl_v, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_x, vnl_nonVnChar, vnl_nonVnChar}, false}}; - -const int VSeqCount = sizeof(VSeqList) / sizeof(VowelSeqInfo); +ConSeqInfo CSeqList[] = {{1, {vnl_b, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_c, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_c, vnl_h, vnl_nonVnChar}, true}, {1, {vnl_d, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_dd, vnl_nonVnChar, vnl_nonVnChar}, false}, {2, {vnl_d, vnl_z, vnl_nonVnChar}, false}, + {1, {vnl_g, vnl_nonVnChar, vnl_nonVnChar}, false}, {2, {vnl_g, vnl_h, vnl_nonVnChar}, false}, + {2, {vnl_g, vnl_i, vnl_nonVnChar}, false}, {3, {vnl_g, vnl_i, vnl_n}, false}, + {1, {vnl_h, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_k, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_k, vnl_h, vnl_nonVnChar}, false}, {1, {vnl_l, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_m, vnl_nonVnChar, vnl_nonVnChar}, true}, {1, {vnl_n, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_n, vnl_g, vnl_nonVnChar}, true}, {3, {vnl_n, vnl_g, vnl_h}, false}, + {2, {vnl_n, vnl_h, vnl_nonVnChar}, true}, {1, {vnl_p, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_p, vnl_h, vnl_nonVnChar}, false}, {1, {vnl_q, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_q, vnl_u, vnl_nonVnChar}, false}, {1, {vnl_r, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_s, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_t, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_t, vnl_h, vnl_nonVnChar}, false}, {2, {vnl_t, vnl_r, vnl_nonVnChar}, false}, + {1, {vnl_v, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_x, vnl_nonVnChar, vnl_nonVnChar}, false}}; + +const int VSeqCount = sizeof(VSeqList) / sizeof(VowelSeqInfo); struct VSeqPair { VnLexiName v[3]; - VowelSeq vs; + VowelSeq vs; }; -VSeqPair SortedVSeqList[VSeqCount]; +VSeqPair SortedVSeqList[VSeqCount]; const int CSeqCount = sizeof(CSeqList) / sizeof(ConSeqInfo); struct CSeqPair { VnLexiName c[3]; - ConSeq cs; + ConSeq cs; }; CSeqPair SortedCSeqList[CSeqCount]; struct VCPair { VowelSeq v; - ConSeq c; + ConSeq c; }; -VCPair VCPairList[] = {{vs_a, cs_c}, {vs_a, cs_ch}, {vs_a, cs_m}, - {vs_a, cs_n}, {vs_a, cs_ng}, {vs_a, cs_nh}, - {vs_a, cs_p}, {vs_a, cs_t}, {vs_ar, cs_c}, - {vs_ar, cs_m}, {vs_ar, cs_n}, {vs_ar, cs_ng}, - {vs_ar, cs_p}, {vs_ar, cs_t}, {vs_ab, cs_c}, - {vs_ab, cs_m}, {vs_ab, cs_n}, {vs_ab, cs_ng}, - {vs_ab, cs_p}, {vs_ab, cs_t}, - - {vs_e, cs_c}, {vs_e, cs_ch}, {vs_e, cs_m}, - {vs_e, cs_n}, {vs_e, cs_ng}, {vs_e, cs_nh}, - {vs_e, cs_p}, {vs_e, cs_t}, {vs_er, cs_c}, - {vs_er, cs_ch}, {vs_er, cs_m}, {vs_er, cs_n}, - {vs_er, cs_nh}, {vs_er, cs_p}, {vs_er, cs_t}, - - {vs_i, cs_c}, {vs_i, cs_ch}, {vs_i, cs_m}, - {vs_i, cs_n}, {vs_i, cs_nh}, {vs_i, cs_p}, - {vs_i, cs_t}, - - {vs_o, cs_c}, {vs_o, cs_m}, {vs_o, cs_n}, - {vs_o, cs_ng}, {vs_o, cs_p}, {vs_o, cs_t}, - {vs_or, cs_c}, {vs_or, cs_m}, {vs_or, cs_n}, - {vs_or, cs_ng}, {vs_or, cs_p}, {vs_or, cs_t}, - {vs_oh, cs_m}, {vs_oh, cs_n}, {vs_oh, cs_p}, - {vs_oh, cs_t}, - - {vs_u, cs_c}, {vs_u, cs_m}, {vs_u, cs_n}, - {vs_u, cs_ng}, {vs_u, cs_p}, {vs_u, cs_t}, - {vs_uh, cs_c}, {vs_uh, cs_m}, {vs_uh, cs_n}, - {vs_uh, cs_ng}, {vs_uh, cs_t}, - - {vs_y, cs_t}, {vs_ie, cs_c}, {vs_ie, cs_m}, - {vs_ie, cs_n}, {vs_ie, cs_ng}, {vs_ie, cs_p}, - {vs_ie, cs_t}, {vs_ier, cs_c}, {vs_ier, cs_m}, - {vs_ier, cs_n}, {vs_ier, cs_ng}, {vs_ier, cs_p}, - {vs_ier, cs_t}, - - {vs_oa, cs_c}, {vs_oa, cs_ch}, {vs_oa, cs_m}, - {vs_oa, cs_n}, {vs_oa, cs_ng}, {vs_oa, cs_nh}, - {vs_oa, cs_p}, {vs_oa, cs_t}, {vs_oab, cs_c}, - {vs_oab, cs_m}, {vs_oab, cs_n}, {vs_oab, cs_ng}, - {vs_oab, cs_t}, - - {vs_oe, cs_n}, {vs_oe, cs_t}, - - {vs_ua, cs_n}, {vs_ua, cs_ng}, {vs_ua, cs_t}, - {vs_uar, cs_n}, {vs_uar, cs_ng}, {vs_uar, cs_t}, - - {vs_ue, cs_c}, {vs_ue, cs_ch}, {vs_ue, cs_n}, - {vs_ue, cs_nh}, {vs_uer, cs_c}, {vs_uer, cs_ch}, - {vs_uer, cs_n}, {vs_uer, cs_nh}, - - {vs_uo, cs_c}, {vs_uo, cs_m}, {vs_uo, cs_n}, - {vs_uo, cs_ng}, {vs_uo, cs_p}, {vs_uo, cs_t}, - {vs_uor, cs_c}, {vs_uor, cs_m}, {vs_uor, cs_n}, - {vs_uor, cs_ng}, {vs_uor, cs_t}, {vs_uho, cs_c}, - {vs_uho, cs_m}, {vs_uho, cs_n}, {vs_uho, cs_ng}, - {vs_uho, cs_p}, {vs_uho, cs_t}, {vs_uhoh, cs_c}, - {vs_uhoh, cs_m}, {vs_uhoh, cs_n}, {vs_uhoh, cs_ng}, - {vs_uhoh, cs_p}, {vs_uhoh, cs_t}, - - {vs_uy, cs_c}, {vs_uy, cs_ch}, {vs_uy, cs_n}, - {vs_uy, cs_nh}, {vs_uy, cs_p}, {vs_uy, cs_t}, - - {vs_ye, cs_m}, {vs_ye, cs_n}, {vs_ye, cs_ng}, - {vs_ye, cs_p}, {vs_ye, cs_t}, {vs_yer, cs_m}, - {vs_yer, cs_n}, {vs_yer, cs_ng}, {vs_yer, cs_t}, - - {vs_uye, cs_n}, {vs_uye, cs_t}, {vs_uyer, cs_n}, - {vs_uyer, cs_t} +VCPair VCPairList[] = {{vs_a, cs_c}, {vs_a, cs_ch}, {vs_a, cs_m}, {vs_a, cs_n}, {vs_a, cs_ng}, {vs_a, cs_nh}, {vs_a, cs_p}, {vs_a, cs_t}, {vs_ar, cs_c}, + {vs_ar, cs_m}, {vs_ar, cs_n}, {vs_ar, cs_ng}, {vs_ar, cs_p}, {vs_ar, cs_t}, {vs_ab, cs_c}, {vs_ab, cs_m}, {vs_ab, cs_n}, {vs_ab, cs_ng}, + {vs_ab, cs_p}, {vs_ab, cs_t}, + + {vs_e, cs_c}, {vs_e, cs_ch}, {vs_e, cs_m}, {vs_e, cs_n}, {vs_e, cs_ng}, {vs_e, cs_nh}, {vs_e, cs_p}, {vs_e, cs_t}, {vs_er, cs_c}, + {vs_er, cs_ch}, {vs_er, cs_m}, {vs_er, cs_n}, {vs_er, cs_nh}, {vs_er, cs_p}, {vs_er, cs_t}, + + {vs_i, cs_c}, {vs_i, cs_ch}, {vs_i, cs_m}, {vs_i, cs_n}, {vs_i, cs_nh}, {vs_i, cs_p}, {vs_i, cs_t}, + + {vs_o, cs_c}, {vs_o, cs_m}, {vs_o, cs_n}, {vs_o, cs_ng}, {vs_o, cs_p}, {vs_o, cs_t}, {vs_or, cs_c}, {vs_or, cs_m}, {vs_or, cs_n}, + {vs_or, cs_ng}, {vs_or, cs_p}, {vs_or, cs_t}, {vs_oh, cs_m}, {vs_oh, cs_n}, {vs_oh, cs_p}, {vs_oh, cs_t}, + + {vs_u, cs_c}, {vs_u, cs_m}, {vs_u, cs_n}, {vs_u, cs_ng}, {vs_u, cs_p}, {vs_u, cs_t}, {vs_uh, cs_c}, {vs_uh, cs_m}, {vs_uh, cs_n}, + {vs_uh, cs_ng}, {vs_uh, cs_t}, + + {vs_y, cs_t}, {vs_ie, cs_c}, {vs_ie, cs_m}, {vs_ie, cs_n}, {vs_ie, cs_ng}, {vs_ie, cs_p}, {vs_ie, cs_t}, {vs_ier, cs_c}, {vs_ier, cs_m}, + {vs_ier, cs_n}, {vs_ier, cs_ng}, {vs_ier, cs_p}, {vs_ier, cs_t}, + + {vs_oa, cs_c}, {vs_oa, cs_ch}, {vs_oa, cs_m}, {vs_oa, cs_n}, {vs_oa, cs_ng}, {vs_oa, cs_nh}, {vs_oa, cs_p}, {vs_oa, cs_t}, {vs_oab, cs_c}, + {vs_oab, cs_m}, {vs_oab, cs_n}, {vs_oab, cs_ng}, {vs_oab, cs_t}, + + {vs_oe, cs_n}, {vs_oe, cs_t}, + + {vs_ua, cs_n}, {vs_ua, cs_ng}, {vs_ua, cs_t}, {vs_uar, cs_n}, {vs_uar, cs_ng}, {vs_uar, cs_t}, + + {vs_ue, cs_c}, {vs_ue, cs_ch}, {vs_ue, cs_n}, {vs_ue, cs_nh}, {vs_uer, cs_c}, {vs_uer, cs_ch}, {vs_uer, cs_n}, {vs_uer, cs_nh}, + + {vs_uo, cs_c}, {vs_uo, cs_m}, {vs_uo, cs_n}, {vs_uo, cs_ng}, {vs_uo, cs_p}, {vs_uo, cs_t}, {vs_uor, cs_c}, {vs_uor, cs_m}, {vs_uor, cs_n}, + {vs_uor, cs_ng}, {vs_uor, cs_t}, {vs_uho, cs_c}, {vs_uho, cs_m}, {vs_uho, cs_n}, {vs_uho, cs_ng}, {vs_uho, cs_p}, {vs_uho, cs_t}, {vs_uhoh, cs_c}, + {vs_uhoh, cs_m}, {vs_uhoh, cs_n}, {vs_uhoh, cs_ng}, {vs_uhoh, cs_p}, {vs_uhoh, cs_t}, + + {vs_uy, cs_c}, {vs_uy, cs_ch}, {vs_uy, cs_n}, {vs_uy, cs_nh}, {vs_uy, cs_p}, {vs_uy, cs_t}, + + {vs_ye, cs_m}, {vs_ye, cs_n}, {vs_ye, cs_ng}, {vs_ye, cs_p}, {vs_ye, cs_t}, {vs_yer, cs_m}, {vs_yer, cs_n}, {vs_yer, cs_ng}, {vs_yer, cs_t}, + + {vs_uye, cs_n}, {vs_uye, cs_t}, {vs_uyer, cs_n}, {vs_uyer, cs_t} }; @@ -829,7 +215,7 @@ const int VCPairCount = sizeof(VCPairList) / sizeof(VCPair); // TODO: auto-complete: e.g. luan -> lua^n -typedef int (UkEngine::*UkKeyProc)(UkKeyEvent &ev); +typedef int (UkEngine::*UkKeyProc)(UkKeyEvent& ev); UkKeyProc UkKeyProcList[vneCount] = { &UkEngine::processRoof, // vneRoofAll @@ -854,17 +240,15 @@ UkKeyProc UkKeyProcList[vneCount] = { &UkEngine::processAppend // vneNormal }; -VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2 = vnl_nonVnChar, - VnLexiName v3 = vnl_nonVnChar); -ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2 = vnl_nonVnChar, - VnLexiName c3 = vnl_nonVnChar); +VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2 = vnl_nonVnChar, VnLexiName v3 = vnl_nonVnChar); +ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2 = vnl_nonVnChar, VnLexiName c3 = vnl_nonVnChar); -bool UkEngine::m_classInit = false; +bool UkEngine::m_classInit = false; //------------------------------------------------ -int tripleVowelCompare(const void *p1, const void *p2) { - VSeqPair *t1 = (VSeqPair *)p1; - VSeqPair *t2 = (VSeqPair *)p2; +int tripleVowelCompare(const void* p1, const void* p2) { + VSeqPair* t1 = (VSeqPair*)p1; + VSeqPair* t2 = (VSeqPair*)p2; for (int i = 0; i < 3; i++) { if (t1->v[i] < t2->v[i]) @@ -876,9 +260,9 @@ int tripleVowelCompare(const void *p1, const void *p2) { } //------------------------------------------------ -int tripleConCompare(const void *p1, const void *p2) { - CSeqPair *t1 = (CSeqPair *)p1; - CSeqPair *t2 = (CSeqPair *)p2; +int tripleConCompare(const void* p1, const void* p2) { + CSeqPair* t1 = (CSeqPair*)p1; + CSeqPair* t2 = (CSeqPair*)p2; for (int i = 0; i < 3; i++) { if (t1->c[i] < t2->c[i]) @@ -890,9 +274,9 @@ int tripleConCompare(const void *p1, const void *p2) { } //------------------------------------------------ -int VCPairCompare(const void *p1, const void *p2) { - VCPair *t1 = (VCPair *)p1; - VCPair *t2 = (VCPair *)p2; +int VCPairCompare(const void* p1, const void* p2) { + VCPair* t1 = (VCPair*)p1; + VCPair* t2 = (VCPair*)p2; if (t1->v < t2->v) return -1; @@ -911,22 +295,18 @@ bool isValidCV(ConSeq c, VowelSeq v) { if (c == cs_nil || v == vs_nil) return true; - VowelSeqInfo &vInfo = VSeqList[v]; + VowelSeqInfo& vInfo = VSeqList[v]; // gi doesn't go with i // qu doesn't go with u, uh // q doesn't go with any vowel - if ((c == cs_gi && vInfo.v[0] == vnl_i) || - (c == cs_qu && (vInfo.v[0] == vnl_u || vInfo.v[0] == vnl_uh)) || - (c == cs_q)) + if ((c == cs_gi && vInfo.v[0] == vnl_i) || (c == cs_qu && (vInfo.v[0] == vnl_u || vInfo.v[0] == vnl_uh)) || (c == cs_q)) return false; // k can only go with the following vowel sequences if (c == cs_k) { - static VowelSeq kVseq[] = {vs_e, vs_i, vs_y, vs_er, vs_eo, - vs_eu, vs_eru, vs_ia, vs_ie, vs_ier, - vs_ieu, vs_ieru, vs_nil}; - int i; + static VowelSeq kVseq[] = {vs_e, vs_i, vs_y, vs_er, vs_eo, vs_eu, vs_eru, vs_ia, vs_ie, vs_ier, vs_ieu, vs_ieru, vs_nil}; + int i; for (i = 0; kVseq[i] != vs_nil && kVseq[i] != v; i++) ; return (kVseq[i] != vs_nil); @@ -941,11 +321,11 @@ bool isValidVC(VowelSeq v, ConSeq c) { if (v == vs_nil || c == cs_nil) return true; - VowelSeqInfo &vInfo = VSeqList[v]; + VowelSeqInfo& vInfo = VSeqList[v]; if (!vInfo.conSuffix) return false; - ConSeqInfo &cInfo = CSeqList[c]; + ConSeqInfo& cInfo = CSeqList[c]; if (!cInfo.suffix) return false; @@ -983,8 +363,7 @@ bool isValidCVC(ConSeq c1, VowelSeq v, ConSeq c2) { return true; // gieng, gie^ng - if (c1 == cs_gi && (v == vs_e || v == vs_er) && - (c2 == cs_n || c2 == cs_ng)) + if (c1 == cs_gi && (v == vs_e || v == vs_er) && (c2 == cs_n || c2 == cs_ng)) return true; } return false; @@ -1015,8 +394,7 @@ void engineClassInit() { unsigned char ch; for (ch = 'a'; ch <= 'z'; ch++) { - if (ch != 'a' && ch != 'e' && ch != 'i' && ch != 'o' && ch != 'u' && - ch != 'y') { + if (ch != 'a' && ch != 'e' && ch != 'i' && ch != 'o' && ch != 'u' && ch != 'y') { IsVnVowel[AZLexiLower[ch - 'a']] = false; IsVnVowel[AZLexiUpper[ch - 'a']] = false; } @@ -1032,8 +410,7 @@ VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2, VnLexiName v3) { key.v[1] = v2; key.v[2] = v3; - VSeqPair *pInfo = (VSeqPair *)bsearch(&key, SortedVSeqList, VSeqCount, - sizeof(VSeqPair), tripleVowelCompare); + VSeqPair* pInfo = (VSeqPair*)bsearch(&key, SortedVSeqList, VSeqCount, sizeof(VSeqPair), tripleVowelCompare); if (pInfo == 0) return vs_nil; return pInfo->vs; @@ -1046,55 +423,47 @@ ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2, VnLexiName c3) { key.c[1] = c2; key.c[2] = c3; - CSeqPair *pInfo = (CSeqPair *)bsearch(&key, SortedCSeqList, CSeqCount, - sizeof(CSeqPair), tripleConCompare); + CSeqPair* pInfo = (CSeqPair*)bsearch(&key, SortedCSeqList, CSeqCount, sizeof(CSeqPair), tripleConCompare); if (pInfo == 0) return cs_nil; return pInfo->cs; } //------------------------------------------------------------------ -int UkEngine::processRoof(UkKeyEvent &ev) { +int UkEngine::processRoof(UkKeyEvent& ev) { if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0) return processAppend(ev); VnLexiName target; switch (ev.evType) { - case vneRoof_a: - target = vnl_ar; - break; - case vneRoof_e: - target = vnl_er; - break; - case vneRoof_o: - target = vnl_or; - break; - default: - target = vnl_nonVnChar; + case vneRoof_a: target = vnl_ar; break; + case vneRoof_e: target = vnl_er; break; + case vneRoof_o: target = vnl_or; break; + default: target = vnl_nonVnChar; } VowelSeq vs, newVs; - int i, vStart, vEnd; - int curTonePos, newTonePos, tone; - int changePos; - bool roofRemoved = false; - - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - (VSeqList[vs].len - 1); + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + int changePos; + bool roofRemoved = false; + + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - (VSeqList[vs].len - 1); curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; bool doubleChangeUO = false; if (vs == vs_uho || vs == vs_uhoh || vs == vs_uhoi || vs == vs_uhohi) { // special cases: u+o+ -> uo^, u+o -> uo^, u+o+i -> uo^i, u+oi -> uo^i - newVs = lookupVSeq(vnl_u, vnl_or, VSeqList[vs].v[2]); + newVs = lookupVSeq(vnl_u, vnl_or, VSeqList[vs].v[2]); doubleChangeUO = true; } else { newVs = VSeqList[vs].withRoof; } - VowelSeqInfo *pInfo; + VowelSeqInfo* pInfo; if (newVs == vs_nil) { if (VSeqList[vs].roofPos == -1) @@ -1103,12 +472,10 @@ int UkEngine::processRoof(UkKeyEvent &ev) { // a roof already exists -> undo roof VnLexiName curCh = m_buffer[vStart + VSeqList[vs].roofPos].vnSym; if (target != vnl_nonVnChar && curCh != target) - return processAppend( - ev); // specific roof and the roof character don't match + return processAppend(ev); // specific roof and the roof character don't match - VnLexiName newCh = - (curCh == vnl_ar) ? vnl_a : ((curCh == vnl_er) ? vnl_e : vnl_o); - changePos = vStart + VSeqList[vs].roofPos; + VnLexiName newCh = (curCh == vnl_ar) ? vnl_a : ((curCh == vnl_er) ? vnl_e : vnl_o); + changePos = vStart + VSeqList[vs].roofPos; if (!m_pCtrl->options.freeMarking && changePos != m_current) return processAppend(ev); @@ -1117,16 +484,13 @@ int UkEngine::processRoof(UkKeyEvent &ev) { m_buffer[changePos].vnSym = newCh; if (VSeqList[vs].len == 3) - newVs = - lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, - m_buffer[vStart + 2].vnSym); + newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, m_buffer[vStart + 2].vnSym); else if (VSeqList[vs].len == 2) - newVs = - lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); + newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); else newVs = lookupVSeq(m_buffer[vStart].vnSym); - pInfo = &VSeqList[newVs]; + pInfo = &VSeqList[newVs]; roofRemoved = true; } else { pInfo = &VSeqList[newVs]; @@ -1134,9 +498,9 @@ int UkEngine::processRoof(UkKeyEvent &ev) { return processAppend(ev); // check validity of new VC and CV - bool valid = true; - ConSeq c1 = cs_nil; - ConSeq c2 = cs_nil; + bool valid = true; + ConSeq c1 = cs_nil; + ConSeq c2 = cs_nil; if (m_buffer[m_current].c1Offset != -1) c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; @@ -1156,7 +520,7 @@ int UkEngine::processRoof(UkKeyEvent &ev) { return processAppend(ev); markChange(changePos); if (doubleChangeUO) { - m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart].vnSym = vnl_u; m_buffer[vStart + 1].vnSym = vnl_or; } else { m_buffer[changePos].vnSym = pInfo->v[pInfo->roofPos]; @@ -1198,122 +562,117 @@ int UkEngine::processRoof(UkKeyEvent &ev) { //------------------------------------------------------------------ // can only be called from processHook //------------------------------------------------------------------ -int UkEngine::processHookWithUO(UkKeyEvent &ev) { +int UkEngine::processHookWithUO(UkKeyEvent& ev) { VowelSeq vs, newVs; - int i, vStart, vEnd; - int curTonePos, newTonePos, tone; - bool hookRemoved = false; - bool removeWithUndo = true; - bool toneRemoved = false; + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + bool hookRemoved = false; + bool removeWithUndo = true; + bool toneRemoved = false; (void)toneRemoved; // fix warning - VnLexiName *v; + VnLexiName* v; if (!m_pCtrl->options.freeMarking && m_buffer[m_current].vOffset != 0) return processAppend(ev); - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - (VSeqList[vs].len - 1); - v = VSeqList[vs].v; + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - (VSeqList[vs].len - 1); + v = VSeqList[vs].v; curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; switch (ev.evType) { - case vneHook_u: - if (v[0] == vnl_u) { - newVs = VSeqList[vs].withHook; - markChange(vStart); - m_buffer[vStart].vnSym = vnl_uh; - } else { // v[0] = vnl_uh, -> uo - newVs = lookupVSeq(vnl_u, vnl_o, v[2]); - markChange(vStart); - m_buffer[vStart].vnSym = vnl_u; - m_buffer[vStart + 1].vnSym = vnl_o; - hookRemoved = true; - toneRemoved = (m_buffer[vStart].tone != 0); - } - break; - case vneHook_o: - if (v[1] == vnl_o || v[1] == vnl_or) { - if (vEnd == m_current && VSeqList[vs].len == 2 && - m_buffer[m_current].form == vnw_cv && - m_buffer[m_current - 2].cseq == cs_th) { - // o|o^ -> o+ + case vneHook_u: + if (v[0] == vnl_u) { newVs = VSeqList[vs].withHook; - markChange(vStart + 1); - m_buffer[vStart + 1].vnSym = vnl_oh; - } else { - newVs = lookupVSeq(vnl_uh, vnl_oh, v[2]); - if (v[0] == vnl_u) { - markChange(vStart); - m_buffer[vStart].vnSym = vnl_uh; - m_buffer[vStart + 1].vnSym = vnl_oh; - } else { - markChange(vStart + 1); - m_buffer[vStart + 1].vnSym = vnl_oh; - } - } - } else { // v[1] = vnl_oh, -> uo - newVs = lookupVSeq(vnl_u, vnl_o, v[2]); - if (v[0] == vnl_uh) { markChange(vStart); - m_buffer[vStart].vnSym = vnl_u; - m_buffer[vStart + 1].vnSym = vnl_o; - } else { - markChange(vStart + 1); + m_buffer[vStart].vnSym = vnl_uh; + } else { // v[0] = vnl_uh, -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; m_buffer[vStart + 1].vnSym = vnl_o; + hookRemoved = true; + toneRemoved = (m_buffer[vStart].tone != 0); } - hookRemoved = true; - toneRemoved = (m_buffer[vStart + 1].tone != 0); - } - break; - default: // vneHookAll, vneHookUO: - if (v[0] == vnl_u) { + break; + case vneHook_o: if (v[1] == vnl_o || v[1] == vnl_or) { - // uo -> uo+ if prefixed by "h", "kh", "th", or stand alone - if ((vs == vs_uo || vs == vs_uor) && vEnd == m_current && - ((m_buffer[m_current].form == vnw_cv && - (m_buffer[m_current - 2].cseq == cs_h || - m_buffer[m_current - 2].cseq == cs_kh || - m_buffer[m_current - 2].cseq == cs_th)) || - m_buffer[m_current].form == vnw_v)) { - newVs = vs_uoh; + if (vEnd == m_current && VSeqList[vs].len == 2 && m_buffer[m_current].form == vnw_cv && m_buffer[m_current - 2].cseq == cs_th) { + // o|o^ -> o+ + newVs = VSeqList[vs].withHook; markChange(vStart + 1); m_buffer[vStart + 1].vnSym = vnl_oh; } else { - // uo -> u+o+ + newVs = lookupVSeq(vnl_uh, vnl_oh, v[2]); + if (v[0] == vnl_u) { + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } + } + } else { // v[1] = vnl_oh, -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); + if (v[0] == vnl_uh) { + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + } else { + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_o; + } + hookRemoved = true; + toneRemoved = (m_buffer[vStart + 1].tone != 0); + } + break; + default: // vneHookAll, vneHookUO: + if (v[0] == vnl_u) { + if (v[1] == vnl_o || v[1] == vnl_or) { + // uo -> uo+ if prefixed by "h", "kh", "th", or stand alone + if ((vs == vs_uo || vs == vs_uor) && vEnd == m_current && + ((m_buffer[m_current].form == vnw_cv && + (m_buffer[m_current - 2].cseq == cs_h || m_buffer[m_current - 2].cseq == cs_kh || m_buffer[m_current - 2].cseq == cs_th)) || + m_buffer[m_current].form == vnw_v)) { + newVs = vs_uoh; + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { + // uo -> u+o+ + newVs = VSeqList[vs].withHook; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + newVs = VSeqList[newVs].withHook; + m_buffer[vStart + 1].vnSym = vnl_oh; + } + } else { // uo+ -> u+o+ newVs = VSeqList[vs].withHook; markChange(vStart); m_buffer[vStart].vnSym = vnl_uh; - newVs = VSeqList[newVs].withHook; + } + } else { // v[0] == vnl_uh + if (v[1] == vnl_o) { // u+o -> u+o+ + newVs = VSeqList[vs].withHook; + markChange(vStart + 1); m_buffer[vStart + 1].vnSym = vnl_oh; + } else { // v[1] == vnl_oh, u+o+ -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); // vs_uo; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + hookRemoved = true; + toneRemoved = (m_buffer[vStart].tone != 0 || m_buffer[vStart + 1].tone != 0); } - } else { // uo+ -> u+o+ - newVs = VSeqList[vs].withHook; - markChange(vStart); - m_buffer[vStart].vnSym = vnl_uh; - } - } else { // v[0] == vnl_uh - if (v[1] == vnl_o) { // u+o -> u+o+ - newVs = VSeqList[vs].withHook; - markChange(vStart + 1); - m_buffer[vStart + 1].vnSym = vnl_oh; - } else { // v[1] == vnl_oh, u+o+ -> uo - newVs = lookupVSeq(vnl_u, vnl_o, v[2]); // vs_uo; - markChange(vStart); - m_buffer[vStart].vnSym = vnl_u; - m_buffer[vStart + 1].vnSym = vnl_o; - hookRemoved = true; - toneRemoved = (m_buffer[vStart].tone != 0 || - m_buffer[vStart + 1].tone != 0); } - } - break; + break; } - VowelSeqInfo *p = &VSeqList[newVs]; + VowelSeqInfo* p = &VSeqList[newVs]; for (i = 0; i < p->len; i++) { // update sub-sequences m_buffer[vStart + i].vseq = p->sub[i]; } @@ -1348,31 +707,29 @@ int UkEngine::processHookWithUO(UkKeyEvent &ev) { } //------------------------------------------------------------------ -int UkEngine::processHook(UkKeyEvent &ev) { +int UkEngine::processHook(UkKeyEvent& ev) { if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0) return processAppend(ev); - VowelSeq vs, newVs; - int i, vStart, vEnd; - int curTonePos, newTonePos, tone; - int changePos; - bool hookRemoved = false; - VowelSeqInfo *pInfo; - VnLexiName *v; + VowelSeq vs, newVs; + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + int changePos; + bool hookRemoved = false; + VowelSeqInfo* pInfo; + VnLexiName* v; vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; + vs = m_buffer[vEnd].vseq; v = VSeqList[vs].v; - if (VSeqList[vs].len > 1 && ev.evType != vneBowl && - (v[0] == vnl_u || v[0] == vnl_uh) && - (v[1] == vnl_o || v[1] == vnl_oh || v[1] == vnl_or)) + if (VSeqList[vs].len > 1 && ev.evType != vneBowl && (v[0] == vnl_u || v[0] == vnl_uh) && (v[1] == vnl_o || v[1] == vnl_oh || v[1] == vnl_or)) return processHookWithUO(ev); - vStart = vEnd - (VSeqList[vs].len - 1); + vStart = vEnd - (VSeqList[vs].len - 1); curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; newVs = VSeqList[vs].withHook; if (newVs == vs_nil) { @@ -1381,70 +738,66 @@ int UkEngine::processHook(UkKeyEvent &ev) { // a hook already exists -> undo hook VnLexiName curCh = m_buffer[vStart + VSeqList[vs].hookPos].vnSym; - VnLexiName newCh = - (curCh == vnl_ab) ? vnl_a : ((curCh == vnl_uh) ? vnl_u : vnl_o); - changePos = vStart + VSeqList[vs].hookPos; + VnLexiName newCh = (curCh == vnl_ab) ? vnl_a : ((curCh == vnl_uh) ? vnl_u : vnl_o); + changePos = vStart + VSeqList[vs].hookPos; if (!m_pCtrl->options.freeMarking && changePos != m_current) return processAppend(ev); switch (ev.evType) { - case vneHook_u: - if (curCh != vnl_uh) - return processAppend(ev); - break; - case vneHook_o: - if (curCh != vnl_oh) - return processAppend(ev); - break; - case vneBowl: - if (curCh != vnl_ab) - return processAppend(ev); - break; - default: - if (ev.evType == vneHook_uo && curCh == vnl_ab) - return processAppend(ev); + case vneHook_u: + if (curCh != vnl_uh) + return processAppend(ev); + break; + case vneHook_o: + if (curCh != vnl_oh) + return processAppend(ev); + break; + case vneBowl: + if (curCh != vnl_ab) + return processAppend(ev); + break; + default: + if (ev.evType == vneHook_uo && curCh == vnl_ab) + return processAppend(ev); } markChange(changePos); m_buffer[changePos].vnSym = newCh; if (VSeqList[vs].len == 3) - newVs = - lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, - m_buffer[vStart + 2].vnSym); + newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, m_buffer[vStart + 2].vnSym); else if (VSeqList[vs].len == 2) - newVs = - lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); + newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); else newVs = lookupVSeq(m_buffer[vStart].vnSym); - pInfo = &VSeqList[newVs]; + pInfo = &VSeqList[newVs]; hookRemoved = true; } else { pInfo = &VSeqList[newVs]; switch (ev.evType) { - case vneHook_u: - if (pInfo->v[pInfo->hookPos] != vnl_uh) - return processAppend(ev); - break; - case vneHook_o: - if (pInfo->v[pInfo->hookPos] != vnl_oh) - return processAppend(ev); - break; - case vneBowl: - if (pInfo->v[pInfo->hookPos] != vnl_ab) - return processAppend(ev); - break; - default: // vneHook_uo, vneHookAll - if (ev.evType == vneHook_uo && pInfo->v[pInfo->hookPos] == vnl_ab) - return processAppend(ev); + case vneHook_u: + if (pInfo->v[pInfo->hookPos] != vnl_uh) + return processAppend(ev); + break; + case vneHook_o: + if (pInfo->v[pInfo->hookPos] != vnl_oh) + return processAppend(ev); + break; + case vneBowl: + if (pInfo->v[pInfo->hookPos] != vnl_ab) + return processAppend(ev); + break; + default: // vneHook_uo, vneHookAll + if (ev.evType == vneHook_uo && pInfo->v[pInfo->hookPos] == vnl_ab) + return processAppend(ev); } // check validity of new VC and CV - bool valid = true; - ConSeq c1 = cs_nil; - ConSeq c2 = cs_nil; + bool valid = true; + ConSeq c1 = cs_nil; + ConSeq c2 = cs_nil; if (m_buffer[m_current].c1Offset != -1) c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; @@ -1498,15 +851,14 @@ int UkEngine::processHook(UkKeyEvent &ev) { //---------------------------------------------------------- int UkEngine::getTonePosition(VowelSeq vs, bool terminated) const { - VowelSeqInfo &info = VSeqList[vs]; + VowelSeqInfo& info = VSeqList[vs]; if (info.len == 1) return 0; if (info.roofPos != -1) return info.roofPos; if (info.hookPos != -1) { - if (vs == vs_uhoh || vs == vs_uhohi || - vs == vs_uhohu) // u+o+, u+o+u, u+o+i + if (vs == vs_uhoh || vs == vs_uhohi || vs == vs_uhohu) // u+o+, u+o+u, u+o+i return 1; return info.hookPos; } @@ -1514,28 +866,25 @@ int UkEngine::getTonePosition(VowelSeq vs, bool terminated) const { if (info.len == 3) return 1; - if (m_pCtrl->options.modernStyle && - (vs == vs_oa || vs == vs_oe || vs == vs_uy)) + if (m_pCtrl->options.modernStyle && (vs == vs_oa || vs == vs_oe || vs == vs_uy)) return 1; return terminated ? 0 : 1; } //---------------------------------------------------------- -int UkEngine::processTone(UkKeyEvent &ev) { +int UkEngine::processTone(UkKeyEvent& ev) { if (m_current < 0 || !m_pCtrl->vietKey) return processAppend(ev); - if (m_buffer[m_current].form == vnw_c && - (m_buffer[m_current].cseq == cs_gi || - m_buffer[m_current].cseq == cs_gin)) { + if (m_buffer[m_current].form == vnw_c && (m_buffer[m_current].cseq == cs_gi || m_buffer[m_current].cseq == cs_gin)) { int p = (m_buffer[m_current].cseq == cs_gi) ? m_current : m_current - 1; if (m_buffer[p].tone == 0 && ev.tone == 0) return processAppend(ev); markChange(p); if (m_buffer[p].tone == ev.tone) { m_buffer[p].tone = 0; - m_singleMode = false; + m_singleMode = false; processAppend(ev); m_reverted = true; return 1; @@ -1547,26 +896,23 @@ int UkEngine::processTone(UkKeyEvent &ev) { if (m_buffer[m_current].vOffset < 0) return processAppend(ev); - int vEnd; + int vEnd; VowelSeq vs; - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - VowelSeqInfo &info = VSeqList[vs]; - if (m_pCtrl->options.spellCheckEnabled && !m_pCtrl->options.freeMarking && - !info.complete) + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + VowelSeqInfo& info = VSeqList[vs]; + if (m_pCtrl->options.spellCheckEnabled && !m_pCtrl->options.freeMarking && !info.complete) return processAppend(ev); - if (m_buffer[m_current].form == vnw_vc || - m_buffer[m_current].form == vnw_cvc) { + if (m_buffer[m_current].form == vnw_vc || m_buffer[m_current].form == vnw_cvc) { ConSeq cs = m_buffer[m_current].cseq; - if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && - (ev.tone == 2 || ev.tone == 3 || ev.tone == 4)) + if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && (ev.tone == 2 || ev.tone == 3 || ev.tone == 4)) return processAppend(ev); // c, ch, p, t suffixes don't allow ` ? ~ } int toneOffset = getTonePosition(vs, vEnd == m_current); - int tonePos = vEnd - (info.len - 1) + toneOffset; + int tonePos = vEnd - (info.len - 1) + toneOffset; if (m_buffer[tonePos].tone == 0 && ev.tone == 0) return processAppend(ev); @@ -1574,7 +920,7 @@ int UkEngine::processTone(UkKeyEvent &ev) { if (m_buffer[tonePos].tone == ev.tone) { markChange(tonePos); m_buffer[tonePos].tone = 0; - m_singleMode = false; + m_singleMode = false; processAppend(ev); m_reverted = true; return 1; @@ -1586,7 +932,7 @@ int UkEngine::processTone(UkKeyEvent &ev) { } //---------------------------------------------------------- -int UkEngine::processDd(UkKeyEvent &ev) { +int UkEngine::processDd(UkKeyEvent& ev) { if (!m_pCtrl->vietKey || m_current < 0) return processAppend(ev); @@ -1594,19 +940,17 @@ int UkEngine::processDd(UkKeyEvent &ev) { // we want to allow dd even in non-vn sequence, because dd is used a lot in // abbreviation we allow dd only if preceding character is not a vowel - if (m_buffer[m_current].form == vnw_nonVn && - m_buffer[m_current].vnSym == vnl_d && - (m_buffer[m_current - 1].vnSym == vnl_nonVnChar || - !IsVnVowel[m_buffer[m_current - 1].vnSym])) { + if (m_buffer[m_current].form == vnw_nonVn && m_buffer[m_current].vnSym == vnl_d && + (m_buffer[m_current - 1].vnSym == vnl_nonVnChar || !IsVnVowel[m_buffer[m_current - 1].vnSym])) { m_singleMode = true; - pos = m_current; + pos = m_current; markChange(pos); - m_buffer[pos].cseq = cs_dd; - m_buffer[pos].vnSym = vnl_dd; - m_buffer[pos].form = vnw_c; + m_buffer[pos].cseq = cs_dd; + m_buffer[pos].vnSym = vnl_dd; + m_buffer[pos].form = vnw_c; m_buffer[pos].c1Offset = 0; m_buffer[pos].c2Offset = -1; - m_buffer[pos].vOffset = -1; + m_buffer[pos].vOffset = -1; return 1; } @@ -1620,7 +964,7 @@ int UkEngine::processDd(UkKeyEvent &ev) { if (m_buffer[pos].cseq == cs_d) { markChange(pos); - m_buffer[pos].cseq = cs_dd; + m_buffer[pos].cseq = cs_dd; m_buffer[pos].vnSym = vnl_dd; // never spellcheck a word which starts with dd, because it's used alot // in abbreviation @@ -1631,9 +975,9 @@ int UkEngine::processDd(UkKeyEvent &ev) { if (m_buffer[pos].cseq == cs_dd) { // undo dd markChange(pos); - m_buffer[pos].cseq = cs_d; + m_buffer[pos].cseq = cs_d; m_buffer[pos].vnSym = vnl_d; - m_singleMode = false; + m_singleMode = false; processAppend(ev); m_reverted = true; return 1; @@ -1661,8 +1005,8 @@ inline VnLexiName vnToLower(VnLexiName x) { } //---------------------------------------------------------- -int UkEngine::processMapChar(UkKeyEvent &ev) { - int capsLockOn = 0; +int UkEngine::processMapChar(UkKeyEvent& ev) { + int capsLockOn = 0; int shiftPressed = 0; if (m_keyCheckFunc) m_keyCheckFunc(&shiftPressed, &capsLockOn); @@ -1674,8 +1018,7 @@ int UkEngine::processMapChar(UkKeyEvent &ev) { if (!m_pCtrl->vietKey) return ret; - if (m_current >= 0 && m_buffer[m_current].form != vnw_empty && - m_buffer[m_current].form != vnw_nonVn) { + if (m_current >= 0 && m_buffer[m_current].form != vnw_empty && m_buffer[m_current].form != vnw_nonVn) { return 1; } @@ -1684,9 +1027,9 @@ int UkEngine::processMapChar(UkKeyEvent &ev) { // mapChar doesn't apply m_current--; - WordInfo &entry = m_buffer[m_current]; + WordInfo& entry = m_buffer[m_current]; - bool undo = false; + bool undo = false; // test if undo is needed if (entry.form != vnw_empty && entry.form != vnw_nonVn) { VnLexiName prevSym = entry.vnSym; @@ -1695,22 +1038,20 @@ int UkEngine::processMapChar(UkKeyEvent &ev) { } if (prevSym == ev.vnSym) { if (entry.form != vnw_c) { - int vStart, vEnd, curTonePos, newTonePos, tone; + int vStart, vEnd, curTonePos, newTonePos, tone; VowelSeq vs, newVs; - vEnd = m_current - entry.vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - VSeqList[vs].len + 1; + vEnd = m_current - entry.vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - VSeqList[vs].len + 1; curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; markChange(m_current); m_current--; // check if tone position is needed - if (tone != 0 && m_current >= 0 && - (m_buffer[m_current].form == vnw_v || - m_buffer[m_current].form == vnw_cv)) { - newVs = m_buffer[m_current].vseq; + if (tone != 0 && m_current >= 0 && (m_buffer[m_current].form == vnw_v || m_buffer[m_current].form == vnw_cv)) { + newVs = m_buffer[m_current].vseq; newTonePos = vStart + getTonePosition(newVs, true); if (newTonePos != curTonePos) { markChange(newTonePos); @@ -1729,56 +1070,56 @@ int UkEngine::processMapChar(UkKeyEvent &ev) { ev.evType = vneNormal; ev.chType = m_pCtrl->input.getCharType(ev.keyCode); - ev.vnSym = IsoToVnLexi(ev.keyCode); - ret = processAppend(ev); + ev.vnSym = IsoToVnLexi(ev.keyCode); + ret = processAppend(ev); if (undo) { m_singleMode = false; - m_reverted = true; + m_reverted = true; return 1; } return ret; } //---------------------------------------------------------- -int UkEngine::processTelexW(UkKeyEvent &ev) { +int UkEngine::processTelexW(UkKeyEvent& ev) { if (!m_pCtrl->vietKey) return processAppend(ev); - int ret; + int ret; static bool usedAsMapChar = false; - int capsLockOn = 0; - int shiftPressed = 0; + int capsLockOn = 0; + int shiftPressed = 0; if (m_keyCheckFunc) m_keyCheckFunc(&shiftPressed, &capsLockOn); if (usedAsMapChar) { ev.evType = vneMapChar; - ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; + ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; if (capsLockOn) ev.vnSym = changeCase(ev.vnSym); ev.chType = ukcVn; - ret = processMapChar(ev); + ret = processMapChar(ev); if (ret == 0) { if (m_current >= 0) m_current--; usedAsMapChar = false; - ev.evType = vneHookAll; + ev.evType = vneHookAll; return processHook(ev); } return ret; } - ev.evType = vneHookAll; + ev.evType = vneHookAll; usedAsMapChar = false; - ret = processHook(ev); + ret = processHook(ev); if (ret == 0) { if (m_current >= 0) m_current--; ev.evType = vneMapChar; - ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; + ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; if (capsLockOn) ev.vnSym = changeCase(ev.vnSym); - ev.chType = ukcVn; + ev.chType = ukcVn; usedAsMapChar = true; return processMapChar(ev); } @@ -1786,306 +1127,274 @@ int UkEngine::processTelexW(UkKeyEvent &ev) { } //---------------------------------------------------------- -int UkEngine::checkEscapeVIQR(UkKeyEvent &ev) { +int UkEngine::checkEscapeVIQR(UkKeyEvent& ev) { if (m_current < 0) return 0; - WordInfo &entry = m_buffer[m_current]; - int escape = 0; + WordInfo& entry = m_buffer[m_current]; + int escape = 0; if (entry.form == vnw_v || entry.form == vnw_cv) { switch (ev.keyCode) { - case '^': - escape = (entry.vnSym == vnl_a || entry.vnSym == vnl_o || - entry.vnSym == vnl_e); - break; - case '(': - escape = (entry.vnSym == vnl_a); - break; - case '+': - escape = (entry.vnSym == vnl_o || entry.vnSym == vnl_u); - break; - case '\'': - case '`': - case '?': - case '~': - case '.': - escape = (entry.tone == 0); - break; + case '^': escape = (entry.vnSym == vnl_a || entry.vnSym == vnl_o || entry.vnSym == vnl_e); break; + case '(': escape = (entry.vnSym == vnl_a); break; + case '+': escape = (entry.vnSym == vnl_o || entry.vnSym == vnl_u); break; + case '\'': + case '`': + case '?': + case '~': + case '.': escape = (entry.tone == 0); break; } } else if (entry.form == vnw_nonVn) { unsigned char ch = toupper(entry.keyCode); switch (ev.keyCode) { - case '^': - escape = (ch == 'A' || ch == 'O' || ch == 'E'); - break; - case '(': - escape = (ch == 'A'); - break; - case '+': - escape = (ch == 'O' || ch == 'U'); - break; - case '\'': - case '`': - case '?': - case '~': - case '.': - escape = (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || - ch == 'U' || ch == 'Y'); - break; + case '^': escape = (ch == 'A' || ch == 'O' || ch == 'E'); break; + case '(': escape = (ch == 'A'); break; + case '+': escape = (ch == 'O' || ch == 'U'); break; + case '\'': + case '`': + case '?': + case '~': + case '.': escape = (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'Y'); break; } } if (escape) { m_current++; - WordInfo *p = &m_buffer[m_current]; - p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + WordInfo* p = &m_buffer[m_current]; + p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; p->c1Offset = p->c2Offset = p->vOffset = -1; - p->keyCode = '?'; - p->vnSym = vnl_nonVnChar; + p->keyCode = '?'; + p->vnSym = vnl_nonVnChar; m_current++; p++; - p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; p->c1Offset = p->c2Offset = p->vOffset = -1; - p->keyCode = ev.keyCode; - p->vnSym = vnl_nonVnChar; + p->keyCode = ev.keyCode; + p->vnSym = vnl_nonVnChar; // write output - m_pOutBuf[0] = '\\'; - m_pOutBuf[1] = ev.keyCode; - *m_pOutSize = 2; + m_pOutBuf[0] = '\\'; + m_pOutBuf[1] = ev.keyCode; + *m_pOutSize = 2; m_outputWritten = true; } return escape; } //---------------------------------------------------------- -int UkEngine::processAppend(UkKeyEvent &ev) { +int UkEngine::processAppend(UkKeyEvent& ev) { int ret = 0; switch (ev.chType) { - case ukcReset: + case ukcReset: #if defined(_WIN32) - if (ev.keyCode == ENTER_CHAR) { - if (m_pCtrl->options.macroEnabled && macroMatch(ev)) - return 1; - } + if (ev.keyCode == ENTER_CHAR) { + if (m_pCtrl->options.macroEnabled && macroMatch(ev)) + return 1; + } #endif - reset(); - return 0; - case ukcWordBreak: - m_singleMode = false; - return processWordEnd(ev); - case ukcNonVn: { - if (m_pCtrl->vietKey && m_pCtrl->charsetId == CONV_CHARSET_VIQR && - checkEscapeVIQR(ev)) - return 1; - - m_current++; - WordInfo &entry = m_buffer[m_current]; - entry.form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - entry.keyCode = ev.keyCode; - entry.vnSym = vnToLower(ev.vnSym); - entry.tone = 0; - entry.caps = (entry.vnSym != ev.vnSym); - if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + reset(); return 0; - markChange(m_current); - return 1; - } - case ukcVn: { - if (IsVnVowel[ev.vnSym]) { - VnLexiName v = (VnLexiName)StdVnNoTone[vnToLower(ev.vnSym)]; - if (m_current >= 0 && m_buffer[m_current].form == vnw_c && - ((m_buffer[m_current].cseq == cs_q && v == vnl_u) || - (m_buffer[m_current].cseq == cs_g && v == vnl_i))) { - return appendConsonnant( - ev); // process u after q, i after g as consonnants - } - return appendVowel(ev); + case ukcWordBreak: m_singleMode = false; return processWordEnd(ev); + case ukcNonVn: { + if (m_pCtrl->vietKey && m_pCtrl->charsetId == CONV_CHARSET_VIQR && checkEscapeVIQR(ev)) + return 1; + + m_current++; + WordInfo& entry = m_buffer[m_current]; + entry.form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + entry.keyCode = ev.keyCode; + entry.vnSym = vnToLower(ev.vnSym); + entry.tone = 0; + entry.caps = (entry.vnSym != ev.vnSym); + if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; } - return appendConsonnant(ev); - } break; + case ukcVn: { + if (IsVnVowel[ev.vnSym]) { + VnLexiName v = (VnLexiName)StdVnNoTone[vnToLower(ev.vnSym)]; + if (m_current >= 0 && m_buffer[m_current].form == vnw_c && ((m_buffer[m_current].cseq == cs_q && v == vnl_u) || (m_buffer[m_current].cseq == cs_g && v == vnl_i))) { + return appendConsonnant(ev); // process u after q, i after g as consonnants + } + return appendVowel(ev); + } + return appendConsonnant(ev); + } break; } return ret; } //---------------------------------------------------------- -int UkEngine::appendVowel(UkKeyEvent &ev) { +int UkEngine::appendVowel(UkKeyEvent& ev) { bool autoCompleted = false; - bool complexEvent = false; + bool complexEvent = false; m_current++; - WordInfo &entry = m_buffer[m_current]; + WordInfo& entry = m_buffer[m_current]; VnLexiName lowerSym = vnToLower(ev.vnSym); - VnLexiName canSym = (VnLexiName)StdVnNoTone[lowerSym]; + VnLexiName canSym = (VnLexiName)StdVnNoTone[lowerSym]; - entry.vnSym = canSym; - entry.caps = (lowerSym != ev.vnSym); - entry.tone = (lowerSym - canSym) / 2; + entry.vnSym = canSym; + entry.caps = (lowerSym != ev.vnSym); + entry.tone = (lowerSym - canSym) / 2; entry.keyCode = ev.keyCode; if (m_current == 0 || !m_pCtrl->vietKey) { - entry.form = vnw_v; + entry.form = vnw_v; entry.c1Offset = entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = lookupVSeq(canSym); + entry.vOffset = 0; + entry.vseq = lookupVSeq(canSym); - if (!m_pCtrl->vietKey || - ((m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && - isalpha(entry.keyCode))) { + if (!m_pCtrl->vietKey || ((m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && isalpha(entry.keyCode))) { return 0; } markChange(m_current); return 1; } - WordInfo &prev = m_buffer[m_current - 1]; - VowelSeq vs, newVs; - ConSeq cs; - int prevTonePos; - int tone, newTone, tonePos, newTonePos; + WordInfo& prev = m_buffer[m_current - 1]; + VowelSeq vs, newVs; + ConSeq cs; + int prevTonePos; + int tone, newTone, tonePos, newTonePos; switch (prev.form) { - case vnw_empty: - entry.form = vnw_v; - entry.c1Offset = entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = newVs = lookupVSeq(canSym); - break; - - case vnw_nonVn: - case vnw_cvc: - case vnw_vc: - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - break; + case vnw_empty: + entry.form = vnw_v; + entry.c1Offset = entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs = lookupVSeq(canSym); + break; - case vnw_v: - case vnw_cv: - vs = prev.vseq; + case vnw_nonVn: + case vnw_cvc: + case vnw_vc: + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; - prevTonePos = (m_current - 1) - (VSeqList[vs].len - 1) + - getTonePosition(vs, true); - tone = m_buffer[prevTonePos].tone; + case vnw_v: + case vnw_cv: + vs = prev.vseq; - // u+o/uo+ + u/i -> u+o+ + u/i - if ((vs == vs_uoh || vs == vs_uho) && - (lowerSym == vnl_i || lowerSym == vnl_u)) { - if (vs == vs_uho) { - markChange(m_current - 1); - prev.vnSym = vnl_oh; - prev.vseq = vs_uhoh; - } else { - markChange(m_current - 2); - m_buffer[m_current - 2].vnSym = vnl_uh; - m_buffer[m_current - 2].vseq = vs_uh; - } + prevTonePos = (m_current - 1) - (VSeqList[vs].len - 1) + getTonePosition(vs, true); + tone = m_buffer[prevTonePos].tone; - vs = vs_uhoh; - complexEvent = true; - } + // u+o/uo+ + u/i -> u+o+ + u/i + if ((vs == vs_uoh || vs == vs_uho) && (lowerSym == vnl_i || lowerSym == vnl_u)) { + if (vs == vs_uho) { + markChange(m_current - 1); + prev.vnSym = vnl_oh; + prev.vseq = vs_uhoh; + } else { + markChange(m_current - 2); + m_buffer[m_current - 2].vnSym = vnl_uh; + m_buffer[m_current - 2].vseq = vs_uh; + } - if (lowerSym != canSym && tone != 0) // new sym has a tone, but there's - // is already a preceeding tone - newVs = vs_nil; - else { - if (VSeqList[vs].len == 3) - newVs = vs_nil; - else if (VSeqList[vs].len == 2) - newVs = - lookupVSeq(VSeqList[vs].v[0], VSeqList[vs].v[1], canSym); - else - newVs = lookupVSeq(VSeqList[vs].v[0], canSym); - } + vs = vs_uhoh; + complexEvent = true; + } - if (newVs != vs_nil && prev.form == vnw_cv) { - cs = m_buffer[m_current - 1 - prev.c1Offset].cseq; - if (!isValidCV(cs, newVs)) + if (lowerSym != canSym && tone != 0) // new sym has a tone, but there's + // is already a preceeding tone newVs = vs_nil; - } + else { + if (VSeqList[vs].len == 3) + newVs = vs_nil; + else if (VSeqList[vs].len == 2) + newVs = lookupVSeq(VSeqList[vs].v[0], VSeqList[vs].v[1], canSym); + else + newVs = lookupVSeq(VSeqList[vs].v[0], canSym); + } - if (newVs == vs_nil) { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - break; - } + if (newVs != vs_nil && prev.form == vnw_cv) { + cs = m_buffer[m_current - 1 - prev.c1Offset].cseq; + if (!isValidCV(cs, newVs)) + newVs = vs_nil; + } - entry.form = prev.form; - if (prev.form == vnw_cv) - entry.c1Offset = prev.c1Offset + 1; - else - entry.c1Offset = -1; - entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = newVs; - entry.tone = 0; - - newTone = (lowerSym - canSym) / 2; - if (tone == 0) { - if (newTone != 0) { - tone = newTone; - tonePos = getTonePosition(newVs, true) + - ((m_current - 1) - VSeqList[vs].len + 1); - markChange(tonePos); - m_buffer[tonePos].tone = tone; - return 1; + if (newVs == vs_nil) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; } - } else { - newTonePos = getTonePosition(newVs, true) + - ((m_current - 1) - VSeqList[vs].len + 1); - if (newTonePos != prevTonePos) { - markChange(prevTonePos); - m_buffer[prevTonePos].tone = 0; - markChange(newTonePos); - if (newTone != 0) + + entry.form = prev.form; + if (prev.form == vnw_cv) + entry.c1Offset = prev.c1Offset + 1; + else + entry.c1Offset = -1; + entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs; + entry.tone = 0; + + newTone = (lowerSym - canSym) / 2; + if (tone == 0) { + if (newTone != 0) { + tone = newTone; + tonePos = getTonePosition(newVs, true) + ((m_current - 1) - VSeqList[vs].len + 1); + markChange(tonePos); + m_buffer[tonePos].tone = tone; + return 1; + } + } else { + newTonePos = getTonePosition(newVs, true) + ((m_current - 1) - VSeqList[vs].len + 1); + if (newTonePos != prevTonePos) { + markChange(prevTonePos); + m_buffer[prevTonePos].tone = 0; + markChange(newTonePos); + if (newTone != 0) + tone = newTone; + m_buffer[newTonePos].tone = tone; + return 1; + } + if (newTone != 0 && newTone != tone) { tone = newTone; - m_buffer[newTonePos].tone = tone; - return 1; - } - if (newTone != 0 && newTone != tone) { - tone = newTone; - markChange(prevTonePos); - m_buffer[prevTonePos].tone = tone; - return 1; + markChange(prevTonePos); + m_buffer[prevTonePos].tone = tone; + return 1; + } } - } - break; - case vnw_c: - newVs = lookupVSeq(canSym); - cs = prev.cseq; - if (!isValidCV(cs, newVs)) { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; break; - } + case vnw_c: + newVs = lookupVSeq(canSym); + cs = prev.cseq; + if (!isValidCV(cs, newVs)) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; + } - entry.form = vnw_cv; - entry.c1Offset = 1; - entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = newVs; - - if (cs == cs_gi && prev.tone != 0) { - if (entry.tone == 0) - entry.tone = prev.tone; - markChange(m_current - 1); - prev.tone = 0; - return 1; - } + entry.form = vnw_cv; + entry.c1Offset = 1; + entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs; + + if (cs == cs_gi && prev.tone != 0) { + if (entry.tone == 0) + entry.tone = prev.tone; + markChange(m_current - 1); + prev.tone = 0; + return 1; + } - break; + break; } if (complexEvent) { return 1; } - if (!autoCompleted && (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && - isalpha(entry.keyCode)) { + if (!autoCompleted && (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && isalpha(entry.keyCode)) { return 0; } @@ -2094,175 +1403,173 @@ int UkEngine::appendVowel(UkKeyEvent &ev) { } //---------------------------------------------------------- -int UkEngine::appendConsonnant(UkKeyEvent &ev) { +int UkEngine::appendConsonnant(UkKeyEvent& ev) { bool complexEvent = false; m_current++; - WordInfo &entry = m_buffer[m_current]; + WordInfo& entry = m_buffer[m_current]; VnLexiName lowerSym = vnToLower(ev.vnSym); - entry.vnSym = lowerSym; - entry.caps = (lowerSym != ev.vnSym); + entry.vnSym = lowerSym; + entry.caps = (lowerSym != ev.vnSym); entry.keyCode = ev.keyCode; - entry.tone = 0; + entry.tone = 0; if (m_current == 0 || !m_pCtrl->vietKey) { - entry.form = vnw_c; + entry.form = vnw_c; entry.c1Offset = 0; entry.c2Offset = -1; - entry.vOffset = -1; - entry.cseq = lookupCSeq(lowerSym); + entry.vOffset = -1; + entry.cseq = lookupCSeq(lowerSym); if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) return 0; markChange(m_current); return 1; } - ConSeq cs, newCs, c1; - VowelSeq vs, newVs; - bool isValid; + ConSeq cs, newCs, c1; + VowelSeq vs, newVs; + bool isValid; - WordInfo &prev = m_buffer[m_current - 1]; + WordInfo& prev = m_buffer[m_current - 1]; switch (prev.form) { - case vnw_nonVn: - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; - case vnw_empty: - entry.form = vnw_c; - entry.c1Offset = 0; - entry.c2Offset = -1; - entry.vOffset = -1; - entry.cseq = lookupCSeq(lowerSym); - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; - case vnw_v: - case vnw_cv: - vs = prev.vseq; - newVs = vs; - if (vs == vs_uoh || vs == vs_uho) { - newVs = vs_uhoh; - } + case vnw_nonVn: + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_empty: + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + entry.cseq = lookupCSeq(lowerSym); + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_v: + case vnw_cv: + vs = prev.vseq; + newVs = vs; + if (vs == vs_uoh || vs == vs_uho) { + newVs = vs_uhoh; + } - c1 = cs_nil; - if (prev.c1Offset != -1) - c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; + c1 = cs_nil; + if (prev.c1Offset != -1) + c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; - newCs = lookupCSeq(lowerSym); - isValid = isValidCVC(c1, newVs, newCs); + newCs = lookupCSeq(lowerSym); + isValid = isValidCVC(c1, newVs, newCs); + + if (isValid) { + // check u+o -> u+o+ + if (vs == vs_uho) { + markChange(m_current - 1); + prev.vnSym = vnl_oh; + prev.vseq = vs_uhoh; + complexEvent = true; + } else if (vs == vs_uoh) { + markChange(m_current - 2); + m_buffer[m_current - 2].vnSym = vnl_uh; + m_buffer[m_current - 2].vseq = vs_uh; + prev.vseq = vs_uhoh; + complexEvent = true; + } - if (isValid) { - // check u+o -> u+o+ - if (vs == vs_uho) { - markChange(m_current - 1); - prev.vnSym = vnl_oh; - prev.vseq = vs_uhoh; - complexEvent = true; - } else if (vs == vs_uoh) { - markChange(m_current - 2); - m_buffer[m_current - 2].vnSym = vnl_uh; - m_buffer[m_current - 2].vseq = vs_uh; - prev.vseq = vs_uhoh; - complexEvent = true; + if (prev.form == vnw_v) { + entry.form = vnw_vc; + entry.c1Offset = -1; + entry.c2Offset = 0; + entry.vOffset = 1; + } else { // prev == vnw_cv + entry.form = vnw_cvc; + entry.c1Offset = prev.c1Offset + 1; + entry.c2Offset = 0; + entry.vOffset = 1; + } + entry.cseq = newCs; + + // reposition tone if needed + int oldIdx = (m_current - 1) - (VSeqList[vs].len - 1) + getTonePosition(vs, true); + if (m_buffer[oldIdx].tone != 0) { + int newIdx = (m_current - 1) - (VSeqList[newVs].len - 1) + getTonePosition(newVs, false); + if (newIdx != oldIdx) { + markChange(newIdx); + m_buffer[newIdx].tone = m_buffer[oldIdx].tone; + markChange(oldIdx); + m_buffer[oldIdx].tone = 0; + return 1; + } + } + } else { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; } - if (prev.form == vnw_v) { - entry.form = vnw_vc; - entry.c1Offset = -1; - entry.c2Offset = 0; - entry.vOffset = 1; - } else { // prev == vnw_cv - entry.form = vnw_cvc; - entry.c1Offset = prev.c1Offset + 1; - entry.c2Offset = 0; - entry.vOffset = 1; - } - entry.cseq = newCs; - - // reposition tone if needed - int oldIdx = (m_current - 1) - (VSeqList[vs].len - 1) + - getTonePosition(vs, true); - if (m_buffer[oldIdx].tone != 0) { - int newIdx = (m_current - 1) - (VSeqList[newVs].len - 1) + - getTonePosition(newVs, false); - if (newIdx != oldIdx) { - markChange(newIdx); - m_buffer[newIdx].tone = m_buffer[oldIdx].tone; - markChange(oldIdx); - m_buffer[oldIdx].tone = 0; - return 1; - } + if (complexEvent) { + return 1; } - } else { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - } - if (complexEvent) { + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); return 1; - } - - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; - case vnw_c: - case vnw_vc: - case vnw_cvc: - cs = prev.cseq; - if (CSeqList[cs].len == 3) - newCs = cs_nil; - else if (CSeqList[cs].len == 2) - newCs = lookupCSeq(CSeqList[cs].c[0], CSeqList[cs].c[1], lowerSym); - else - newCs = lookupCSeq(CSeqList[cs].c[0], lowerSym); + case vnw_c: + case vnw_vc: + case vnw_cvc: + cs = prev.cseq; + if (CSeqList[cs].len == 3) + newCs = cs_nil; + else if (CSeqList[cs].len == 2) + newCs = lookupCSeq(CSeqList[cs].c[0], CSeqList[cs].c[1], lowerSym); + else + newCs = lookupCSeq(CSeqList[cs].c[0], lowerSym); - if (newCs != cs_nil && (prev.form == vnw_vc || prev.form == vnw_cvc)) { - // Check CVC combination - c1 = cs_nil; - if (prev.c1Offset != -1) - c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; + if (newCs != cs_nil && (prev.form == vnw_vc || prev.form == vnw_cvc)) { + // Check CVC combination + c1 = cs_nil; + if (prev.c1Offset != -1) + c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; - int vIdx = (m_current - 1) - prev.vOffset; - vs = m_buffer[vIdx].vseq; - isValid = isValidCVC(c1, vs, newCs); + int vIdx = (m_current - 1) - prev.vOffset; + vs = m_buffer[vIdx].vseq; + isValid = isValidCVC(c1, vs, newCs); - if (!isValid) - newCs = cs_nil; - } + if (!isValid) + newCs = cs_nil; + } - if (newCs == cs_nil) { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - } else { - if (prev.form == vnw_c) { - entry.form = vnw_c; - entry.c1Offset = 0; - entry.c2Offset = -1; - entry.vOffset = -1; - } else if (prev.form == vnw_vc) { - entry.form = vnw_vc; - entry.c1Offset = -1; - entry.c2Offset = 0; - entry.vOffset = prev.vOffset + 1; - } else { // vnw_cvc - entry.form = vnw_cvc; - entry.c1Offset = prev.c1Offset + 1; - entry.c2Offset = 0; - entry.vOffset = prev.vOffset + 1; + if (newCs == cs_nil) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + } else { + if (prev.form == vnw_c) { + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + } else if (prev.form == vnw_vc) { + entry.form = vnw_vc; + entry.c1Offset = -1; + entry.c2Offset = 0; + entry.vOffset = prev.vOffset + 1; + } else { // vnw_cvc + entry.form = vnw_cvc; + entry.c1Offset = prev.c1Offset + 1; + entry.c2Offset = 0; + entry.vOffset = prev.vOffset + 1; + } + entry.cseq = newCs; } - entry.cseq = newCs; - } - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; } if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) @@ -2272,10 +1579,8 @@ int UkEngine::appendConsonnant(UkKeyEvent &ev) { } //---------------------------------------------------------- -int UkEngine::processEscChar(UkKeyEvent &ev) { - if (m_pCtrl->vietKey && m_current >= 0 && - m_buffer[m_current].form != vnw_empty && - m_buffer[m_current].form != vnw_nonVn) { +int UkEngine::processEscChar(UkKeyEvent& ev) { + if (m_pCtrl->vietKey && m_current >= 0 && m_buffer[m_current].form != vnw_empty && m_buffer[m_current].form != vnw_nonVn) { m_toEscape = true; } return processAppend(ev); @@ -2292,42 +1597,39 @@ void UkEngine::pass(int keyCode) { // This can be called only after other processing have been done. // The new event is supposed to be put into m_buffer already //--------------------------------------------- -int UkEngine::processNoSpellCheck(UkKeyEvent &ev) { - WordInfo &entry = m_buffer[m_current]; +int UkEngine::processNoSpellCheck(UkKeyEvent& ev) { + WordInfo& entry = m_buffer[m_current]; if (IsVnVowel[entry.vnSym]) { - entry.form = vnw_v; - entry.vOffset = 0; - entry.vseq = lookupVSeq(entry.vnSym); + entry.form = vnw_v; + entry.vOffset = 0; + entry.vseq = lookupVSeq(entry.vnSym); entry.c1Offset = entry.c2Offset = -1; } else { - entry.form = vnw_c; + entry.form = vnw_c; entry.c1Offset = 0; entry.c2Offset = -1; - entry.vOffset = -1; - entry.cseq = lookupCSeq(entry.vnSym); + entry.vOffset = -1; + entry.cseq = lookupCSeq(entry.vnSym); } - if (ev.evType == vneNormal && - ((entry.keyCode >= 'a' && entry.keyCode <= 'z') || - (entry.keyCode >= 'A' && entry.keyCode <= 'Z'))) + if (ev.evType == vneNormal && ((entry.keyCode >= 'a' && entry.keyCode <= 'z') || (entry.keyCode >= 'A' && entry.keyCode <= 'Z'))) return 0; markChange(m_current); return 1; } //---------------------------------------------------------- -int UkEngine::process(unsigned int keyCode, int &backs, unsigned char *outBuf, - int &outSize, UkOutputType &outType) { +int UkEngine::process(unsigned int keyCode, int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType) { UkKeyEvent ev; prepareBuffer(); - m_backs = 0; - m_changePos = m_current + 1; - m_pOutBuf = outBuf; - m_pOutSize = &outSize; + m_backs = 0; + m_changePos = m_current + 1; + m_pOutBuf = outBuf; + m_pOutSize = &outSize; m_outputWritten = false; - m_reverted = false; - m_keyRestored = false; - m_keyRestoring = false; - m_outType = UkCharOutput; + m_reverted = false; + m_keyRestored = false; + m_keyRestoring = false; + m_outType = UkCharOutput; m_pCtrl->input.keyCodeToEvent(keyCode, ev); @@ -2336,8 +1638,7 @@ int UkEngine::process(unsigned int keyCode, int &backs, unsigned char *outBuf, ret = (this->*UkKeyProcList[ev.evType])(ev); } else { m_toEscape = false; - if (m_current < 0 || ev.evType == vneNormal || - ev.evType == vneEscChar) { + if (m_current < 0 || ev.evType == vneNormal || ev.evType == vneEscChar) { ret = processAppend(ev); } else { m_current--; @@ -2348,9 +1649,7 @@ int UkEngine::process(unsigned int keyCode, int &backs, unsigned char *outBuf, } } - if (m_pCtrl->vietKey && m_current >= 0 && - m_buffer[m_current].form == vnw_nonVn && ev.chType == ukcVn && - (!m_pCtrl->options.spellCheckEnabled || m_singleMode)) { + if (m_pCtrl->vietKey && m_current >= 0 && m_buffer[m_current].form == vnw_nonVn && ev.chType == ukcVn && (!m_pCtrl->options.spellCheckEnabled || m_singleMode)) { // The spell check has failed, but because we are in non-spellcheck // mode, we consider the new character as the beginning of a new word @@ -2369,12 +1668,12 @@ int UkEngine::process(unsigned int keyCode, int &backs, unsigned char *outBuf, if (m_current >= 0) { ev.chType = m_pCtrl->input.getCharType(ev.keyCode); m_keyCurrent++; - m_keyStrokes[m_keyCurrent].ev = ev; + m_keyStrokes[m_keyCurrent].ev = ev; m_keyStrokes[m_keyCurrent].converted = (ret && !m_keyRestored); } if (ret == 0) { - backs = 0; + backs = 0; outSize = 0; outType = m_outType; return 0; @@ -2389,29 +1688,26 @@ int UkEngine::process(unsigned int keyCode, int &backs, unsigned char *outBuf, return ret; } //---------------------------------------------------------- -void UkEngine::rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, - int &outSize) { - static const std::unordered_map map{ - {vnl_Ar, vneRoof_a}, {vnl_Ab, vneBowl}, {vnl_DD, vneDd}, - {vnl_Er, vneRoof_e}, {vnl_Or, vneRoof_o}, {vnl_Oh, vneHook_o}, - {vnl_Uh, vneHook_u}}; +void UkEngine::rebuildChar(VnLexiName ch, int& backs, unsigned char* outBuf, int& outSize) { + static const std::unordered_map map{{vnl_Ar, vneRoof_a}, {vnl_Ab, vneBowl}, {vnl_DD, vneDd}, {vnl_Er, vneRoof_e}, + {vnl_Or, vneRoof_o}, {vnl_Oh, vneHook_o}, {vnl_Uh, vneHook_u}}; if (ch == vnl_nonVnChar) { return; } prepareBuffer(); - m_backs = 0; + m_backs = 0; m_changePos = m_current + 1; - m_pOutBuf = outBuf; - m_pOutSize = &outSize; + m_pOutBuf = outBuf; + m_pOutSize = &outSize; UkKeyEvent ev; - auto rootChar = StdVnRootChar[ch]; - auto noToneChar = StdVnNoTone[ch]; + auto rootChar = StdVnRootChar[ch]; + auto noToneChar = StdVnNoTone[ch]; - auto keyCode = UnicodeTable[rootChar]; + auto keyCode = UnicodeTable[rootChar]; m_pCtrl->input.keyCodeToEvent(keyCode, ev); // root char @@ -2419,13 +1715,11 @@ void UkEngine::rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, // add root char to key strokes m_keyCurrent++; - m_keyStrokes[m_keyCurrent].ev = ev; + m_keyStrokes[m_keyCurrent].ev = ev; m_keyStrokes[m_keyCurrent].converted = true; // modify vowel - auto it = - map.find(noToneChar % 2 == 0 ? static_cast(noToneChar) - : static_cast(noToneChar - 1)); + auto it = map.find(noToneChar % 2 == 0 ? static_cast(noToneChar) : static_cast(noToneChar - 1)); if (it != map.end()) { ev.evType = it->second; (this->*UkKeyProcList[ev.evType])(ev); @@ -2435,7 +1729,7 @@ void UkEngine::rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, auto tone = (ch - noToneChar) / 2; if (tone >= 1 && tone <= 5) { ev.evType = vneTone0 + tone; - ev.tone = tone; + ev.tone = tone; (this->*UkKeyProcList[ev.evType])(ev); } @@ -2450,12 +1744,12 @@ void UkEngine::rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, // outSize: [in] size of buffer in bytes // [out] bytes written to buffer //---------------------------------------------------------- -int UkEngine::writeOutput(unsigned char *outBuf, int &outSize) { - StdVnChar stdChar; - int i, bytesWritten; - int ret = 1; +int UkEngine::writeOutput(unsigned char* outBuf, int& outSize) { + StdVnChar stdChar; + int i, bytesWritten; + int ret = 1; StringBOStream os(outBuf, outSize); - VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); + VnCharset* pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); pCharset->startOutput(); for (i = m_changePos; i <= m_current; i++) { @@ -2488,14 +1782,13 @@ int UkEngine::getSeqSteps(int first, int last) const { if (last < first) return 0; - if (m_pCtrl->charsetId == CONV_CHARSET_XUTF8 || - m_pCtrl->charsetId == CONV_CHARSET_UNICODE) + if (m_pCtrl->charsetId == CONV_CHARSET_XUTF8 || m_pCtrl->charsetId == CONV_CHARSET_UNICODE) return (last - first + 1); StringBOStream os(0, 0); - int i, bytesWritten; + int i, bytesWritten; - VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); + VnCharset* pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); pCharset->startOutput(); for (i = first; i <= last; i++) { @@ -2540,56 +1833,49 @@ void UkEngine::synchKeyStrokeBuffer() { // in character buffer, we have reached a word break, // so we also need to move key stroke pointer backward to corresponding // word break - while (m_keyCurrent >= 0 && - m_keyStrokes[m_keyCurrent].ev.chType != ukcWordBreak) { + while (m_keyCurrent >= 0 && m_keyStrokes[m_keyCurrent].ev.chType != ukcWordBreak) { m_keyCurrent--; } } } //--------------------------------------------- -int UkEngine::processBackspace(int &backs, unsigned char *outBuf, int &outSize, - UkOutputType &outType) { +int UkEngine::processBackspace(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType) { outType = UkCharOutput; if (!m_pCtrl->vietKey || m_current < 0) { - backs = 0; + backs = 0; outSize = 0; return 0; } - m_backs = 0; + m_backs = 0; m_changePos = m_current + 1; markChange(m_current); - if (m_current == 0 || m_buffer[m_current].form == vnw_empty || - m_buffer[m_current].form == vnw_nonVn || - m_buffer[m_current].form == vnw_c || - m_buffer[m_current - 1].form == vnw_c || - m_buffer[m_current - 1].form == vnw_cvc || - m_buffer[m_current - 1].form == vnw_vc) { + if (m_current == 0 || m_buffer[m_current].form == vnw_empty || m_buffer[m_current].form == vnw_nonVn || m_buffer[m_current].form == vnw_c || + m_buffer[m_current - 1].form == vnw_c || m_buffer[m_current - 1].form == vnw_cvc || m_buffer[m_current - 1].form == vnw_vc) { m_current--; - backs = m_backs; + backs = m_backs; outSize = 0; synchKeyStrokeBuffer(); return (backs > 1); } VowelSeq vs, newVs; - int curTonePos, newTonePos, tone, vStart, vEnd; + int curTonePos, newTonePos, tone, vStart, vEnd; - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - VSeqList[vs].len + 1; - newVs = m_buffer[m_current - 1].vseq; + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - VSeqList[vs].len + 1; + newVs = m_buffer[m_current - 1].vseq; curTonePos = vStart + getTonePosition(vs, vEnd == m_current); newTonePos = vStart + getTonePosition(newVs, true); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; - if (tone == 0 || curTonePos == newTonePos || - (curTonePos == m_current && m_buffer[m_current].tone != 0)) { + if (tone == 0 || curTonePos == newTonePos || (curTonePos == m_current && m_buffer[m_current].tone != 0)) { m_current--; - backs = m_backs; + backs = m_backs; outSize = 0; synchKeyStrokeBuffer(); return (backs > 1); @@ -2608,14 +1894,16 @@ int UkEngine::processBackspace(int &backs, unsigned char *outBuf, int &outSize, //------------------------------------------------ void UkEngine::reset() { - m_current = -1; + m_current = -1; m_keyCurrent = -1; m_singleMode = false; - m_toEscape = false; + m_toEscape = false; } //------------------------------------------------ -void UkEngine::resetKeyBuf() { m_keyCurrent = -1; } +void UkEngine::resetKeyBuf() { + m_keyCurrent = -1; +} //------------------------------------------------ UkEngine::UkEngine() { @@ -2623,16 +1911,16 @@ UkEngine::UkEngine() { engineClassInit(); m_classInit = true; } - m_pCtrl = 0; - m_bufSize = MAX_UK_ENGINE; - m_keyBufSize = MAX_UK_ENGINE; - m_current = -1; - m_keyCurrent = -1; - m_singleMode = false; + m_pCtrl = 0; + m_bufSize = MAX_UK_ENGINE; + m_keyBufSize = MAX_UK_ENGINE; + m_current = -1; + m_keyCurrent = -1; + m_singleMode = false; m_keyCheckFunc = 0; - m_reverted = false; - m_toEscape = false; - m_keyRestored = false; + m_reverted = false; + m_toEscape = false; + m_keyRestored = false; } //---------------------------------------------------- @@ -2644,15 +1932,13 @@ void UkEngine::prepareBuffer() { if (m_current >= 0 && m_current + 10 >= m_bufSize) { // Get rid of at least half of the current entries // don't get rid from the middle of a word. - for (rid = m_current / 2; - m_buffer[rid].form != vnw_empty && rid < m_current; rid++) + for (rid = m_current / 2; m_buffer[rid].form != vnw_empty && rid < m_current; rid++) ; if (rid == m_current) { m_current = -1; } else { rid++; - memmove(m_buffer, m_buffer + rid, - (m_current - rid + 1) * sizeof(WordInfo)); + memmove(m_buffer, m_buffer + rid, (m_current - rid + 1) * sizeof(WordInfo)); m_current -= rid; } } @@ -2661,18 +1947,21 @@ void UkEngine::prepareBuffer() { if (m_keyCurrent > 0 && m_keyCurrent + 1 >= m_keyBufSize) { // Get rid of at least half of the current entries rid = m_keyCurrent / 2; - memmove(m_keyStrokes, m_keyStrokes + rid, - (m_keyCurrent - rid + 1) * sizeof(m_keyStrokes[0])); + memmove(m_keyStrokes, m_keyStrokes + rid, (m_keyCurrent - rid + 1) * sizeof(m_keyStrokes[0])); m_keyCurrent -= rid; } } #define ENTER_CHAR 13 -enum VnCaseType { VnCaseNoChange, VnCaseAllCapital, VnCaseAllSmall }; +enum VnCaseType { + VnCaseNoChange, + VnCaseAllCapital, + VnCaseAllSmall +}; //---------------------------------------------------- -int UkEngine::macroMatch(UkKeyEvent &ev) { - int capsLockOn = 0; +int UkEngine::macroMatch(UkKeyEvent& ev) { + int capsLockOn = 0; int shiftPressed = 0; if (m_keyCheckFunc) m_keyCheckFunc(&shiftPressed, &capsLockOn); @@ -2680,20 +1969,19 @@ int UkEngine::macroMatch(UkKeyEvent &ev) { if (shiftPressed && (ev.keyCode == ' ' || ev.keyCode == ENTER_CHAR)) return 0; - const StdVnChar *pMacText = NULL; - StdVnChar key[MAX_MACRO_KEY_LEN + 1]; - StdVnChar *pKeyStart; + const StdVnChar* pMacText = NULL; + StdVnChar key[MAX_MACRO_KEY_LEN + 1]; + StdVnChar* pKeyStart; // Use static macro text so we can gain a bit of performance // by avoiding memory allocation each time this function is called static StdVnChar macroText[MAX_MACRO_TEXT_LEN + 1]; - int i, j; + int i, j; i = m_current; while (i >= 0 && (m_current - i + 1) < MAX_MACRO_KEY_LEN) { - while (i >= 0 && m_buffer[i].form != vnw_empty && - (m_current - i + 1) < MAX_MACRO_KEY_LEN) + while (i >= 0 && m_buffer[i].form != vnw_empty && (m_current - i + 1) < MAX_MACRO_KEY_LEN) i--; if (i >= 0 && m_buffer[i].form != vnw_empty) return 0; @@ -2773,9 +2061,8 @@ int UkEngine::macroMatch(UkKeyEvent &ev) { // Convert to target output charset int outSize; int maxOutSize = *m_pOutSize; - int inLen = charCount * sizeof(StdVnChar); - VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, (UKBYTE *)macroText, - (UKBYTE *)m_pOutBuf, &inLen, &maxOutSize); + int inLen = charCount * sizeof(StdVnChar); + VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, (UKBYTE*)macroText, (UKBYTE*)m_pOutBuf, &inLen, &maxOutSize); outSize = maxOutSize; // write the last input character @@ -2787,37 +2074,32 @@ int UkEngine::macroMatch(UkKeyEvent &ev) { else vnChar = ev.keyCode; inLen = sizeof(StdVnChar); - VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, - (UKBYTE *)&vnChar, ((UKBYTE *)m_pOutBuf) + outSize, &inLen, - &maxOutSize); + VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, (UKBYTE*)&vnChar, ((UKBYTE*)m_pOutBuf) + outSize, &inLen, &maxOutSize); outSize += maxOutSize; } int backs = m_backs; // store m_backs before calling reset reset(); m_outputWritten = true; - m_backs = backs; - *m_pOutSize = outSize; + m_backs = backs; + *m_pOutSize = outSize; return 1; } //---------------------------------------------------- -int UkEngine::restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, - UkOutputType &outType) { +int UkEngine::restoreKeyStrokes(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType) { outType = UkKeyOutput; if (!lastWordHasVnMark()) { - backs = 0; + backs = 0; outSize = 0; return 0; } - m_backs = 0; + m_backs = 0; m_changePos = m_current + 1; - int keyStart; + int keyStart; bool converted = false; - for (keyStart = m_keyCurrent; - keyStart >= 0 && m_keyStrokes[keyStart].ev.chType != ukcWordBreak; - keyStart--) { + for (keyStart = m_keyCurrent; keyStart >= 0 && m_keyStrokes[keyStart].ev.chType != ukcWordBreak; keyStart--) { if (m_keyStrokes[keyStart].converted) { converted = true; } @@ -2827,7 +2109,7 @@ int UkEngine::restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, if (!converted) { // no key stroke has been converted, so it doesn't make sense to restore // key strokes - backs = 0; + backs = 0; outSize = 0; return 0; } @@ -2838,8 +2120,8 @@ int UkEngine::restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, markChange(m_current + 1); backs = m_backs; - int count; - int i; + int count; + int i; UkKeyEvent ev; m_keyRestoring = true; for (i = keyStart, count = 0; i <= m_keyCurrent; i++) { @@ -2850,19 +2132,21 @@ int UkEngine::restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, m_keyStrokes[i].converted = false; processAppend(ev); } - outSize = count; + outSize = count; m_keyRestoring = false; return 1; } //-------------------------------------------------- -void UkEngine::setSingleMode() { m_singleMode = true; } +void UkEngine::setSingleMode() { + m_singleMode = true; +} //-------------------------------------------------- static void SetupUnikeyEngineOnce() { SetupInputClassifierTable(); - int i; + int i; VnLexiName lexi; // Calculate IsoStdVnCharMap @@ -2871,8 +2155,7 @@ static void SetupUnikeyEngineOnce() { } for (i = 0; SpecialWesternChars[i]; i++) { - IsoStdVnCharMap[SpecialWesternChars[i]] = - (vnl_lastChar + i) + VnStdCharOffset; + IsoStdVnCharMap[SpecialWesternChars[i]] = (vnl_lastChar + i) + VnStdCharOffset; } for (i = 0; i < 256; i++) { @@ -2884,7 +2167,9 @@ static void SetupUnikeyEngineOnce() { std::once_flag setupFlag; -void SetupUnikeyEngine() { std::call_once(setupFlag, SetupUnikeyEngineOnce); } +void SetupUnikeyEngine() { + std::call_once(setupFlag, SetupUnikeyEngineOnce); +} //-------------------------------------------------- bool UkEngine::atWordBeginning() const { @@ -2896,22 +2181,21 @@ bool UkEngine::atWordBeginning() const { // Spell-check, if is valid Vietnamese, return normally, if not: // restore key strokes if auto-restore is enabled //-------------------------------------------------- -int UkEngine::processWordEnd(UkKeyEvent &ev) { +int UkEngine::processWordEnd(UkKeyEvent& ev) { if (m_pCtrl->options.macroEnabled && macroMatch(ev)) return 1; - auto putKeyInBuffer = [this](UkKeyEvent &ev) { + auto putKeyInBuffer = [this](UkKeyEvent& ev) { m_current++; - WordInfo &entry = m_buffer[m_current]; - entry.form = vnw_empty; + WordInfo& entry = m_buffer[m_current]; + entry.form = vnw_empty; entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - entry.keyCode = ev.keyCode; - entry.vnSym = vnToLower(ev.vnSym); - entry.caps = (entry.vnSym != ev.vnSym); + entry.keyCode = ev.keyCode; + entry.vnSym = vnToLower(ev.vnSym); + entry.caps = (entry.vnSym != ev.vnSym); }; - if (!m_pCtrl->options.spellCheckEnabled || m_singleMode || m_current < 0 || - m_keyRestoring) { + if (!m_pCtrl->options.spellCheckEnabled || m_singleMode || m_current < 0 || m_keyRestoring) { putKeyInBuffer(ev); return 0; } @@ -2920,7 +2204,7 @@ int UkEngine::processWordEnd(UkKeyEvent &ev) { if (m_pCtrl->options.autoNonVnRestore && lastWordIsNonVn()) { outSize = *m_pOutSize; if (restoreKeyStrokes(m_backs, m_pOutBuf, outSize, m_outType)) { - m_keyRestored = true; + m_keyRestored = true; m_outputWritten = true; } } @@ -2948,38 +2232,33 @@ bool UkEngine::lastWordIsNonVn() const { return false; switch (m_buffer[m_current].form) { - case vnw_nonVn: - return true; - case vnw_empty: - case vnw_c: - return false; - case vnw_v: - case vnw_cv: - return !VSeqList[m_buffer[m_current].vseq].complete; - case vnw_vc: - case vnw_cvc: { - int vIndex = m_current - m_buffer[m_current].vOffset; - VowelSeq vs = m_buffer[vIndex].vseq; - if (!VSeqList[vs].complete) - return true; - ConSeq cs = m_buffer[m_current].cseq; - ConSeq c1 = cs_nil; - if (m_buffer[m_current].c1Offset != -1) - c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; + case vnw_nonVn: return true; + case vnw_empty: + case vnw_c: return false; + case vnw_v: + case vnw_cv: return !VSeqList[m_buffer[m_current].vseq].complete; + case vnw_vc: + case vnw_cvc: { + int vIndex = m_current - m_buffer[m_current].vOffset; + VowelSeq vs = m_buffer[vIndex].vseq; + if (!VSeqList[vs].complete) + return true; + ConSeq cs = m_buffer[m_current].cseq; + ConSeq c1 = cs_nil; + if (m_buffer[m_current].c1Offset != -1) + c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; - if (!isValidCVC(c1, vs, cs)) { - return true; - } + if (!isValidCVC(c1, vs, cs)) { + return true; + } - int tonePos = - (vIndex - VSeqList[vs].len + 1) + getTonePosition(vs, false); - int tone = m_buffer[tonePos].tone; - if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && - (tone == 2 || tone == 3 || tone == 4)) { - return true; + int tonePos = (vIndex - VSeqList[vs].len + 1) + getTonePosition(vs, false); + int tone = m_buffer[tonePos].tone; + if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && (tone == 2 || tone == 3 || tone == 4)) { + return true; + } } } - } return false; } diff --git a/unikey/core/ukengine.h b/unikey/core/ukengine.h index c79a1304..d218e5ce 100644 --- a/unikey/core/ukengine.h +++ b/unikey/core/ukengine.h @@ -16,33 +16,42 @@ // This is a shared object among processes, do not put any pointer in it struct UkSharedMem { // states - bool vietKey; + bool vietKey; - UnikeyOptions options; + UnikeyOptions options; UkInputProcessor input; - bool usrKeyMapLoaded; - int usrKeyMap[256]; - int charsetId; + bool usrKeyMapLoaded; + int usrKeyMap[256]; + int charsetId; - CMacroTable macStore; + CMacroTable macStore; }; #define MAX_UK_ENGINE 128 -enum VnWordForm { vnw_nonVn, vnw_empty, vnw_c, vnw_v, vnw_cv, vnw_vc, vnw_cvc }; +enum VnWordForm { + vnw_nonVn, + vnw_empty, + vnw_c, + vnw_v, + vnw_cv, + vnw_vc, + vnw_cvc +}; -typedef std::function - CheckKeyboardCaseCb; +typedef std::function CheckKeyboardCaseCb; struct KeyBufEntry { UkKeyEvent ev; - bool converted; + bool converted; }; class UkEngine { -public: + public: UkEngine(); - void setCtrlInfo(UkSharedMem *p) { m_pCtrl = p; } + void setCtrlInfo(UkSharedMem* p) { + m_pCtrl = p; + } void setCheckKbCaseFunc(CheckKeyboardCaseCb pFunc) { m_keyCheckFunc = pFunc; @@ -50,69 +59,65 @@ class UkEngine { bool atWordBeginning() const; - int process(unsigned int keyCode, int &backs, unsigned char *outBuf, - int &outSize, UkOutputType &outType); + int process(unsigned int keyCode, int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType); // just pass through without filtering void pass(int keyCode); // rebuild preedit from surrounding char - void rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, - int &outSize); + void rebuildChar(VnLexiName ch, int& backs, unsigned char* outBuf, int& outSize); void setSingleMode(); - int processBackspace(int &backs, unsigned char *outBuf, int &outSize, - UkOutputType &outType); + int processBackspace(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType); void reset(); - int restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, - UkOutputType &outType); + int restoreKeyStrokes(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType); // following methods must be public just to enable the use of pointers to // them they should not be called from outside. - int processTone(UkKeyEvent &ev); - int processRoof(UkKeyEvent &ev); - int processHook(UkKeyEvent &ev); - int processAppend(UkKeyEvent &ev); - int appendVowel(UkKeyEvent &ev); - int appendConsonnant(UkKeyEvent &ev); - int processDd(UkKeyEvent &ev); - int processMapChar(UkKeyEvent &ev); - int processTelexW(UkKeyEvent &ev); - int processEscChar(UkKeyEvent &ev); - -protected: - static bool m_classInit; + int processTone(UkKeyEvent& ev); + int processRoof(UkKeyEvent& ev); + int processHook(UkKeyEvent& ev); + int processAppend(UkKeyEvent& ev); + int appendVowel(UkKeyEvent& ev); + int appendConsonnant(UkKeyEvent& ev); + int processDd(UkKeyEvent& ev); + int processMapChar(UkKeyEvent& ev); + int processTelexW(UkKeyEvent& ev); + int processEscChar(UkKeyEvent& ev); + + protected: + static bool m_classInit; CheckKeyboardCaseCb m_keyCheckFunc; - UkSharedMem *m_pCtrl; + UkSharedMem* m_pCtrl; - int m_changePos; - int m_backs; - int m_bufSize; - int m_current; - int m_singleMode; + int m_changePos; + int m_backs; + int m_bufSize; + int m_current; + int m_singleMode; - int m_keyBufSize; + int m_keyBufSize; // unsigned int m_keyStrokes[MAX_UK_ENGINE]; KeyBufEntry m_keyStrokes[MAX_UK_ENGINE]; - int m_keyCurrent; - bool m_toEscape; + int m_keyCurrent; + bool m_toEscape; // variables valid in one session - unsigned char *m_pOutBuf; - int *m_pOutSize; - bool m_outputWritten; - bool m_reverted; - bool m_keyRestored; - bool m_keyRestoring; - UkOutputType m_outType; + unsigned char* m_pOutBuf; + int* m_pOutSize; + bool m_outputWritten; + bool m_reverted; + bool m_keyRestored; + bool m_keyRestoring; + UkOutputType m_outType; struct WordInfo { // info for word ending at this position VnWordForm form; - int c1Offset, vOffset, c2Offset; + int c1Offset, vOffset, c2Offset; union { VowelSeq vseq; - ConSeq cseq; + ConSeq cseq; }; // info for current symbol @@ -120,23 +125,23 @@ class UkEngine { // canonical symbol, after caps, tone are removed // for non-Vn, vnSym == -1 VnLexiName vnSym; - int keyCode; + int keyCode; }; WordInfo m_buffer[MAX_UK_ENGINE]; - int processHookWithUO(UkKeyEvent &ev); - int macroMatch(UkKeyEvent &ev); - void markChange(int pos); - void prepareBuffer(); // make sure we have a least 10 entries available - int writeOutput(unsigned char *outBuf, int &outSize); + int processHookWithUO(UkKeyEvent& ev); + int macroMatch(UkKeyEvent& ev); + void markChange(int pos); + void prepareBuffer(); // make sure we have a least 10 entries available + int writeOutput(unsigned char* outBuf, int& outSize); // int getSeqLength(int first, int last); - int getSeqSteps(int first, int last) const; - int getTonePosition(VowelSeq vs, bool terminated) const; + int getSeqSteps(int first, int last) const; + int getTonePosition(VowelSeq vs, bool terminated) const; void resetKeyBuf(); - int checkEscapeVIQR(UkKeyEvent &ev); - int processNoSpellCheck(UkKeyEvent &ev); - int processWordEnd(UkKeyEvent &ev); + int checkEscapeVIQR(UkKeyEvent& ev); + int processNoSpellCheck(UkKeyEvent& ev); + int processWordEnd(UkKeyEvent& ev); void synchKeyStrokeBuffer(); bool lastWordHasVnMark() const; bool lastWordIsNonVn() const; diff --git a/unikey/core/unikeyinputcontext.cpp b/unikey/core/unikeyinputcontext.cpp index 2043bf17..894bee10 100644 --- a/unikey/core/unikeyinputcontext.cpp +++ b/unikey/core/unikeyinputcontext.cpp @@ -15,22 +15,21 @@ using namespace std; //-------------------------------------------- -void CreateDefaultUnikeyOptions(UnikeyOptions *pOpt) { - pOpt->freeMarking = 1; - pOpt->modernStyle = 0; - pOpt->macroEnabled = 0; +void CreateDefaultUnikeyOptions(UnikeyOptions* pOpt) { + pOpt->freeMarking = 1; + pOpt->modernStyle = 0; + pOpt->macroEnabled = 0; pOpt->useUnicodeClipboard = 0; - pOpt->alwaysMacro = 0; - pOpt->spellCheckEnabled = 1; - pOpt->autoNonVnRestore = 0; + pOpt->alwaysMacro = 0; + pOpt->spellCheckEnabled = 1; + pOpt->autoNonVnRestore = 0; } -UnikeyInputMethod::UnikeyInputMethod() - : sharedMem_(std::make_unique()) { +UnikeyInputMethod::UnikeyInputMethod() : sharedMem_(std::make_unique()) { SetupUnikeyEngine(); sharedMem_->input.init(); sharedMem_->macStore.init(); - sharedMem_->vietKey = true; + sharedMem_->vietKey = true; sharedMem_->usrKeyMapLoaded = false; setInputMethod(UkTelex); setOutputCharset(CONV_CHARSET_XUTF8); @@ -39,8 +38,7 @@ UnikeyInputMethod::UnikeyInputMethod() //-------------------------------------------- void UnikeyInputMethod::setInputMethod(UkInputMethod im) { - if (im == UkTelex || im == UkVni || im == UkSimpleTelex || - im == UkSimpleTelex2 || im == UkViqr || im == UkMsVi) { + if (im == UkTelex || im == UkVni || im == UkSimpleTelex || im == UkSimpleTelex2 || im == UkViqr || im == UkMsVi) { sharedMem_->input.setIM(im); } else if (im == UkUsrIM && sharedMem_->usrKeyMapLoaded) { // cout << "Switched to user mode\n"; //DEBUG @@ -56,32 +54,31 @@ void UnikeyInputMethod::setOutputCharset(int charset) { } //-------------------------------------------- -void UnikeyInputMethod::setOptions(UnikeyOptions *pOpt) { - sharedMem_->options.freeMarking = pOpt->freeMarking; - sharedMem_->options.modernStyle = pOpt->modernStyle; - sharedMem_->options.macroEnabled = pOpt->macroEnabled; +void UnikeyInputMethod::setOptions(UnikeyOptions* pOpt) { + sharedMem_->options.freeMarking = pOpt->freeMarking; + sharedMem_->options.modernStyle = pOpt->modernStyle; + sharedMem_->options.macroEnabled = pOpt->macroEnabled; sharedMem_->options.useUnicodeClipboard = pOpt->useUnicodeClipboard; - sharedMem_->options.alwaysMacro = pOpt->alwaysMacro; - sharedMem_->options.spellCheckEnabled = pOpt->spellCheckEnabled; - sharedMem_->options.autoNonVnRestore = pOpt->autoNonVnRestore; + sharedMem_->options.alwaysMacro = pOpt->alwaysMacro; + sharedMem_->options.spellCheckEnabled = pOpt->spellCheckEnabled; + sharedMem_->options.autoNonVnRestore = pOpt->autoNonVnRestore; } //-------------------------------------------- void UnikeyInputContext::setCapsState(int shiftPressed, int CapsLockOn) { // UnikeyCapsAll = (shiftPressed && !CapsLockOn) || (!shiftPressed && // CapsLockOn); - capsLockOn_ = CapsLockOn; + capsLockOn_ = CapsLockOn; shiftPressed_ = shiftPressed; } //-------------------------------------------- -UnikeyInputContext::UnikeyInputContext(UnikeyInputMethod *im) { - conn_ = - im->connect([this]() { engine_.reset(); }); +UnikeyInputContext::UnikeyInputContext(UnikeyInputMethod* im) { + conn_ = im->connect([this]() { engine_.reset(); }); engine_.setCtrlInfo(im->sharedMem()); - engine_.setCheckKbCaseFunc([this](int *pShiftPressed, int *pCapsLockOn) { + engine_.setCheckKbCaseFunc([this](int* pShiftPressed, int* pCapsLockOn) { *pShiftPressed = shiftPressed_; - *pCapsLockOn = capsLockOn_; + *pCapsLockOn = capsLockOn_; }); } @@ -97,7 +94,7 @@ void UnikeyInputContext::filter(unsigned int ch) { //-------------------------------------------- void UnikeyInputContext::putChar(unsigned int ch) { engine_.pass(ch); - bufChars_ = 0; + bufChars_ = 0; backspaces_ = 0; } @@ -108,7 +105,9 @@ void UnikeyInputContext::rebuildChar(VnLexiName ch) { } //-------------------------------------------- -void UnikeyInputContext::resetBuf() { engine_.reset(); } +void UnikeyInputContext::resetBuf() { + engine_.reset(); +} //-------------------------------------------- void UnikeyInputContext::backspacePress() { diff --git a/unikey/core/unikeyinputcontext.h b/unikey/core/unikeyinputcontext.h index bb9f02da..1416f31e 100644 --- a/unikey/core/unikeyinputcontext.h +++ b/unikey/core/unikeyinputcontext.h @@ -13,7 +13,7 @@ #include class UnikeyInputMethod : public fcitx::ConnectableObject { -public: + public: UnikeyInputMethod(); // set input method @@ -23,25 +23,27 @@ class UnikeyInputMethod : public fcitx::ConnectableObject { void setOutputCharset(int charset); // set extra options - void setOptions(UnikeyOptions *pOpt); + void setOptions(UnikeyOptions* pOpt); //-------------------------------------------- - int loadMacroTable(const char *fileName) { + int loadMacroTable(const char* fileName) { return sharedMem_->macStore.loadFromFile(fileName); } - UkSharedMem *sharedMem() { return sharedMem_.get(); } + UkSharedMem* sharedMem() { + return sharedMem_.get(); + } FCITX_DECLARE_SIGNAL(UnikeyInputMethod, Reset, void()); -private: + private: FCITX_DEFINE_SIGNAL(UnikeyInputMethod, Reset); std::unique_ptr sharedMem_; }; class UnikeyInputContext { -public: - UnikeyInputContext(UnikeyInputMethod *im); + public: + UnikeyInputContext(UnikeyInputMethod* im); ~UnikeyInputContext(); // call this to reset Unikey's state when focus, context is changed or @@ -67,21 +69,27 @@ class UnikeyInputContext { bool isAtWordBeginning() const; - int backspaces() const { return backspaces_; } - int bufChars() const { return bufChars_; } - const unsigned char *buf() const { return buf_; } + int backspaces() const { + return backspaces_; + } + int bufChars() const { + return bufChars_; + } + const unsigned char* buf() const { + return buf_; + } -private: + private: fcitx::ScopedConnection conn_; - unsigned char buf_[1024]; - int backspaces_ = 0; - int bufChars_; - UkOutputType output_; - UkEngine engine_; + unsigned char buf_[1024]; + int backspaces_ = 0; + int bufChars_; + UkOutputType output_; + UkEngine engine_; - int capsLockOn_ = 0; - int shiftPressed_ = 0; + int capsLockOn_ = 0; + int shiftPressed_ = 0; }; #endif // _UNIKEY_UNIKEYINPUTCONTEXT_H_ diff --git a/unikey/core/usrkeymap.cpp b/unikey/core/usrkeymap.cpp index 557f753d..0dd8bb48 100644 --- a/unikey/core/usrkeymap.cpp +++ b/unikey/core/usrkeymap.cpp @@ -18,63 +18,50 @@ namespace { -constexpr char OPT_COMMENT_CHAR = ';'; + constexpr char OPT_COMMENT_CHAR = ';'; -struct UkEventLabelPair { - char label[32]; - int ev; -}; + struct UkEventLabelPair { + char label[32]; + int ev; + }; -const char *UkKeyMapHeader = "; This is UniKey user-defined key mapping file, " - "generated from UniKey (Fcitx 5)\n\n"; + const char* UkKeyMapHeader = "; This is UniKey user-defined key mapping file, " + "generated from UniKey (Fcitx 5)\n\n"; -constexpr UkKeyEvName lexi(VnLexiName v) { - return static_cast( - static_cast(vneCount) + static_cast(v)); -} + constexpr UkKeyEvName lexi(VnLexiName v) { + return static_cast(static_cast(vneCount) + static_cast(v)); + } -constexpr UkEventLabelPair UkEvLabelList[] = { - {"Tone0", vneTone0}, {"Tone1", vneTone1}, - {"Tone2", vneTone2}, {"Tone3", vneTone3}, - {"Tone4", vneTone4}, {"Tone5", vneTone5}, - {"Roof-All", vneRoofAll}, {"Roof-A", vneRoof_a}, - {"Roof-E", vneRoof_e}, {"Roof-O", vneRoof_o}, - {"Hook-Bowl", vneHookAll}, {"Hook-UO", vneHook_uo}, - {"Hook-U", vneHook_u}, {"Hook-O", vneHook_o}, - {"Bowl", vneBowl}, {"D-Mark", vneDd}, - {"Telex-W", vne_telex_w}, {"Escape", vneEscChar}, - - {"DD", lexi(vnl_DD)}, {"dd", lexi(vnl_dd)}, - {"A^", lexi(vnl_Ar)}, {"a^", lexi(vnl_ar)}, - {"A(", lexi(vnl_Ab)}, {"a(", lexi(vnl_ab)}, - {"E^", lexi(vnl_Er)}, {"e^", lexi(vnl_er)}, - {"O^", lexi(vnl_Or)}, {"o^", lexi(vnl_or)}, - {"O+", lexi(vnl_Oh)}, {"o+", lexi(vnl_oh)}, - {"U+", lexi(vnl_Uh)}, {"u+", lexi(vnl_uh)}, -}; - -constexpr auto UkEvLabelCount = FCITX_ARRAY_SIZE(UkEvLabelList); - -//------------------------------------------- -void initKeyMap(int keyMap[256]) { - unsigned int c; - for (c = 0; c < 256; c++) - keyMap[c] = vneNormal; -} + constexpr UkEventLabelPair UkEvLabelList[] = { + {"Tone0", vneTone0}, {"Tone1", vneTone1}, {"Tone2", vneTone2}, {"Tone3", vneTone3}, {"Tone4", vneTone4}, {"Tone5", vneTone5}, {"Roof-All", vneRoofAll}, + {"Roof-A", vneRoof_a}, {"Roof-E", vneRoof_e}, {"Roof-O", vneRoof_o}, {"Hook-Bowl", vneHookAll}, {"Hook-UO", vneHook_uo}, {"Hook-U", vneHook_u}, {"Hook-O", vneHook_o}, + {"Bowl", vneBowl}, {"D-Mark", vneDd}, {"Telex-W", vne_telex_w}, {"Escape", vneEscChar}, + + {"DD", lexi(vnl_DD)}, {"dd", lexi(vnl_dd)}, {"A^", lexi(vnl_Ar)}, {"a^", lexi(vnl_ar)}, {"A(", lexi(vnl_Ab)}, {"a(", lexi(vnl_ab)}, {"E^", lexi(vnl_Er)}, + {"e^", lexi(vnl_er)}, {"O^", lexi(vnl_Or)}, {"o^", lexi(vnl_or)}, {"O+", lexi(vnl_Oh)}, {"o+", lexi(vnl_oh)}, {"U+", lexi(vnl_Uh)}, {"u+", lexi(vnl_uh)}, + }; + + constexpr auto UkEvLabelCount = FCITX_ARRAY_SIZE(UkEvLabelList); -int getLabelIndex(int event) { - for (size_t i = 0; i < UkEvLabelCount; i++) { - if (UkEvLabelList[i].ev == event) - return i; + //------------------------------------------- + void initKeyMap(int keyMap[256]) { + unsigned int c; + for (c = 0; c < 256; c++) + keyMap[c] = vneNormal; + } + + int getLabelIndex(int event) { + for (size_t i = 0; i < UkEvLabelCount; i++) { + if (UkEvLabelList[i].ev == event) + return i; + } + return -1; } - return -1; -} } // namespace //-------------------------------------------------- -static bool parseNameValue(std::string_view line, std::string_view *name, - std::string_view *value) { +static bool parseNameValue(std::string_view line, std::string_view* name, std::string_view* value) { if (line.empty()) { return false; } @@ -98,7 +85,7 @@ static bool parseNameValue(std::string_view line, std::string_view *name, return false; } - *name = k; + *name = k; *value = v; return true; } @@ -107,7 +94,7 @@ static bool parseNameValue(std::string_view line, std::string_view *name, DllExport void UkLoadKeyMap(int fd, int keyMap[256]) { std::vector orderMap = UkLoadKeyOrderMap(fd); initKeyMap(keyMap); - for (const auto &item : orderMap) { + for (const auto& item : orderMap) { keyMap[item.key] = item.action; if (item.action < vneCount) { keyMap[tolower(item.key)] = item.action; @@ -118,14 +105,14 @@ DllExport void UkLoadKeyMap(int fd, int keyMap[256]) { //------------------------------------------------------------------ DllExport std::vector UkLoadKeyOrderMap(int fd) { size_t lineCount = 0; - int keyMap[256]; + int keyMap[256]; initKeyMap(keyMap); std::vector pMap; - fcitx::IFDStreamBuf buf(fd); - std::istream in(&buf); - std::string line; + fcitx::IFDStreamBuf buf(fd); + std::istream in(&buf); + std::string line; while (std::getline(in, line)) { lineCount++; auto text = fcitx::stringutils::trimView(line); @@ -135,8 +122,7 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { std::string_view name, value; if (parseNameValue(text, &name, &value)) { if (name.size() != 1) { - FCITX_ERROR() << "Error in user key layout, line " << lineCount - << ": key name is not a single character"; + FCITX_ERROR() << "Error in user key layout, line " << lineCount << ": key name is not a single character"; continue; } size_t i = 0; @@ -146,8 +132,7 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { } } if (i == UkEvLabelCount) { - FCITX_ERROR() << "Error in user key layout, line " << lineCount - << ": command not found"; + FCITX_ERROR() << "Error in user key layout, line " << lineCount << ": command not found"; continue; } @@ -162,7 +147,7 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { UkKeyMapping newPair; newPair.action = UkEvLabelList[i].ev; if (keyMap[c] < vneCount) { - newPair.key = toupper(c); + newPair.key = toupper(c); keyMap[toupper(c)] = UkEvLabelList[i].ev; } else { newPair.key = c; @@ -173,12 +158,11 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { return pMap; } -DllExport void UkStoreKeyOrderMap(FILE *f, - const std::vector &pMap) { +DllExport void UkStoreKeyOrderMap(FILE* f, const std::vector& pMap) { int labelIndex; fputs(UkKeyMapHeader, f); - for (const auto &item : pMap) { + for (const auto& item : pMap) { labelIndex = getLabelIndex(item.action); if (labelIndex != -1) { fprintf(f, "%c = %s\n", item.key, UkEvLabelList[labelIndex].label); diff --git a/unikey/core/usrkeymap.h b/unikey/core/usrkeymap.h index 76098527..7073aa67 100644 --- a/unikey/core/usrkeymap.h +++ b/unikey/core/usrkeymap.h @@ -13,7 +13,6 @@ DllInterface void UkLoadKeyMap(int fd, int keyMap[256]); DllInterface std::vector UkLoadKeyOrderMap(int fd); -DllInterface void UkStoreKeyOrderMap(FILE *f, - const std::vector &pMap); +DllInterface void UkStoreKeyOrderMap(FILE* f, const std::vector& pMap); #endif diff --git a/unikey/core/vnconv.h b/unikey/core/vnconv.h index f51a98a5..e3030ab6 100644 --- a/unikey/core/vnconv.h +++ b/unikey/core/vnconv.h @@ -21,57 +21,51 @@ #define DllImport #endif -#define CONV_CHARSET_UNICODE 0 -#define CONV_CHARSET_UNIUTF8 1 -#define CONV_CHARSET_UNIREF 2 //&#D; -#define CONV_CHARSET_UNIREF_HEX 3 +#define CONV_CHARSET_UNICODE 0 +#define CONV_CHARSET_UNIUTF8 1 +#define CONV_CHARSET_UNIREF 2 //&#D; +#define CONV_CHARSET_UNIREF_HEX 3 #define CONV_CHARSET_UNIDECOMPOSED 4 -#define CONV_CHARSET_WINCP1258 5 -#define CONV_CHARSET_UNI_CSTRING 6 -#define CONV_CHARSET_VNSTANDARD 7 +#define CONV_CHARSET_WINCP1258 5 +#define CONV_CHARSET_UNI_CSTRING 6 +#define CONV_CHARSET_VNSTANDARD 7 -#define CONV_CHARSET_VIQR 10 +#define CONV_CHARSET_VIQR 10 #define CONV_CHARSET_UTF8VIQR 11 -#define CONV_CHARSET_XUTF8 12 +#define CONV_CHARSET_XUTF8 12 -#define CONV_CHARSET_TCVN3 20 -#define CONV_CHARSET_VPS 21 -#define CONV_CHARSET_VISCII 22 -#define CONV_CHARSET_BKHCM1 23 +#define CONV_CHARSET_TCVN3 20 +#define CONV_CHARSET_VPS 21 +#define CONV_CHARSET_VISCII 22 +#define CONV_CHARSET_BKHCM1 23 #define CONV_CHARSET_VIETWAREF 24 -#define CONV_CHARSET_ISC 25 +#define CONV_CHARSET_ISC 25 -#define CONV_CHARSET_VNIWIN 40 -#define CONV_CHARSET_BKHCM2 41 +#define CONV_CHARSET_VNIWIN 40 +#define CONV_CHARSET_BKHCM2 41 #define CONV_CHARSET_VIETWAREX 42 -#define CONV_CHARSET_VNIMAC 43 +#define CONV_CHARSET_VNIMAC 43 #define CONV_TOTAL_SINGLE_CHARSETS 6 #define CONV_TOTAL_DOUBLE_CHARSETS 4 -#define IS_SINGLE_BYTE_CHARSET(x) \ - (x >= CONV_CHARSET_TCVN3 && \ - x < CONV_CHARSET_TCVN3 + CONV_TOTAL_SINGLE_CHARSETS) -#define IS_DOUBLE_BYTE_CHARSET(x) \ - (x >= CONV_CHARSET_VNIWIN && \ - x < CONV_CHARSET_VNIWIN + CONV_TOTAL_DOUBLE_CHARSETS) +#define IS_SINGLE_BYTE_CHARSET(x) (x >= CONV_CHARSET_TCVN3 && x < CONV_CHARSET_TCVN3 + CONV_TOTAL_SINGLE_CHARSETS) +#define IS_DOUBLE_BYTE_CHARSET(x) (x >= CONV_CHARSET_VNIWIN && x < CONV_CHARSET_VNIWIN + CONV_TOTAL_DOUBLE_CHARSETS) typedef unsigned char UKBYTE; #if defined(__cplusplus) extern "C" { #endif -DllInterface int VnConvert(int inCharset, int outCharset, UKBYTE *input, - UKBYTE *output, int *pInLen, int *pMaxOutLen); +DllInterface int VnConvert(int inCharset, int outCharset, UKBYTE* input, UKBYTE* output, int* pInLen, int* pMaxOutLen); -DllInterface int VnFileConvert(int inCharset, int outCharset, - const char *inFile, const char *outFile); +DllInterface int VnFileConvert(int inCharset, int outCharset, const char* inFile, const char* outFile); #if defined(__cplusplus) } #endif -DllInterface const char *VnConvErrMsg(int errCode); +DllInterface const char* VnConvErrMsg(int errCode); enum VnConvError { VNCONV_NO_ERROR, @@ -87,8 +81,8 @@ enum VnConvError { typedef struct _CharsetNameId CharsetNameId; struct _CharsetNameId { - const char *name; - int id; + const char* name; + int id; }; typedef struct _VnConvOptions VnConvOptions; @@ -102,8 +96,8 @@ struct _VnConvOptions { int smartViqr; }; -DllInterface void VnConvSetOptions(VnConvOptions *pOptions); -DllInterface void VnConvGetOptions(VnConvOptions *pOptions); -DllInterface void VnConvResetOptions(VnConvOptions *pOptions); +DllInterface void VnConvSetOptions(VnConvOptions* pOptions); +DllInterface void VnConvGetOptions(VnConvOptions* pOptions); +DllInterface void VnConvResetOptions(VnConvOptions* pOptions); #endif From d9e7c6639859db918dd7d000c7232c402aee7184 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 8 May 2026 03:41:32 +0700 Subject: [PATCH 16/42] rm stuff Signed-off-by: Zebra2711 --- src/lotus-state.cpp | 96 --------------------------------------------- src/lotus-state.h | 9 ----- 2 files changed, 105 deletions(-) diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index db9ebc41..4794d901 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -1174,100 +1174,4 @@ namespace fcitx { bool LotusState::isX11() const { return false; //cat /proc//maps | grep -E 'libX11|libxcb' } - /* - void LotusState::replayBufferedKeys() { - LOTUS_INFO("Starting replay buffered keys"); - if (buffered_keys_.empty()) { - return; - } - auto keys = std::move(buffered_keys_); - for (size_t i = 0; i < keys.size(); ++i) { - auto sym = static_cast(keys[i].sym); - uint32_t state = keys[i].state; - std::string keyUtf8 = Key::keySymToUTF8(sym); - if (keyUtf8.empty()) { - continue; - } - - bool processed = inputBackend_->processKeyEvent(sym, state); - - std::string commitPull; - inputBackend_->pullCommit(&commitPull); - UniqueCPtr commitF(commitPull.empty() ? nullptr : strdup(commitPull.c_str())); - if (commitF && (*commitF.get() != 0)) { - std::string commitStr = commitF.get(); - std::string commonPrefix; - std::string deletedPart; - std::string addedPart; - compareAndSplitStrings(oldPreBuffer_, commitStr, commonPrefix, deletedPart, addedPart); - - if (!deletedPart.empty()) { - // Re-buffer remaining keys for next replay cycle. - for (size_t j = i + 1; j < keys.size(); ++j) { - if (buffered_keys_.size() < MAX_BUFFERED_KEYS) { - buffered_keys_.push_back(keys[j]); - } - } - performReplacement(deletedPart, addedPart); - hasHistory_ = false; - inputBackend_->resetEngine(); - oldPreBuffer_.clear(); - return; - } - if (!addedPart.empty()) { - ic_->commitString(addedPart); - } - - hasHistory_ = false; - inputBackend_->resetEngine(); - oldPreBuffer_.clear(); - continue; - } - - if (!processed) { - ic_->commitString(keyUtf8); - continue; - } - - hasHistory_ = true; - realtextLen.fetch_add(1, std::memory_order_acq_rel); - - std::string preeditStr; - inputBackend_->pullPreedit(&preeditStr); - - std::string commonPrefix; - std::string deletedPart; - std::string addedPart; - if (compareAndSplitStrings(oldPreBuffer_, preeditStr, commonPrefix, deletedPart, addedPart) != 0) { - if (deletedPart.empty()) { - if (!addedPart.empty()) { - ic_->commitString(addedPart); - oldPreBuffer_ = preeditStr; - } - } else { - if (uinput_client_fd_ < 0) { - ic_->commitString(keyUtf8); - continue; - } - - if (is_deleting_.load()) { - is_deleting_.store(false, std::memory_order_release); - } - - // Re-buffer remaining keys for next replay cycle. - for (size_t j = i + 1; j < keys.size(); ++j) { - if (buffered_keys_.size() < MAX_BUFFERED_KEYS) { - buffered_keys_.push_back(keys[j]); - } - } - performReplacement(deletedPart, addedPart); - - oldPreBuffer_ = preeditStr; - return; - } - } - } - LOTUS_INFO("Replay buffered keys done"); - } -*/ } // namespace fcitx diff --git a/src/lotus-state.h b/src/lotus-state.h index 33c10f0c..a911beb6 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -214,15 +214,6 @@ namespace fcitx { * @param currentSym Current key symbol. */ void processNormalKey(KeyEvent& keyEvent, KeySym currentSym); - - /** - * @brief Replays keystrokes buffered during replacement. - * - * When is_deleting_ is true, non-special keystrokes are buffered - * instead of being discarded. This method replays them after the - * replacement completes. - */ - void replayBufferedKeys(); }; } // namespace fcitx From ce9d874e111f0834d9884a6aa2a7fcf8db5f6b6e Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 8 May 2026 06:32:17 +0700 Subject: [PATCH 17/42] ack Signed-off-by: Zebra2711 --- server/lotus-server.cpp | 4 ++++ src/lotus-engine.cpp | 8 +++++++- src/lotus-state.cpp | 22 +++++++++++++++++----- src/lotus-unikey-backend.cpp | 35 +++++++++++++++++++++++++++++++---- unikey/core/usrkeymap.cpp | 2 +- 5 files changed, 60 insertions(+), 11 deletions(-) diff --git a/server/lotus-server.cpp b/server/lotus-server.cpp index 2f75fd7a..5de91cf3 100644 --- a/server/lotus-server.cpp +++ b/server/lotus-server.cpp @@ -286,6 +286,10 @@ int main(int argc, char* argv[]) { uinput.send_backspace(); --pending_backspaces; last_bs_ms = now_ms; + if (pending_backspaces == 0) { + char ack = '7'; + send(fds[3].fd, &ack, sizeof(ack), MSG_NOSIGNAL); + } } } diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 2f4ae83e..5d15e301 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -394,7 +394,7 @@ namespace fcitx { // TODO: Properly fixes instead ugly WA state->wa_flag = false; state->surrtp = false; - state->waitAck_ = false; + bool prevAck = state->waitAck_; if (*config_.fixUinputWithAck) { if (targetMode == LotusMode::Uinput || targetMode == LotusMode::UinputHC || targetMode == LotusMode::Smooth) { #if __cplusplus >= 202002L @@ -420,6 +420,12 @@ namespace fcitx { } } } + if (prevAck != state->waitAck_ && uinput_client_fd_ >= 0) { + // close(uinput_client_fd_); + // uinput_client_fd_ = -1; + char drain[64]; + recv(uinput_client_fd_, drain, sizeof(drain), MSG_DONTWAIT | MSG_NOSIGNAL); + } if (event.type() == EventType::InputContextFocusIn && is_dbus && !surrvalid) { LOTUS_INFO("Skip clearAllBuffers"); } else if (surrvalid && !state->oldPreBuffer_.empty() && (now_ms() - state->lastDeactivateTime_) < 100) { diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 4794d901..869b2399 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -144,7 +144,11 @@ namespace fcitx { if (waitAck_) { LOTUS_INFO("Waiting for ack"); - std::this_thread::sleep_for(std::chrono::milliseconds(count * 5)); + char ack; + recv(uinput_client_fd_, &ack, sizeof(ack), MSG_NOSIGNAL); + replacement_start_ms_.store(0, std::memory_order_release); + // ez way but cause alot of problem + //std::this_thread::sleep_for(std::chrono::milliseconds(count * 5)); } } @@ -467,6 +471,7 @@ namespace fcitx { return false; // Allow intermediate backspaces to reach the app to clear autofill/old text. } is_deleting_.store(false); + /* replacement_start_ms_.store(0, std::memory_order_release); replacement_thread_id_.store(0, std::memory_order_release); int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); @@ -483,6 +488,17 @@ namespace fcitx { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } + */ + replacement_start_ms_.store(0, std::memory_order_release); + replacement_thread_id_.store(0, std::memory_order_release); + int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); + int64_t wait_ms = static_cast(sleepTime) - elapsed_ms; + if (wait_ms > 0) + std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); + if (waitAck_){ + const int wait_ms_ack = 5; + std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms_ack)); + } ic_->commitString(pending_commit_string_); LOTUS_INFO("Commit: " + pending_commit_string_); expected_backspaces_ = 0; @@ -490,8 +506,6 @@ namespace fcitx { pending_commit_string_ = ""; event.filterAndAccept(); // Filter out the final trigger backspace. - //if (getFrontendName(ic_) == "dbus" && !ic_->surroundingText().isValid()) - // replayBufferedKeys(); // Does we need drop this? return true; } return false; @@ -958,8 +972,6 @@ namespace fcitx { } replacement_thread_id_.store(0, std::memory_order_release); replacement_start_ms_.store(0, std::memory_order_release); - //if (getFrontendName(ic_) == "dbus" && !ic_->surroundingText().isValid()) - // replayBufferedKeys(); // Does we need drop this? } KeySym currentSym = keyEvent.rawKey().sym(); if (*engine_->config().autoCapitalizeAfterPunctuation && realMode != LotusMode::Off) { diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index fd1ef592..2fc008fb 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -141,9 +141,12 @@ namespace fcitx { void applyFromConfig(LotusEngine* engine) { if (!uk_) return; - UkInputMethod im = mapLotusIm(engine->config().inputMethod.value()); - uk_->setInputMethod(im); + + UkInputMethod currentIM_ = mapLotusIm(engine->config().inputMethod.value()); + + uk_->setInputMethod(currentIM_); uk_->setOutputCharset(mapLotusCharset(engine->config().outputCharset.value())); + UnikeyOptions opt{}; opt.freeMarking = *engine->config().freeMarking ? 1 : 0; opt.modernStyle = *engine->config().modernStyle ? 1 : 0; @@ -154,6 +157,7 @@ namespace fcitx { opt.useIME = 0; opt.spellCheckEnabled = *engine->config().spellCheck ? 1 : 0; opt.autoNonVnRestore = *engine->config().autoNonVnRestore ? 1 : 0; + uk_->setOptions(&opt); } @@ -236,16 +240,39 @@ namespace fcitx { } if (rawSym >= FcitxKey_space && rawSym <= FcitxKey_asciitilde) { - uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); + const bool beginWord = uk_->isAtWordBeginning(); + + // Forward numbers in Telex. + // Prevent tone-number handling from eating digits. + if ( + rawSym >= FcitxKey_0 && + rawSym <= FcitxKey_9) { + return false; + } + + // Keep leading "w" literal at beginning of word. + // Avoid "w" -> "ư". + if ( + beginWord && + (rawSym == FcitxKey_w || rawSym == FcitxKey_W)) { + return false; + } + + uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, + st.test(KeyState::CapsLock) ? 1 : 0); + uk_->filter(sym); syncState(rawSym); - if (!preeditStr_.empty() && preeditStr_.back() == static_cast(sym) && isWordBreakSym(static_cast(sym))) { + if (!preeditStr_.empty() && + preeditStr_.back() == static_cast(sym) && + isWordBreakSym(static_cast(sym))) { pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); return true; } + return true; } diff --git a/unikey/core/usrkeymap.cpp b/unikey/core/usrkeymap.cpp index 0dd8bb48..54e2afce 100644 --- a/unikey/core/usrkeymap.cpp +++ b/unikey/core/usrkeymap.cpp @@ -32,7 +32,7 @@ namespace { return static_cast(static_cast(vneCount) + static_cast(v)); } - constexpr UkEventLabelPair UkEvLabelList[] = { + static const UkEventLabelPair UkEvLabelList[] = { {"Tone0", vneTone0}, {"Tone1", vneTone1}, {"Tone2", vneTone2}, {"Tone3", vneTone3}, {"Tone4", vneTone4}, {"Tone5", vneTone5}, {"Roof-All", vneRoofAll}, {"Roof-A", vneRoof_a}, {"Roof-E", vneRoof_e}, {"Roof-O", vneRoof_o}, {"Hook-Bowl", vneHookAll}, {"Hook-UO", vneHook_uo}, {"Hook-U", vneHook_u}, {"Hook-O", vneHook_o}, {"Bowl", vneBowl}, {"D-Mark", vneDd}, {"Telex-W", vne_telex_w}, {"Escape", vneEscChar}, From 778948e130a331c0792cdd2fcbc14bb021e4ed52 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 10 May 2026 05:05:02 +0700 Subject: [PATCH 18/42] small fix Signed-off-by: Zebra2711 --- src/app_quirks.h | 9 ++++-- src/lotus-config.h | 8 ++--- src/lotus-engine.cpp | 23 +++++++++---- src/lotus-state.cpp | 62 ++++++++++++++++++++++-------------- src/lotus-state.h | 4 +-- src/lotus-unikey-backend.cpp | 2 +- 6 files changed, 69 insertions(+), 39 deletions(-) diff --git a/src/app_quirks.h b/src/app_quirks.h index 45cd69b2..c65fce9e 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -20,11 +20,16 @@ * * Chromium-based browsers that need special handling for text replacement. */ -inline constexpr std::array ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", - "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex"}; +inline constexpr std::array ack_apps = { + "chrome", "chromium", "brave", "edge", "vivaldi", "opera", + "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex", + "vesktop" +}; /** * @brief List of application names have goood support surrowding text * */ inline constexpr std::array surrtp_apps = {"soffice", "mullvad", "waterfox", "librewolf"}; + +inline constexpr std::array terminalm = {"foot", "kitty", "alacritty", "ghostty", "st"}; diff --git a/src/lotus-config.h b/src/lotus-config.h index 648e3520..1799e8e9 100644 --- a/src/lotus-config.h +++ b/src/lotus-config.h @@ -29,7 +29,7 @@ namespace fcitx { Off = 0, Smooth = 1, Uinput = 2, - UinputHC = 3, + UinputWine = 3, SurroundingText = 4, Preedit = 5, Emoji = 6, @@ -47,7 +47,7 @@ namespace fcitx { case LotusMode::Uinput: return "Uinput (Slow)"; case LotusMode::SurroundingText: return "Surrounding Text"; case LotusMode::Preedit: return "Preedit"; - case LotusMode::UinputHC: return "Uinput (Hardcore)"; + case LotusMode::UinputWine: return "Uinput (Wine)"; case LotusMode::Emoji: return "Emoji Picker"; case LotusMode::Smooth: return "Uinput (Smooth)"; default: return ""; @@ -65,7 +65,7 @@ namespace fcitx { {"Uinput (Slow)", LotusMode::Uinput}, {"Surrounding Text", LotusMode::SurroundingText}, {"Preedit", LotusMode::Preedit}, - {"Uinput (Hardcore)", LotusMode::UinputHC}, + {"Uinput (Wine)", LotusMode::UinputWine}, {"Emoji Picker", LotusMode::Emoji}, {"Uinput (Smooth)", LotusMode::Smooth}, }; @@ -153,7 +153,7 @@ namespace fcitx { * @brief Initializes with default mode list. */ ModeListAnnotation() { - list_ = {"Uinput (Smooth)", "Uinput (Slow)", "Surrounding Text", "Preedit", "Uinput (Hardcore)", "OFF"}; + list_ = {"Uinput (Smooth)", "Uinput (Slow)", "Surrounding Text", "Preedit", "Uinput (Wine)", "OFF"}; } }; diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 5d15e301..d1d9ac21 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -392,13 +392,16 @@ namespace fcitx { // it not support surrounding text so can't know when it show suggestions // // TODO: Properly fixes instead ugly WA + state->isTerm = false; state->wa_flag = false; state->surrtp = false; bool prevAck = state->waitAck_; + state->waitAck_ = false; if (*config_.fixUinputWithAck) { - if (targetMode == LotusMode::Uinput || targetMode == LotusMode::UinputHC || targetMode == LotusMode::Smooth) { + if (targetMode == LotusMode::Uinput || targetMode == LotusMode::UinputWine || targetMode == LotusMode::Smooth) { #if __cplusplus >= 202002L - std::ranges::transform(appName, appName.begin(), ::tolower); + std::ranges::transform(appName, appName.begin(), + [](unsigned char c) { return std::tolower(c); }); #else std::transform(appName.begin(), appName.end(), appName.begin(), ::tolower); #endif @@ -414,13 +417,21 @@ namespace fcitx { } for (const auto& _App : surrtp_apps) { if (appName.find(_App) != std::string::npos) { + LOTUS_INFO(std::string(_App) + " support surr"); state->surrtp = true; break; } } + for (const auto& _term : terminalm) { + if (appName.find(_term) != std::string::npos) { + LOTUS_INFO(std::string(_term) + " is terminal"); + state->isTerm = true; + break; + } + } } } - if (prevAck != state->waitAck_ && uinput_client_fd_ >= 0) { + if (prevAck != state->waitAck_ && !state->waitAck_ && uinput_client_fd_ >= 0) { // close(uinput_client_fd_); // uinput_client_fd_ = -1; char drain[64]; @@ -536,7 +547,7 @@ namespace fcitx { break; } case FcitxKey_3: { - selectedMode = LotusMode::UinputHC; + selectedMode = LotusMode::UinputWine; break; } case FcitxKey_4: { @@ -885,7 +896,7 @@ namespace fcitx { candidateList->append(std::make_unique(Text(_("App: ") + currentConfigureApp_))); candidateList->append(std::make_unique(getLabel(LotusMode::Smooth, _("[1] Uinput (Smooth)")), applyMode(LotusMode::Smooth))); candidateList->append(std::make_unique(getLabel(LotusMode::Uinput, _("[2] Uinput (Slow)")), applyMode(LotusMode::Uinput))); - candidateList->append(std::make_unique(getLabel(LotusMode::UinputHC, _("[3] Uinput (Hardcore)")), applyMode(LotusMode::UinputHC))); + candidateList->append(std::make_unique(getLabel(LotusMode::UinputWine, _("[3] Uinput (Wine)")), applyMode(LotusMode::UinputWine))); candidateList->append(std::make_unique(getLabel(LotusMode::SurroundingText, _("[4] Surrounding Text")), applyMode(LotusMode::SurroundingText))); candidateList->append(std::make_unique(getLabel(LotusMode::Preedit, _("[q] Preedit")), applyMode(LotusMode::Preedit))); candidateList->append(std::make_unique(getLabel(LotusMode::Emoji, _("[w] Emoji Picker")), applyMode(LotusMode::Emoji))); @@ -916,7 +927,7 @@ namespace fcitx { switch (realMode) { case LotusMode::Smooth: selectedIndex = 1; break; case LotusMode::Uinput: selectedIndex = 2; break; - case LotusMode::UinputHC: selectedIndex = 3; break; + case LotusMode::UinputWine: selectedIndex = 3; break; case LotusMode::SurroundingText: selectedIndex = 4; break; case LotusMode::Preedit: selectedIndex = 5; break; case LotusMode::Emoji: selectedIndex = 6; break; diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 869b2399..18772f1c 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -144,22 +144,28 @@ namespace fcitx { if (waitAck_) { LOTUS_INFO("Waiting for ack"); + LOTUS_INFO("chrome x11 hit me"); char ack; recv(uinput_client_fd_, &ack, sizeof(ack), MSG_NOSIGNAL); + // keep safe that bs is finish by app + std::this_thread::sleep_for(std::chrono::milliseconds(1)); replacement_start_ms_.store(0, std::memory_order_release); // ez way but cause alot of problem //std::this_thread::sleep_for(std::chrono::milliseconds(count * 5)); + } else { + LOTUS_INFO("firefox hit me"); + std::this_thread::sleep_for(std::chrono::milliseconds(count * 2)); } } void LotusState::send_backspace_forward(int count) const { if (count <= 0) return; - for (int i = 0; i < count - 1; ++i) { + for (int i = 0; i < count; ++i) { ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), false); ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), true); } - send_backspace_uinput(0); // trigger 1bs to make all bs prev release + //send_backspace_uinput(0); // trigger 1bs to make all bs prev release } bool LotusState::isAutofillCertain(const SurroundingText& s) { @@ -504,7 +510,6 @@ namespace fcitx { expected_backspaces_ = 0; current_backspace_count_ = 0; pending_commit_string_ = ""; - event.filterAndAccept(); // Filter out the final trigger backspace. return true; } @@ -519,29 +524,33 @@ namespace fcitx { const auto& surrounding = ic_->surroundingText(); int autofillOffset = isAutofillCertain(surrounding) ? 1 : 0; expected_backspaces_ = static_cast(utf8::length(deletedPart)) + 1 + autofillOffset; + if (realMode == LotusMode::UinputWine) + --expected_backspaces_; // Use deleteSurroundingText for apps that support it for smooth typing - if (surrtp // Lmfao, only this work :> - && surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) && - (surrounding.text()).back() != '\n' // firefox and discord insert '\n' into surrounding cause bug - && !(autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes + bool test_flags = true; // use for testing only :v + if (surrtp) + LOTUS_INFO("surrtp"); + if ( (test_flags || surrtp) // Lmfao, only this work :> + && (surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) + && (!surrounding.text().empty() + && surrounding.text().back() != '\n') // firefox and discord insert '\n' into surr cause bug + && !autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes ) { + LOTUS_INFO("deleteSurroundingText branch"); auto cur = static_cast(surrounding.cursor()); const int bsCount = static_cast(utf8::length(deletedPart)); if (autofillOffset) { + LOTUS_INFO("have suggestions branch"); int surrLen = static_cast(utf8::length(surrounding.text())); int realLen = static_cast(cur); int suggestionLen = surrLen - realLen; // delete suggestion tail if (suggestionLen > 0) ic_->deleteSurroundingText(0, 1); - // delete addedPart - if (bsCount > 0) - ic_->deleteSurroundingText(-bsCount, static_cast(bsCount)); - } else { - if (bsCount > 0) { - ic_->deleteSurroundingText(-bsCount, static_cast(bsCount)); - } } + // delete addedPart + if (bsCount > 0) + ic_->deleteSurroundingText(-bsCount, static_cast(bsCount)); ic_->commitString(addedPart); //clearAllBuffers(); return true; @@ -550,9 +559,14 @@ namespace fcitx { replacement_start_ms_.store(now_ms(), std::memory_order_release); is_deleting_.store(true, std::memory_order_release); monitor_cv.notify_one(); - //send_backspace_forward(expected_backspaces_ - 1); - send_backspace_uinput(expected_backspaces_); - LOTUS_INFO("Send " + std::to_string(expected_backspaces_) + " backspaces"); + if (0 && isTerm) { + send_backspace_forward(expected_backspaces_ - 1); + return true; + } else + send_backspace_uinput(expected_backspaces_); + LOTUS_INFO("Send " + std::to_string(expected_backspaces_ - 1 - autofillOffset) + " backspaces + 1 trigger"); + if (autofillOffset) + LOTUS_INFO("Send more 1 extra delete suggestions"); } return false; } @@ -617,7 +631,7 @@ namespace fcitx { return false; } - void LotusState::handleUinputMode(KeyEvent& keyEvent, KeySym currentSym, bool checkEmptyPreedit) { + void LotusState::handleUinputMode(KeyEvent& keyEvent, KeySym currentSym) { if (checkForwardSpecialKey(keyEvent, currentSym)) { keyEvent.forward(); return; @@ -690,7 +704,7 @@ namespace fcitx { // Treat "processed but no effect" as passthrough if (!processed || (!commitStr.empty() && !preeditStrBuf.empty())) { - if (checkEmptyPreedit && !preeditStrBuf.empty()) { + if (!preeditStrBuf.empty()) { hasHistory_ = false; inputBackend_->resetEngine(); oldPreBuffer_.clear(); @@ -1049,11 +1063,11 @@ namespace fcitx { switch (realMode) { case LotusMode::Uinput: case LotusMode::Smooth: { - handleUinputMode(keyEvent, currentSym, true); + handleUinputMode(keyEvent, currentSym); break; } - case LotusMode::UinputHC: { - handleUinputMode(keyEvent, currentSym, false); + case LotusMode::UinputWine: { + handleUinputMode(keyEvent, currentSym); break; } case LotusMode::SurroundingText: { @@ -1110,7 +1124,7 @@ namespace fcitx { } case LotusMode::SurroundingText: case LotusMode::Uinput: - case LotusMode::UinputHC: + case LotusMode::UinputWine: case LotusMode::Smooth: { ic_->inputPanel().reset(); break; @@ -1144,7 +1158,7 @@ namespace fcitx { break; } case LotusMode::Uinput: - case LotusMode::UinputHC: + case LotusMode::UinputWine: case LotusMode::Smooth: case LotusMode::SurroundingText: { if (inputBackend_) { diff --git a/src/lotus-state.h b/src/lotus-state.h index a911beb6..8b048c5b 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -113,6 +113,7 @@ namespace fcitx { int64_t lastSkippedResetMs_ = 0; bool wa_flag = false; bool surrtp = false; + bool isTerm = false; /** * @brief Connects to the uinput server. @@ -196,10 +197,9 @@ namespace fcitx { * @brief Handles uinput mode processing. * @param keyEvent The key event. * @param currentSym Current key symbol. - * @param checkEmptyPreedit Whether to check for empty preedit. * @param sleepTime Delay in microseconds. */ - void handleUinputMode(KeyEvent& keyEvent, KeySym currentSym, bool checkEmptyPreedit); + void handleUinputMode(KeyEvent& keyEvent, KeySym currentSym); /** * @brief Handles surrounding text mode. diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index 2fc008fb..cc022d2d 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -218,7 +218,7 @@ namespace fcitx { pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); - return true; + return !pendingPullCommit_.empty(); } if (static_cast(preeditStr_.length()) <= uk_->context()->backspaces()) preeditStr_.clear(); From acd92a848a8bda91de70cf7e6d16448ac8da6e0d Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sun, 10 May 2026 05:25:26 +0700 Subject: [PATCH 19/42] fix use correct mode Signed-off-by: Zebra2711 --- src/app_quirks.h | 7 ++----- src/lotus-engine.cpp | 2 +- src/lotus-state.cpp | 11 +++++------ src/lotus-unikey-backend.cpp | 29 +++++------------------------ 4 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/app_quirks.h b/src/app_quirks.h index c65fce9e..7e7a9a37 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -20,11 +20,8 @@ * * Chromium-based browsers that need special handling for text replacement. */ -inline constexpr std::array ack_apps = { - "chrome", "chromium", "brave", "edge", "vivaldi", "opera", - "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex", - "vesktop" -}; +inline constexpr std::array ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", + "cromite", "helium", "thorium", "slimjet", "yandex", "vesktop"}; /** * @brief List of application names have goood support surrowding text diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index d1d9ac21..12e2fc14 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -116,7 +116,7 @@ namespace fcitx { imNames_ = std::move(imNames); } #else - imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "Telex + VNI + VIQR", "VIQR", "Microsoft layout", "VNI Bàn phím tiếng Pháp", "Custom"}; + imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "Telex + VNI + VIQR", "VIQR", "Microsoft layout", "VNI Bàn phím tiếng Pháp", "Simple","Custom"}; #endif config_.inputMethod.annotation().setList(imNames_); diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 18772f1c..e845d352 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -501,7 +501,7 @@ namespace fcitx { int64_t wait_ms = static_cast(sleepTime) - elapsed_ms; if (wait_ms > 0) std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); - if (waitAck_){ + if (waitAck_) { const int wait_ms_ack = 5; std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms_ack)); } @@ -530,11 +530,10 @@ namespace fcitx { bool test_flags = true; // use for testing only :v if (surrtp) LOTUS_INFO("surrtp"); - if ( (test_flags || surrtp) // Lmfao, only this work :> - && (surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) - && (!surrounding.text().empty() - && surrounding.text().back() != '\n') // firefox and discord insert '\n' into surr cause bug - && !autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes + if ((test_flags || surrtp) // Lmfao, only this work :> + && (surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) && + (!surrounding.text().empty() && surrounding.text().back() != '\n') // firefox and discord insert '\n' into surr cause bug + && !autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes ) { LOTUS_INFO("deleteSurroundingText branch"); auto cur = static_cast(surrounding.cursor()); diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index cc022d2d..f08ef69c 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -33,7 +33,7 @@ namespace fcitx { } static UkInputMethod mapLotusIm(const std::string& name) { - if (name.find("Telex") != std::string::npos && name.find("VNI") == std::string::npos) + if (name.find("Telex 2") != std::string::npos && name.find("VNI") == std::string::npos) return UkTelex; if (name.find("VNI") != std::string::npos || name == "VNI") return UkVni; @@ -41,8 +41,8 @@ namespace fcitx { return UkViqr; if (name.find("Microsoft") != std::string::npos || name.find("Ms") != std::string::npos) return UkMsVi; - if (name.find("Simple") != std::string::npos) - return UkSimpleTelex2; + if (name.find("Telex") != std::string::npos) + return UkSimpleTelex; return UkTelex; } @@ -242,31 +242,12 @@ namespace fcitx { if (rawSym >= FcitxKey_space && rawSym <= FcitxKey_asciitilde) { const bool beginWord = uk_->isAtWordBeginning(); - // Forward numbers in Telex. - // Prevent tone-number handling from eating digits. - if ( - rawSym >= FcitxKey_0 && - rawSym <= FcitxKey_9) { - return false; - } - - // Keep leading "w" literal at beginning of word. - // Avoid "w" -> "ư". - if ( - beginWord && - (rawSym == FcitxKey_w || rawSym == FcitxKey_W)) { - return false; - } - - uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, - st.test(KeyState::CapsLock) ? 1 : 0); + uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); uk_->filter(sym); syncState(rawSym); - if (!preeditStr_.empty() && - preeditStr_.back() == static_cast(sym) && - isWordBreakSym(static_cast(sym))) { + if (!preeditStr_.empty() && preeditStr_.back() == static_cast(sym) && isWordBreakSym(static_cast(sym))) { pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); From de204563ad615c9c5b9ba47c8340e54a5ff4bc16 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Tue, 12 May 2026 06:25:19 +0700 Subject: [PATCH 20/42] small fix Signed-off-by: Zebra2711 --- src/lotus-unikey-backend.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index f08ef69c..7998779d 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -34,7 +34,7 @@ namespace fcitx { static UkInputMethod mapLotusIm(const std::string& name) { if (name.find("Telex 2") != std::string::npos && name.find("VNI") == std::string::npos) - return UkTelex; + return UkSimpleTelex; if (name.find("VNI") != std::string::npos || name == "VNI") return UkVni; if (name.find("VIQR") != std::string::npos) @@ -199,7 +199,6 @@ namespace fcitx { rawSym == FcitxKey_Delete || rawSym == FcitxKey_KP_Enter || (rawSym >= FcitxKey_Home && rawSym <= FcitxKey_Insert) || (rawSym >= FcitxKey_KP_Home && rawSym <= FcitxKey_KP_Delete)) { uk_->context()->filter(0); - syncState(rawSym); if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); @@ -231,7 +230,6 @@ namespace fcitx { if (rawSym >= FcitxKey_KP_Multiply && rawSym <= FcitxKey_KP_9) { uk_->context()->filter(0); - syncState(rawSym); if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); From e84e192891df5cacb2fd5aa21339564c55316123 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Wed, 13 May 2026 06:09:19 +0700 Subject: [PATCH 21/42] more fix Signed-off-by: Zebra2711 --- src/lotus-engine.cpp | 6 +++-- src/lotus-state.cpp | 52 ++++++++++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 12e2fc14..74ba9227 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -451,7 +451,8 @@ namespace fcitx { LOTUS_INFO("inputPanel reset"); ic->inputPanel().reset(); ic->updateUserInterface(UserInterfaceComponent::InputPanel); - ic->updatePreedit(); + if (realMode == LotusMode::Preedit) + ic->updatePreedit(); } for (const auto& action : toggleActions_) { statusArea.addAction(StatusGroup::InputMethod, action); @@ -682,7 +683,8 @@ namespace fcitx { needEngineReset.store(false); ic->inputPanel().reset(); ic->updateUserInterface(UserInterfaceComponent::InputPanel); - ic->updatePreedit(); + if (realMode == LotusMode::Preedit) + ic->updatePreedit(); } } diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index e845d352..cf5b39ad 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -70,6 +70,20 @@ namespace fcitx { return r; } + inline void update_max(std::atomic& value, uint32_t target) { + uint32_t current = value.load(std::memory_order_acquire); + + asm volatile("1:\n\t" + "cmpl %[target], %[current]\n\t" + "jae 2f\n\t" + "lock cmpxchgl %[target], %[mem]\n\t" + "jne 1b\n\t" + "2:\n\t" + : [mem] "+m"(value), [current] "+a"(current) + : [target] "r"(target) + : "memory"); + } + LotusState::LotusState(LotusEngine* engine, InputContext* ic) : engine_(engine), ic_(ic) { setEngine(); } @@ -141,10 +155,10 @@ namespace fcitx { send(uinput_client_fd_, &count, sizeof(count), MSG_NOSIGNAL); } } - - if (waitAck_) { + //HACK + //if (waitAck_) { LOTUS_INFO("Waiting for ack"); - LOTUS_INFO("chrome x11 hit me"); + // LOTUS_INFO("chrome x11 hit me"); char ack; recv(uinput_client_fd_, &ack, sizeof(ack), MSG_NOSIGNAL); // keep safe that bs is finish by app @@ -152,10 +166,10 @@ namespace fcitx { replacement_start_ms_.store(0, std::memory_order_release); // ez way but cause alot of problem //std::this_thread::sleep_for(std::chrono::milliseconds(count * 5)); - } else { - LOTUS_INFO("firefox hit me"); - std::this_thread::sleep_for(std::chrono::milliseconds(count * 2)); - } + //} else { + // LOTUS_INFO("firefox hit me"); + // std::this_thread::sleep_for(std::chrono::milliseconds(count * 2)); + //} } void LotusState::send_backspace_forward(int count) const { @@ -176,22 +190,24 @@ namespace fcitx { const unsigned int cursor = s.cursor(); const unsigned int anchor = s.anchor(); const auto& text = s.text(); - const size_t textLen = utf8::length(text); + const size_t cursor_sz = static_cast(cursor); // Fix that surrounding text is delay update const size_t buffLen = utf8::length(oldPreBuffer_); const size_t pb = text.find(oldPreBuffer_); - size_t rangeStart = buffLen >= static_cast(cursor) ? 0 : static_cast(cursor) - buffLen; - const bool sameprefix = pb != std::string::npos && pb >= rangeStart && pb <= static_cast(cursor); + size_t rangeStart = buffLen >= cursor_sz ? 0 : cursor_sz - buffLen; + const bool sameprefix = pb != std::string::npos && pb >= rangeStart && pb <= cursor_sz; // Detect browser autofill/autocomplete suggestions via selection. + // This check for wayland_input method v2/v3 and not dbus if (cursor != anchor) { + LOTUS_INFO("check suggest wayland"); unsigned int selectionStart = std::min(anchor, cursor); unsigned int selectionEnd = std::max(anchor, cursor); // Only consider it browser autofill if the selection starts at the cursor // and extends to the end of the line (common address bar behavior). - if (selectionStart >= cursor || (selectionStart < cursor && selectionEnd > cursor)) { + if (cursor <= selectionEnd) { if (!sameprefix) return false; // If the selection contains a newline, it's likely a multiline editor (AI ghost text), @@ -201,18 +217,22 @@ namespace fcitx { } } - if (textLen == static_cast(cursor)) { + const size_t textLen = utf8::length(text); + if (textLen == cursor_sz) { realtextLen.store(textLen, std::memory_order_release); return false; } // Heuristic: rapid text growth in a single-line context. // Applied only when no newline is present after the cursor to distinguish from AI text in editors. - if (textLen > static_cast(cursor) && cursor == realtextLen.load(std::memory_order_acquire) && text.find('\n', cursor) == std::string::npos && sameprefix) - return true; + // Check for wayland app that use dbus as backend + if (textLen > cursor_sz) + if(cursor == realtextLen.load(std::memory_order_acquire) + && text.find('\n', cursor) == std::string::npos + && sameprefix) + return true; - for (auto v = realtextLen.load(std::memory_order_acquire); v < cursor && !realtextLen.compare_exchange_weak(v, cursor, std::memory_order_acq_rel);) - ; + update_max(realtextLen, static_cast(cursor)); return false; } From 029d4937da88060c07c590f086ed050c89b9746a Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Wed, 13 May 2026 12:13:47 +0700 Subject: [PATCH 22/42] eeee --- src/app_quirks.h | 2 +- unikey/core/byteio.cpp | 166 +- unikey/core/byteio.h | 146 +- unikey/core/charset.cpp | 583 ++++--- unikey/core/charset.h | 244 +-- unikey/core/convert.cpp | 53 +- unikey/core/data.cpp | 599 ++++--- unikey/core/data.h | 9 +- unikey/core/inputproc.cpp | 174 +- unikey/core/inputproc.h | 55 +- unikey/core/keycons.h | 31 +- unikey/core/mactab.cpp | 121 +- unikey/core/mactab.h | 38 +- unikey/core/pattern.cpp | 12 +- unikey/core/pattern.h | 31 +- unikey/core/ukengine.cpp | 2409 ++++++++++++++++++---------- unikey/core/ukengine.h | 129 +- unikey/core/unikeyinputcontext.cpp | 53 +- unikey/core/unikeyinputcontext.h | 44 +- unikey/core/usrkeymap.cpp | 107 +- unikey/core/usrkeymap.h | 3 +- unikey/core/vnconv.h | 60 +- 22 files changed, 3037 insertions(+), 2032 deletions(-) diff --git a/src/app_quirks.h b/src/app_quirks.h index 7e7a9a37..9737c0b7 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -27,6 +27,6 @@ inline constexpr std::array ack_apps = {"chrome", "chromi * @brief List of application names have goood support surrowding text * */ -inline constexpr std::array surrtp_apps = {"soffice", "mullvad", "waterfox", "librewolf"}; +inline constexpr std::array surrtp_apps = {"soffice"}; inline constexpr std::array terminalm = {"foot", "kitty", "alacritty", "ghostty", "st"}; diff --git a/unikey/core/byteio.cpp b/unikey/core/byteio.cpp index 12c1397b..81fb773c 100644 --- a/unikey/core/byteio.cpp +++ b/unikey/core/byteio.cpp @@ -7,14 +7,14 @@ #include //------------------------------------------------ -StringBIStream::StringBIStream(UKBYTE* data, int len, int elementSize) { +StringBIStream::StringBIStream(UKBYTE *data, int len, int elementSize) { m_data = m_current = data; m_len = m_left = len; if (len == -1) { if (elementSize == 2) - m_eos = (*(UKWORD*)data == 0); + m_eos = (*(UKWORD *)data == 0); else if (elementSize == 4) - m_eos = (*(UKDWORD*)data == 4); + m_eos = (*(UKDWORD *)data == 4); else m_eos = (*data == 0); } else @@ -23,12 +23,10 @@ StringBIStream::StringBIStream(UKBYTE* data, int len, int elementSize) { } //------------------------------------------------ -int StringBIStream::eos() { - return m_eos; -} +int StringBIStream::eos() { return m_eos; } //------------------------------------------------ -int StringBIStream::getNext(UKBYTE& b) { +int StringBIStream::getNext(UKBYTE &b) { if (m_eos) return 0; b = *m_current++; @@ -45,7 +43,7 @@ int StringBIStream::getNext(UKBYTE& b) { int StringBIStream::unget(UKBYTE b) { if (m_current != m_data) { *--m_current = b; - m_eos = 0; + m_eos = 0; if (m_len != -1) m_left++; } @@ -53,10 +51,10 @@ int StringBIStream::unget(UKBYTE b) { } //------------------------------------------------ -int StringBIStream::getNextW(UKWORD& w) { +int StringBIStream::getNextW(UKWORD &w) { if (m_eos) return 0; - w = *((UKWORD*)m_current); + w = *((UKWORD *)m_current); m_current += 2; if (m_len == -1) m_eos = (w == 0); @@ -68,11 +66,11 @@ int StringBIStream::getNextW(UKWORD& w) { } //------------------------------------------------ -int StringBIStream::getNextDW(UKDWORD& dw) { +int StringBIStream::getNextDW(UKDWORD &dw) { if (m_eos) return 0; - dw = *((UKDWORD*)m_current); + dw = *((UKDWORD *)m_current); m_current += 4; if (m_len == -1) m_eos = (dw == 0); @@ -84,7 +82,7 @@ int StringBIStream::getNextDW(UKDWORD& dw) { } //------------------------------------------------ -int StringBIStream::peekNext(UKBYTE& b) { +int StringBIStream::peekNext(UKBYTE &b) { if (m_eos) return 0; b = *m_current; @@ -92,10 +90,10 @@ int StringBIStream::peekNext(UKBYTE& b) { } //------------------------------------------------ -int StringBIStream::peekNextW(UKWORD& w) { +int StringBIStream::peekNextW(UKWORD &w) { if (m_eos) return 0; - w = *((UKWORD*)m_current); + w = *((UKWORD *)m_current); return 1; } @@ -113,7 +111,7 @@ int StringBIStream::peekNextDW(UKDWORD & dw) //------------------------------------------------ void StringBIStream::reopen() { m_current = m_data; - m_left = m_len; + m_left = m_len; if (m_len == -1) m_eos = (m_data == 0); else @@ -123,12 +121,12 @@ void StringBIStream::reopen() { //------------------------------------------------ int StringBIStream::bookmark() { - m_didBookmark = 1; + m_didBookmark = 1; m_bookmark.current = m_current; - m_bookmark.data = m_data; - m_bookmark.eos = m_eos; - m_bookmark.left = m_left; - m_bookmark.len = m_len; + m_bookmark.data = m_data; + m_bookmark.eos = m_eos; + m_bookmark.left = m_left; + m_bookmark.len = m_len; return 1; } @@ -137,28 +135,26 @@ int StringBIStream::gotoBookmark() { if (!m_didBookmark) return 0; m_current = m_bookmark.current; - m_data = m_bookmark.data; - m_eos = m_bookmark.eos; - m_left = m_bookmark.left; - m_len = m_bookmark.len; + m_data = m_bookmark.data; + m_eos = m_bookmark.eos; + m_left = m_bookmark.left; + m_len = m_bookmark.len; return 1; } //------------------------------------------------ -int StringBIStream::close() { - return 1; -}; +int StringBIStream::close() { return 1; }; ////////////////////////////////////////////////// // Class StringBOStream ////////////////////////////////////////////////// //------------------------------------------------ -StringBOStream::StringBOStream(UKBYTE* buf, int len) { +StringBOStream::StringBOStream(UKBYTE *buf, int len) { m_current = m_buf = buf; - m_len = len; - m_out = 0; - m_bad = 0; + m_len = len; + m_out = 0; + m_bad = 0; } //------------------------------------------------ @@ -192,7 +188,7 @@ int StringBOStream::putW(UKWORD w) { if (m_bad) return 0; if (m_out <= m_len) { - *((UKWORD*)m_current) = w; + *((UKWORD *)m_current) = w; m_current += 2; return 1; } @@ -201,7 +197,7 @@ int StringBOStream::putW(UKWORD w) { } //------------------------------------------------ -int StringBOStream::puts(const char* s, int size) { +int StringBOStream::puts(const char *s, int size) { if (size == -1) { while (*s) { m_out++; @@ -232,28 +228,26 @@ int StringBOStream::puts(const char* s, int size) { //------------------------------------------------ void StringBOStream::reopen() { m_current = m_buf; - m_out = 0; - m_bad = 0; + m_out = 0; + m_bad = 0; } //------------------------------------------------ -int StringBOStream::isOK() { - return !m_bad; -} +int StringBOStream::isOK() { return !m_bad; } //////////////////////////////////////////////////// // Class FileBIStream // //////////////////////////////////////////////////// //---------------------------------------------------- -FileBIStream::FileBIStream(int bufSize, char* buf) { - m_file = NULL; - m_buf = buf; - m_bufSize = bufSize; - m_own = 1; +FileBIStream::FileBIStream(int bufSize, char *buf) { + m_file = NULL; + m_buf = buf; + m_bufSize = bufSize; + m_own = 1; m_didBookmark = 0; - m_readAhead = 0; + m_readAhead = 0; m_lastIsAhead = 0; } @@ -264,13 +258,13 @@ FileBIStream::~FileBIStream() { } //---------------------------------------------------- -int FileBIStream::open(const char* fileName) { +int FileBIStream::open(const char *fileName) { m_file = fopen(fileName, "rb"); if (m_file == NULL) return 0; setvbuf(m_file, m_buf, _IOFBF, m_bufSize); - m_own = 0; - m_readAhead = 0; + m_own = 0; + m_readAhead = 0; m_lastIsAhead = 0; return 1; } @@ -285,10 +279,10 @@ int FileBIStream::close() { } //---------------------------------------------------- -void FileBIStream::attach(FILE* f) { - m_file = f; - m_own = 0; - m_readAhead = 0; +void FileBIStream::attach(FILE *f) { + m_file = f; + m_own = 0; + m_readAhead = 0; m_lastIsAhead = 0; } @@ -300,21 +294,21 @@ int FileBIStream::eos() { } //---------------------------------------------------- -int FileBIStream::getNext(UKBYTE& b) { +int FileBIStream::getNext(UKBYTE &b) { if (m_readAhead) { - m_readAhead = 0; - b = m_readByte; + m_readAhead = 0; + b = m_readByte; m_lastIsAhead = 1; return 1; } m_lastIsAhead = 0; - b = fgetc(m_file); + b = fgetc(m_file); return (!feof(m_file)); } //---------------------------------------------------- -int FileBIStream::peekNext(UKBYTE& b) { +int FileBIStream::peekNext(UKBYTE &b) { if (m_readAhead) { b = m_readByte; return 1; @@ -331,8 +325,8 @@ int FileBIStream::peekNext(UKBYTE& b) { int FileBIStream::unget(UKBYTE b) { if (m_lastIsAhead) { m_lastIsAhead = 0; - m_readAhead = 1; - m_readByte = b; + m_readAhead = 1; + m_readByte = b; return 1; } @@ -341,13 +335,13 @@ int FileBIStream::unget(UKBYTE b) { } //---------------------------------------------------- -int FileBIStream::getNextW(UKWORD& w) { +int FileBIStream::getNextW(UKWORD &w) { UKBYTE b1, b2; if (getNext(b1)) { if (getNext(b2)) { - *((UKBYTE*)&w) = b1; - *(((UKBYTE*)&w) + 1) = b2; + *((UKBYTE *)&w) = b1; + *(((UKBYTE *)&w) + 1) = b2; return 1; } } @@ -355,33 +349,33 @@ int FileBIStream::getNextW(UKWORD& w) { } //---------------------------------------------------- -int FileBIStream::getNextDW(UKDWORD& dw) { +int FileBIStream::getNextDW(UKDWORD &dw) { UKWORD w1, w2; if (getNextW(w1)) { if (getNextW(w2)) { - *((UKWORD*)&dw) = w1; - *(((UKWORD*)&dw) + 1) = w2; + *((UKWORD *)&dw) = w1; + *(((UKWORD *)&dw) + 1) = w2; return 1; } } return 0; } //---------------------------------------------------- -int FileBIStream::peekNextW(UKWORD& w) { +int FileBIStream::peekNextW(UKWORD &w) { UKBYTE hi, low; if (getNext(low)) { if (getNext(hi)) { unget(hi); - w = hi; - w = (w << 8) + low; - m_readAhead = 1; - m_readByte = low; + w = hi; + w = (w << 8) + low; + m_readAhead = 1; + m_readByte = low; m_lastIsAhead = 0; return 1; } - m_readAhead = 1; - m_readByte = low; + m_readAhead = 1; + m_readByte = low; m_lastIsAhead = 0; return 0; } @@ -390,7 +384,7 @@ int FileBIStream::peekNextW(UKWORD& w) { //---------------------------------------------------- int FileBIStream::bookmark() { - m_didBookmark = 1; + m_didBookmark = 1; m_bookmark.pos = ftell(m_file); return 1; } @@ -407,12 +401,12 @@ int FileBIStream::gotoBookmark() { // Class FileBOStream // //////////////////////////////////////////////////// //---------------------------------------------------- -FileBOStream::FileBOStream(int bufSize, char* buf) { - m_file = NULL; - m_buf = buf; +FileBOStream::FileBOStream(int bufSize, char *buf) { + m_file = NULL; + m_buf = buf; m_bufSize = bufSize; - m_own = 1; - m_bad = 1; + m_own = 1; + m_bad = 1; } //---------------------------------------------------- @@ -422,7 +416,7 @@ FileBOStream::~FileBOStream() { } //---------------------------------------------------- -int FileBOStream::open(const char* fileName) { +int FileBOStream::open(const char *fileName) { m_file = fopen(fileName, "wb"); if (m_file == NULL) return 0; @@ -433,10 +427,10 @@ int FileBOStream::open(const char* fileName) { } //---------------------------------------------------- -void FileBOStream::attach(FILE* f) { +void FileBOStream::attach(FILE *f) { m_file = f; - m_own = 0; - m_bad = 0; + m_own = 0; + m_bad = 0; } //---------------------------------------------------- @@ -469,7 +463,7 @@ int FileBOStream::putW(UKWORD w) { } //---------------------------------------------------- -int FileBOStream::puts(const char* s, int size) { +int FileBOStream::puts(const char *s, int size) { if (m_bad) return 0; if (size == -1) { @@ -477,11 +471,9 @@ int FileBOStream::puts(const char* s, int size) { return (!m_bad); } int out = fwrite(s, 1, size, m_file); - m_bad = (out != size); + m_bad = (out != size); return (!m_bad); } //---------------------------------------------------- -int FileBOStream::isOK() { - return !m_bad; -} +int FileBOStream::isOK() { return !m_bad; } diff --git a/unikey/core/byteio.h b/unikey/core/byteio.h index 24cbf046..9b3582d7 100644 --- a/unikey/core/byteio.h +++ b/unikey/core/byteio.h @@ -9,75 +9,73 @@ // #include "vnconv.h" #include -typedef unsigned char UKBYTE; +typedef unsigned char UKBYTE; typedef unsigned short UKWORD; -typedef unsigned int UKDWORD; +typedef unsigned int UKDWORD; //---------------------------------------------------- class ByteStream { - public: +public: virtual ~ByteStream() {} }; //---------------------------------------------------- class ByteInStream : public ByteStream { - public: - virtual int getNext(UKBYTE& b) = 0; - virtual int peekNext(UKBYTE& b) = 0; - virtual int unget(UKBYTE b) = 0; +public: + virtual int getNext(UKBYTE &b) = 0; + virtual int peekNext(UKBYTE &b) = 0; + virtual int unget(UKBYTE b) = 0; - virtual int getNextW(UKWORD& w) = 0; - virtual int peekNextW(UKWORD& w) = 0; + virtual int getNextW(UKWORD &w) = 0; + virtual int peekNextW(UKWORD &w) = 0; - virtual int getNextDW(UKDWORD& dw) = 0; + virtual int getNextDW(UKDWORD &dw) = 0; virtual int bookmark() // no support for bookmark by default { return 0; } - virtual int gotoBookmark() { - return 0; - } + virtual int gotoBookmark() { return 0; } - virtual int eos() = 0; // end of stream + virtual int eos() = 0; // end of stream virtual int close() = 0; }; //---------------------------------------------------- class ByteOutStream : public ByteStream { - public: - virtual int putB(UKBYTE b) = 0; - virtual int putW(UKWORD w) = 0; - virtual int puts(const char* s, int size = -1) = 0; // write an 8-bit string - virtual int isOK() = 0; // get current stream state +public: + virtual int putB(UKBYTE b) = 0; + virtual int putW(UKWORD w) = 0; + virtual int puts(const char *s, int size = -1) = 0; // write an 8-bit string + virtual int isOK() = 0; // get current stream state }; //---------------------------------------------------- class StringBIStream : public ByteInStream { - protected: - int m_eos; +protected: + int m_eos; UKBYTE *m_data, *m_current; - int m_len, m_left; + int m_len, m_left; struct { - int eos; + int eos; UKBYTE *data, *current; - int len, left; + int len, left; } m_bookmark; int m_didBookmark; - public: - StringBIStream(UKBYTE* data, int len, int elementSize = 1); - virtual int getNext(UKBYTE& b); - virtual int peekNext(UKBYTE& b); +public: + StringBIStream(UKBYTE *data, int len, int elementSize = 1); + virtual int getNext(UKBYTE &b); + virtual int peekNext(UKBYTE &b); virtual int unget(UKBYTE b); - virtual int getNextW(UKWORD& w); - virtual int peekNextW(UKWORD& w); + virtual int getNextW(UKWORD &w); + virtual int peekNextW(UKWORD &w); - virtual int getNextDW(UKDWORD& dw); + virtual int getNextDW(UKDWORD &dw); virtual int eos(); // end of stream virtual int close(); @@ -85,20 +83,18 @@ class StringBIStream : public ByteInStream { virtual int bookmark(); virtual int gotoBookmark(); - void reopen(); - int left() { - return m_left; - } + void reopen(); + int left() { return m_left; } }; //---------------------------------------------------- class FileBIStream : public ByteInStream { - protected: - FILE* m_file; - int m_bufSize; - char* m_buf; - int m_own; - int m_didBookmark; +protected: + FILE *m_file; + int m_bufSize; + char *m_buf; + int m_own; + int m_didBookmark; struct { long pos; @@ -107,25 +103,25 @@ class FileBIStream : public ByteInStream { // some systems don't have wide char IO functions // we have to use this variables to implement that UKBYTE m_readByte; - int m_readAhead; - int m_lastIsAhead; + int m_readAhead; + int m_lastIsAhead; - public: - FileBIStream(int bufsize = 8192, char* buf = NULL); +public: + FileBIStream(int bufsize = 8192, char *buf = NULL); // FileBIStream(char *fileName, int bufsize = 8192, void *buf = NULL); - int open(const char* fileName); - void attach(FILE* f); + int open(const char *fileName); + void attach(FILE *f); virtual int close(); - virtual int getNext(UKBYTE& b); - virtual int peekNext(UKBYTE& b); + virtual int getNext(UKBYTE &b); + virtual int peekNext(UKBYTE &b); virtual int unget(UKBYTE b); - virtual int getNextW(UKWORD& w); - virtual int peekNextW(UKWORD& w); + virtual int getNextW(UKWORD &w); + virtual int peekNextW(UKWORD &w); - virtual int getNextDW(UKDWORD& dw); + virtual int getNextDW(UKDWORD &dw); virtual int eos(); // end of stream @@ -137,49 +133,45 @@ class FileBIStream : public ByteInStream { //---------------------------------------------------- class StringBOStream : public ByteOutStream { - protected: +protected: UKBYTE *m_buf, *m_current; - int m_out; - int m_len; - int m_bad; + int m_out; + int m_len; + int m_bad; - public: - StringBOStream(UKBYTE* buf, int len); +public: + StringBOStream(UKBYTE *buf, int len); virtual int putB(UKBYTE b); virtual int putW(UKWORD w); - virtual int puts(const char* s, int size = -1); + virtual int puts(const char *s, int size = -1); virtual int isOK(); // get current stream state - virtual int close() { - return 1; - }; + virtual int close() { return 1; }; void reopen(); - int getOutBytes() { - return m_out; - } + int getOutBytes() { return m_out; } }; //---------------------------------------------------- class FileBOStream : public ByteOutStream { - protected: - FILE* m_file; - int m_bufSize; - char* m_buf; - int m_own; - int m_bad; - - public: - FileBOStream(int bufsize = 8192, char* buf = NULL); +protected: + FILE *m_file; + int m_bufSize; + char *m_buf; + int m_own; + int m_bad; + +public: + FileBOStream(int bufsize = 8192, char *buf = NULL); // FileBOStream(char *fileName, int bufsize = 8192, void *buf = NULL); - int open(const char* fileName); - void attach(FILE*); + int open(const char *fileName); + void attach(FILE *); virtual int close(); virtual int putB(UKBYTE b); virtual int putW(UKWORD w); - virtual int puts(const char* s, int size = -1); + virtual int puts(const char *s, int size = -1); virtual int isOK(); // get current stream state virtual ~FileBOStream(); }; diff --git a/unikey/core/charset.cpp b/unikey/core/charset.cpp index 3666fb0f..e6719a4b 100644 --- a/unikey/core/charset.cpp +++ b/unikey/core/charset.cpp @@ -16,22 +16,23 @@ int LoVowel['z' - 'a' + 1]; int HiVowel['Z' - 'A' + 1]; -#define IS_VOWEL(x) ((x >= 'a' && x <= 'z' && LoVowel[x - 'a']) || (x >= 'A' && x <= 'Z' && HiVowel[x - 'A'])) +#define IS_VOWEL(x) \ + ((x >= 'a' && x <= 'z' && LoVowel[x - 'a']) || \ + (x >= 'A' && x <= 'Z' && HiVowel[x - 'A'])) -SingleByteCharset* SgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; -DoubleByteCharset* DbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; +SingleByteCharset *SgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; +DoubleByteCharset *DbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; DllExport CVnCharsetLib VnCharsetLibObj; ////////////////////////////////////////////////////// // Generic VnCharset class ////////////////////////////////////////////////////// -int VnCharset::elementSize() { - return 1; -} +int VnCharset::elementSize() { return 1; } //------------------------------------------- -int VnInternalCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int VnInternalCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { if (!is.getNextDW(stdChar)) { bytesRead = 0; return 0; @@ -41,74 +42,81 @@ int VnInternalCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte } //------------------------------------------- -int VnInternalCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { +int VnInternalCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { outLen = sizeof(StdVnChar); os.putW((UKWORD)stdChar); return os.putW((UKWORD)(stdChar >> (sizeof(UKWORD) * 8))); } //------------------------------------------- -int VnInternalCharset::elementSize() { - return 4; -} +int VnInternalCharset::elementSize() { return 4; } //------------------------------------------- -SingleByteCharset::SingleByteCharset(unsigned char* vnChars) { +SingleByteCharset::SingleByteCharset(unsigned char *vnChars) { int i; m_vnChars = vnChars; memset(m_stdMap, 0, 256 * sizeof(UKWORD)); for (i = 0; i < TOTAL_VNCHARS; i++) { - if (vnChars[i] != 0 && (i == TOTAL_VNCHARS - 1 || vnChars[i] != vnChars[i + 1])) + if (vnChars[i] != 0 && + (i == TOTAL_VNCHARS - 1 || vnChars[i] != vnChars[i + 1])) m_stdMap[vnChars[i]] = i + 1; } } //------------------------------------------- -int SingleByteCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int SingleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { unsigned char ch; if (!is.getNext(ch)) { bytesRead = 0; return 0; } - stdChar = (m_stdMap[ch]) ? (VnStdCharOffset + m_stdMap[ch] - 1) : ch; + stdChar = (m_stdMap[ch]) ? (VnStdCharOffset + m_stdMap[ch] - 1) : ch; bytesRead = 1; return 1; } //------------------------------------------- -int SingleByteCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { - int ret; +int SingleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + int ret; unsigned char ch; if (stdChar >= VnStdCharOffset) { outLen = 1; - ch = m_vnChars[stdChar - VnStdCharOffset]; + ch = m_vnChars[stdChar - VnStdCharOffset]; if (ch == 0) - ch = (stdChar == StdStartQuote) ? PadStartQuote : ((stdChar == StdEndQuote) ? PadEndQuote : ((stdChar == StdEllipsis) ? PadEllipsis : PadChar)); + ch = (stdChar == StdStartQuote) + ? PadStartQuote + : ((stdChar == StdEndQuote) + ? PadEndQuote + : ((stdChar == StdEllipsis) ? PadEllipsis + : PadChar)); ret = os.putB(ch); } else { if (stdChar > 255 || m_stdMap[stdChar]) { // this character is missing in the charset // output padding character outLen = 1; - ret = os.putB(PadChar); + ret = os.putB(PadChar); } else { outLen = 1; - ret = os.putB((UKBYTE)stdChar); + ret = os.putB((UKBYTE)stdChar); } } return ret; } //------------------------------------------- -int wideCharCompare(const void* ele1, const void* ele2) { - UKWORD ch1 = LOWORD(*((UKDWORD*)ele1)); - UKWORD ch2 = LOWORD(*((UKDWORD*)ele2)); +int wideCharCompare(const void *ele1, const void *ele2) { + UKWORD ch1 = LOWORD(*((UKDWORD *)ele1)); + UKWORD ch2 = LOWORD(*((UKDWORD *)ele2)); return (ch1 == ch2) ? 0 : ((ch1 > ch2) ? 1 : -1); } //------------------------------------------- -UnicodeCharset::UnicodeCharset(UnicodeChar* vnChars) { +UnicodeCharset::UnicodeCharset(UnicodeChar *vnChars) { UKDWORD i; m_toUnicode = vnChars; for (i = 0; i < TOTAL_VNCHARS; i++) @@ -117,15 +125,17 @@ UnicodeCharset::UnicodeCharset(UnicodeChar* vnChars) { } //------------------------------------------- -int UnicodeCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int UnicodeCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { UnicodeChar uniCh; if (!is.getNextW(uniCh)) { bytesRead = 0; return 0; } - bytesRead = sizeof(UnicodeChar); - UKDWORD key = uniCh; - UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); + bytesRead = sizeof(UnicodeChar); + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -134,30 +144,31 @@ int UnicodeCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRe } //------------------------------------------- -int UnicodeCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { +int UnicodeCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { outLen = sizeof(UnicodeChar); - return os.putW((stdChar >= VnStdCharOffset) ? m_toUnicode[stdChar - VnStdCharOffset] : (UnicodeChar)stdChar); + return os.putW((stdChar >= VnStdCharOffset) + ? m_toUnicode[stdChar - VnStdCharOffset] + : (UnicodeChar)stdChar); } //------------------------------------------- -int UnicodeCharset::elementSize() { - return 2; -} +int UnicodeCharset::elementSize() { return 2; } //////////////////////////////////////// // Unicode decomposed //////////////////////////////////////// //------------------------------------------- -int uniCompInfoCompare(const void* ele1, const void* ele2) { - UKDWORD ch1 = ((UniCompCharInfo*)ele1)->compChar; - UKDWORD ch2 = ((UniCompCharInfo*)ele2)->compChar; +int uniCompInfoCompare(const void *ele1, const void *ele2) { + UKDWORD ch1 = ((UniCompCharInfo *)ele1)->compChar; + UKDWORD ch2 = ((UniCompCharInfo *)ele2)->compChar; return (ch1 == ch2) ? 0 : ((ch1 > ch2) ? 1 : -1); } -UnicodeCompCharset::UnicodeCompCharset(UnicodeChar* uniChars, UKDWORD* uniCompChars) { +UnicodeCompCharset::UnicodeCompCharset(UnicodeChar *uniChars, + UKDWORD *uniCompChars) { int i, k; m_uniCompChars = uniCompChars; - m_totalChars = 0; + m_totalChars = 0; for (i = 0; i < TOTAL_VNCHARS; i++) { m_info[i].compChar = uniCompChars[i]; m_info[i].stdIndex = i; @@ -176,19 +187,22 @@ UnicodeCompCharset::UnicodeCompCharset(UnicodeChar* uniChars, UKDWORD* uniCompCh } //--------------------------------------------- -int UnicodeCompCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int UnicodeCompCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { // read first char UniCompCharInfo key; - UKWORD w; + UKWORD w; if (!is.getNextW(w)) { bytesRead = 0; return 0; } key.compChar = w; - bytesRead = 2; + bytesRead = 2; - UniCompCharInfo* pInfo = (UniCompCharInfo*)bsearch(&key, m_info, m_totalChars, sizeof(UniCompCharInfo), uniCompInfoCompare); + UniCompCharInfo *pInfo = + (UniCompCharInfo *)bsearch(&key, m_info, m_totalChars, + sizeof(UniCompCharInfo), uniCompInfoCompare); if (!pInfo) stdChar = key.compChar; else { @@ -197,7 +211,9 @@ int UnicodeCompCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byt UKDWORD hi = w; if (hi > 0) { key.compChar += hi << 16; - pInfo = (UniCompCharInfo*)bsearch(&key, m_info, m_totalChars, sizeof(UniCompCharInfo), uniCompInfoCompare); + pInfo = (UniCompCharInfo *)bsearch(&key, m_info, m_totalChars, + sizeof(UniCompCharInfo), + uniCompInfoCompare); if (pInfo) { stdChar = pInfo->stdIndex + VnStdCharOffset; bytesRead += 2; @@ -210,36 +226,36 @@ int UnicodeCompCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byt } //--------------------------------------------- -int UnicodeCompCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { +int UnicodeCompCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { int ret; if (stdChar >= VnStdCharOffset) { UKDWORD uniCompCh = m_uniCompChars[stdChar - VnStdCharOffset]; - UKWORD lo = LOWORD(uniCompCh); - UKWORD hi = HIWORD(uniCompCh); - outLen = 2; - ret = os.putW(lo); + UKWORD lo = LOWORD(uniCompCh); + UKWORD hi = HIWORD(uniCompCh); + outLen = 2; + ret = os.putW(lo); if (hi > 0) { outLen += 2; ret = os.putW(hi); } } else { outLen = 2; - ret = os.putW((UKWORD)stdChar); + ret = os.putW((UKWORD)stdChar); } return ret; } //------------------------------------------- -int UnicodeCompCharset::elementSize() { - return 2; -} +int UnicodeCompCharset::elementSize() { return 2; } //////////////////////////////// // Unicode UTF-8 // //////////////////////////////// -int UnicodeUTF8Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { - UKWORD w1, w2, w3; - UKBYTE first, second, third; +int UnicodeUTF8Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { + UKWORD w1, w2, w3; + UKBYTE first, second, third; UnicodeChar uniCh; bytesRead = 0; @@ -259,9 +275,9 @@ int UnicodeUTF8Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byt } is.getNext(second); bytesRead = 2; - w1 = first; - w2 = second; - uniCh = ((w1 & 0x001F) << 6) | (w2 & 0x3F); + w1 = first; + w2 = second; + uniCh = ((w1 & 0x001F) << 6) | (w2 & 0x3F); } else if ((first & 0xF0) == 0xE0) { // 3-byte sequence if (!is.peekNext(second)) @@ -280,18 +296,19 @@ int UnicodeUTF8Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byt } is.getNext(third); bytesRead = 3; - w1 = first; - w2 = second; - w3 = third; - uniCh = ((w1 & 0x000F) << 12) | ((w2 & 0x003F) << 6) | (w3 & 0x003F); + w1 = first; + w2 = second; + w3 = third; + uniCh = ((w1 & 0x000F) << 12) | ((w2 & 0x003F) << 6) | (w3 & 0x003F); } else { stdChar = INVALID_STD_CHAR; return 1; } // translate to StdVnChar - UKDWORD key = uniCh; - UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -300,12 +317,15 @@ int UnicodeUTF8Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byt } //------------------------------------------- -int UnicodeUTF8Charset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeUTF8Charset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 0x0080) { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else if (uChar < 0x0800) { outLen = 2; os.putB(0xC0 | (UKBYTE)(uChar >> 6)); @@ -333,14 +353,15 @@ int hexDigitValue(unsigned char digit) { } //-------------------------------------- -int UnicodeRefCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int UnicodeRefCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { unsigned char ch; - UnicodeChar uniCh; + UnicodeChar uniCh; bytesRead = 0; if (!is.getNext(ch)) return 0; bytesRead = 1; - uniCh = ch; + uniCh = ch; if (ch == '&') { if (is.peekNext(ch) && ch == '#') { is.getNext(ch); @@ -348,8 +369,8 @@ int UnicodeRefCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte if (!is.eos()) { is.peekNext(ch); if (ch != 'x' && ch != 'X') { - UKWORD code = 0; - int digits = 0; + UKWORD code = 0; + int digits = 0; while (is.peekNext(ch) && isdigit(ch) && digits < 5) { is.getNext(ch); bytesRead++; @@ -364,8 +385,8 @@ int UnicodeRefCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte } else { is.getNext(ch); bytesRead++; - UKWORD code = 0; - int digits = 0; + UKWORD code = 0; + int digits = 0; while (is.peekNext(ch) && isxdigit(ch) && digits < 4) { is.getNext(ch); bytesRead++; @@ -383,8 +404,9 @@ int UnicodeRefCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte } // translate to StdVnChar - UKDWORD key = uniCh; - UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -393,12 +415,15 @@ int UnicodeRefCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte } //-------------------------------- -int UnicodeRefCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeRefCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 128) { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else { outLen = 2; os.putB((UKBYTE)'&'); @@ -426,12 +451,15 @@ int UnicodeRefCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen #define HEX_DIGIT(x) ((x < 10) ? ('0' + x) : ('A' + x - 10)) //-------------------------------- -int UnicodeHexCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeHexCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 256) { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else { outLen = 3; os.putB('&'); @@ -439,7 +467,7 @@ int UnicodeHexCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen os.putB('x'); int i, digit; - int prev = 0; + int prev = 0; int shifts = 12; for (i = 0; i < 4; i++) { @@ -460,25 +488,24 @@ int UnicodeHexCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen ///////////////////////////////// // Class UnicodeCStringCharset / ///////////////////////////////// -void UnicodeCStringCharset::startInput() { - m_prevIsHex = 0; -} +void UnicodeCStringCharset::startInput() { m_prevIsHex = 0; } //---------------------------------------- -int UnicodeCStringCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int UnicodeCStringCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { unsigned char ch; - UnicodeChar uniCh; + UnicodeChar uniCh; bytesRead = 0; if (!is.getNext(ch)) return 0; bytesRead = 1; - uniCh = ch; + uniCh = ch; if (ch == '\\') { if (is.peekNext(ch) && (ch == 'x' || ch == 'X')) { is.getNext(ch); bytesRead++; - UKWORD code = 0; - int digits = 0; + UKWORD code = 0; + int digits = 0; while (is.peekNext(ch) && isxdigit(ch) && digits < 4) { is.getNext(ch); bytesRead++; @@ -490,8 +517,9 @@ int UnicodeCStringCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& } // translate to StdVnChar - UKDWORD key = uniCh; - UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); + UKDWORD key = uniCh; + UKDWORD *pChar = (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); if (pChar) stdChar = VnStdCharOffset + HIWORD(*pChar); else @@ -500,19 +528,22 @@ int UnicodeCStringCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& } //------------------------------------ -int UnicodeCStringCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { - UnicodeChar uChar = (stdChar < VnStdCharOffset) ? (UnicodeChar)stdChar : m_toUnicode[stdChar - VnStdCharOffset]; - int ret; +int UnicodeCStringCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { + UnicodeChar uChar = (stdChar < VnStdCharOffset) + ? (UnicodeChar)stdChar + : m_toUnicode[stdChar - VnStdCharOffset]; + int ret; if (uChar < 128 && !isxdigit(uChar) && uChar != 'x' && uChar != 'X') { outLen = 1; - ret = os.putB((UKBYTE)uChar); + ret = os.putB((UKBYTE)uChar); } else { outLen = 2; os.putB('\\'); os.putB('x'); int i, digit; - int prev = 0; + int prev = 0; int shifts = 12; for (i = 0; i < 4; i++) { @@ -524,7 +555,7 @@ int UnicodeCStringCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& ou } shifts -= 4; } - ret = os.isOK(); + ret = os.isOK(); m_prevIsHex = 1; } return ret; @@ -533,7 +564,7 @@ int UnicodeCStringCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& ou ///////////////////////////////// // Double-byte charsets // ///////////////////////////////// -DoubleByteCharset::DoubleByteCharset(UKWORD* vnChars) { +DoubleByteCharset::DoubleByteCharset(UKWORD *vnChars) { m_toDoubleChar = vnChars; memset(m_stdMap, 0, 256 * sizeof(UKWORD)); for (int i = 0; i < TOTAL_VNCHARS; i++) { @@ -541,13 +572,15 @@ DoubleByteCharset::DoubleByteCharset(UKWORD* vnChars) { m_stdMap[vnChars[i] >> 8] = 0xFFFF; // INVALID_STD_CHAR; else if (m_stdMap[vnChars[i]] == 0) m_stdMap[vnChars[i]] = i + 1; - m_vnChars[i] = (i << 16) + vnChars[i]; // high word is used for StdChar index + m_vnChars[i] = + (i << 16) + vnChars[i]; // high word is used for StdChar index } qsort(m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); } //--------------------------------------------- -int DoubleByteCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int DoubleByteCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { unsigned char ch; // read first byte @@ -555,7 +588,7 @@ int DoubleByteCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte if (!is.getNext(ch)) return 0; bytesRead = 1; - stdChar = m_stdMap[ch]; + stdChar = m_stdMap[ch]; if (stdChar == 0) stdChar = ch; else if (stdChar == 0xFFFF) @@ -565,10 +598,12 @@ int DoubleByteCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte UKBYTE hi; if (is.peekNext(hi) && hi > 0) { // test if a double-byte character is encountered - UKDWORD key = MAKEWORD(ch, hi); - UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, TOTAL_VNCHARS, sizeof(UKDWORD), wideCharCompare); + UKDWORD key = MAKEWORD(ch, hi); + UKDWORD *pChar = + (UKDWORD *)bsearch(&key, m_vnChars, TOTAL_VNCHARS, + sizeof(UKDWORD), wideCharCompare); if (pChar) { - stdChar = VnStdCharOffset + HIWORD(*pChar); + stdChar = VnStdCharOffset + HIWORD(*pChar); bytesRead = 2; is.getNext(hi); } @@ -578,7 +613,8 @@ int DoubleByteCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& byte } //--------------------------------------------- -int DoubleByteCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { +int DoubleByteCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { int ret; if (stdChar >= VnStdCharOffset) { UKWORD wCh = m_toDoubleChar[stdChar - VnStdCharOffset]; @@ -592,7 +628,7 @@ int DoubleByteCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen if (m_stdMap[b] == 0xFFFF) b = PadChar; outLen = 1; - ret = os.putB(b); + ret = os.putB(b); } /* outLen = 1; @@ -605,10 +641,10 @@ int DoubleByteCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen } else { if (stdChar > 255 || m_stdMap[stdChar]) { outLen = 1; - ret = os.putB((UKBYTE)PadChar); + ret = os.putB((UKBYTE)PadChar); } else { outLen = 1; - ret = os.putB((UKBYTE)stdChar); + ret = os.putB((UKBYTE)stdChar); } } return ret; @@ -620,13 +656,14 @@ int DoubleByteCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen unsigned char VIQRTones[] = {'\'', '`', '?', '~', '.'}; -const char* VIQREscapes[] = {"://", "/", "@", "mailto:", "email:", "news:", "www", "ftp"}; +const char *VIQREscapes[] = { + "://", "/", "@", "mailto:", "email:", "news:", "www", "ftp"}; -const int VIQREscCount = sizeof(VIQREscapes) / sizeof(char*); +const int VIQREscCount = sizeof(VIQREscapes) / sizeof(char *); -VIQRCharset::VIQRCharset(UKDWORD* vnChars) { +VIQRCharset::VIQRCharset(UKDWORD *vnChars) { memset(m_stdMap, 0, 256 * sizeof(UKWORD)); - int i; + int i; UKDWORD dw; m_vnChars = vnChars; for (i = 0; i < TOTAL_VNCHARS; i++) { @@ -639,11 +676,11 @@ VIQRCharset::VIQRCharset(UKDWORD* vnChars) { // set offset from base characters according to tone marks m_stdMap[(unsigned char)'\''] = 2; - m_stdMap[(unsigned char)'`'] = 4; - m_stdMap[(unsigned char)'?'] = 6; - m_stdMap[(unsigned char)'~'] = 8; - m_stdMap[(unsigned char)'.'] = 10; - m_stdMap[(unsigned char)'^'] = 12; + m_stdMap[(unsigned char)'`'] = 4; + m_stdMap[(unsigned char)'?'] = 6; + m_stdMap[(unsigned char)'~'] = 8; + m_stdMap[(unsigned char)'.'] = 10; + m_stdMap[(unsigned char)'^'] = 12; m_stdMap[(unsigned char)'('] = 24; m_stdMap[(unsigned char)'+'] = 26; @@ -652,23 +689,24 @@ VIQRCharset::VIQRCharset(UKDWORD* vnChars) { //--------------------------------------------------- void VIQRCharset::startInput() { - m_suspicious = 0; + m_suspicious = 0; m_atWordBeginning = 1; - m_gotTone = 0; - m_escAll = 0; + m_gotTone = 0; + m_escAll = 0; if (VnCharsetLibObj.m_options.viqrEsc) VnCharsetLibObj.m_VIQREscPatterns.reset(); } //--------------------------------------------------- -int VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { unsigned char ch1; bytesRead = 0; if (!is.getNext(ch1)) return 0; bytesRead = 1; - stdChar = m_stdMap[ch1]; + stdChar = m_stdMap[ch1]; if (VnCharsetLibObj.m_options.viqrEsc) { if (VnCharsetLibObj.m_VIQREscPatterns.foundAtNextChar(ch1) != -1) { @@ -694,29 +732,40 @@ int VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) unsigned char ch2; is.peekNext(ch2); unsigned char upper = toupper(ch1); - if ((!VnCharsetLibObj.m_options.smartViqr || m_atWordBeginning) && upper == 'D' && (ch2 == 'd' || ch2 == 'D')) { + if ((!VnCharsetLibObj.m_options.smartViqr || m_atWordBeginning) && + upper == 'D' && (ch2 == 'd' || ch2 == 'D')) { is.getNext(ch2); bytesRead++; stdChar += 2; // dd is 2 positions after d. } else { StdVnChar index = m_stdMap[ch2]; - int cond; + int cond; if (m_suspicious) { - cond = IS_VOWEL(ch1) && - (index == 2 || index == 4 || index == 8 || // not accepting ? . in suspicious mode - (index == 12 && (upper == 'A' || upper == 'E' || upper == 'O')) || (m_stdMap[ch2] == 24 && upper == 'A') || + cond = + IS_VOWEL(ch1) && + (index == 2 || index == 4 || + index == 8 || // not accepting ? . in suspicious mode + (index == 12 && + (upper == 'A' || upper == 'E' || upper == 'O')) || + (m_stdMap[ch2] == 24 && upper == 'A') || (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); if (cond) m_suspicious = 0; } else - cond = IS_VOWEL(ch1) && - ((index <= 10 && index > 0 && (!m_gotTone || (index != 6 && index != 10))) || (index == 12 && (upper == 'A' || upper == 'E' || upper == 'O')) || - (m_stdMap[ch2] == 24 && upper == 'A') || (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); + cond = + IS_VOWEL(ch1) && + ((index <= 10 && index > 0 && + (!m_gotTone || (index != 6 && index != 10))) || + (index == 12 && + (upper == 'A' || upper == 'E' || upper == 'O')) || + (m_stdMap[ch2] == 24 && upper == 'A') || + (m_stdMap[ch2] == 26 && (upper == 'O' || upper == 'U'))); if (cond) { if (index > 0) - m_gotTone = 1; // we have a tone/breve/hook in the current word + m_gotTone = + 1; // we have a tone/breve/hook in the current word // ok, take this byte is.getNext(ch2); @@ -729,7 +778,8 @@ int VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) stdChar += offset; // check next byte if (is.peekNext(ch2)) { - if (index > 10 && m_stdMap[ch2] > 0 && m_stdMap[ch2] <= 10) { + if (index > 10 && m_stdMap[ch2] > 0 && + m_stdMap[ch2] <= 10) { // ok, take one more byte is.getNext(ch2); bytesRead++; @@ -741,7 +791,8 @@ int VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) } m_atWordBeginning = (stdChar < 256); if (stdChar < 256) { - m_gotTone = 0; // reset this flag because we are at the beginning of a new word + m_gotTone = + 0; // reset this flag because we are at the beginning of a new word } // adjust stdChar @@ -756,22 +807,22 @@ void VIQRCharset::startOutput() { m_escapeRoof = 0; m_escapeHook = 0; m_escapeTone = 0; - m_noOutEsc = 0; + m_noOutEsc = 0; VnCharsetLibObj.m_VIQROutEscPatterns.reset(); } //--------------------------------------------------- -int VIQRCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { - int ret; +int VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) { + int ret; UKBYTE b; if (stdChar >= VnStdCharOffset) { - outLen = 1; - UKDWORD dw = m_vnChars[stdChar - VnStdCharOffset]; + outLen = 1; + UKDWORD dw = m_vnChars[stdChar - VnStdCharOffset]; - unsigned char first = (unsigned char)dw; + unsigned char first = (unsigned char)dw; unsigned char firstUpper = toupper(first); - b = (UKBYTE)dw; + b = (UKBYTE)dw; ret = os.putB(b); if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar(b) != -1) m_noOutEsc = 1; @@ -788,7 +839,7 @@ int VIQRCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { if (dw & 0x00FF0000) { // third byte is present outLen++; - ret = os.putB((UKBYTE)(dw >> 16)); + ret = os.putB((UKBYTE)(dw >> 16)); m_escapeTone = 0; } else { UKWORD index = m_stdMap[second]; @@ -804,28 +855,34 @@ int VIQRCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { m_escapeTone = IS_VOWEL(first); m_escapeBowl = (firstUpper == 'A'); m_escapeHook = (firstUpper == 'U' || firstUpper == 'O'); - m_escapeRoof = (firstUpper == 'A' || firstUpper == 'E' || firstUpper == 'O'); + m_escapeRoof = + (firstUpper == 'A' || firstUpper == 'E' || firstUpper == 'O'); } } else { if (stdChar > 255) { outLen = 1; - ret = os.putB((UKBYTE)PadChar); - if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar((UKBYTE)PadChar) != -1) + ret = os.putB((UKBYTE)PadChar); + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar( + (UKBYTE)PadChar) != -1) m_noOutEsc = 1; } else { - outLen = 1; + outLen = 1; UKWORD index = m_stdMap[stdChar]; if (!VnCharsetLibObj.m_options.viqrMixed && !m_noOutEsc && - (stdChar == '\\' || (index > 0 && index <= 10 && m_escapeTone) || (index == 12 && m_escapeRoof) || (index == 24 && m_escapeBowl) || + (stdChar == '\\' || + (index > 0 && index <= 10 && m_escapeTone) || + (index == 12 && m_escapeRoof) || + (index == 24 && m_escapeBowl) || (index == 26 && m_escapeHook))) { //(m_stdMap[stdChar] > 0 && m_stdMap[stdChar] <= 26)) { // tone mark, needs an escape character outLen++; ret = os.putB('\\'); - if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar('\\') != -1) + if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar( + '\\') != -1) m_noOutEsc = 1; } - b = (UKBYTE)stdChar; + b = (UKBYTE)stdChar; ret = os.putB(b); if (VnCharsetLibObj.m_VIQROutEscPatterns.foundAtNextChar(b) != -1) m_noOutEsc = 1; @@ -846,8 +903,8 @@ int VIQRCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { ///////////////////////////////////////////// //----------------------------------------- -UTF8VIQRCharset::UTF8VIQRCharset(UnicodeUTF8Charset* pUtf, VIQRCharset* pViqr) { - m_pUtf = pUtf; +UTF8VIQRCharset::UTF8VIQRCharset(UnicodeUTF8Charset *pUtf, VIQRCharset *pViqr) { + m_pUtf = pUtf; m_pViqr = pViqr; } @@ -864,7 +921,8 @@ void UTF8VIQRCharset::startOutput() { } //----------------------------------------- -int UTF8VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int UTF8VIQRCharset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { UKBYTE ch; if (!is.peekNext(ch)) @@ -880,7 +938,8 @@ int UTF8VIQRCharset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesR } //----------------------------------------- -int UTF8VIQRCharset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { +int UTF8VIQRCharset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { return m_pViqr->putChar(os, stdChar, outLen); } @@ -905,15 +964,15 @@ CVnCharsetLib::CVnCharsetLib() { HiVowel['U' - 'A'] = 1; HiVowel['Y' - 'A'] = 1; - m_pUniCharset = NULL; + m_pUniCharset = NULL; m_pUniCompCharset = NULL; - m_pUniUTF8 = NULL; - m_pUniRef = NULL; - m_pUniHex = NULL; - m_pVIQRCharObj = NULL; - m_pUVIQRCharObj = NULL; - m_pWinCP1258 = NULL; - m_pVnIntCharset = NULL; + m_pUniUTF8 = NULL; + m_pUniRef = NULL; + m_pUniHex = NULL; + m_pVIQRCharObj = NULL; + m_pUVIQRCharObj = NULL; + m_pWinCP1258 = NULL; + m_pVnIntCharset = NULL; int i; for (i = 0; i < CONV_TOTAL_SINGLE_CHARSETS; i++) @@ -923,8 +982,8 @@ CVnCharsetLib::CVnCharsetLib() { m_dbCharsets[i] = NULL; VnConvResetOptions(&m_options); - m_VIQREscPatterns.init((char**)VIQREscapes, VIQREscCount); - m_VIQROutEscPatterns.init((char**)VIQREscapes, VIQREscCount); + m_VIQREscPatterns.init((char **)VIQREscapes, VIQREscCount); + m_VIQROutEscPatterns.init((char **)VIQREscapes, VIQREscCount); } //----------------------------------------- @@ -959,104 +1018,106 @@ CVnCharsetLib::~CVnCharsetLib() { } //----------------------------------------- -VnCharset* CVnCharsetLib::getVnCharset(int charsetIdx) { +VnCharset *CVnCharsetLib::getVnCharset(int charsetIdx) { switch (charsetIdx) { - case CONV_CHARSET_UNICODE: - if (m_pUniCharset == NULL) - m_pUniCharset = new UnicodeCharset(UnicodeTable); - return m_pUniCharset; - case CONV_CHARSET_UNIDECOMPOSED: - if (m_pUniCompCharset == NULL) - m_pUniCompCharset = new UnicodeCompCharset(UnicodeTable, UnicodeComposite); - return m_pUniCompCharset; - case CONV_CHARSET_UNIUTF8: - case CONV_CHARSET_XUTF8: - if (m_pUniUTF8 == NULL) - m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); - return m_pUniUTF8; - - case CONV_CHARSET_UNIREF: - if (m_pUniRef == NULL) - m_pUniRef = new UnicodeRefCharset(UnicodeTable); - return m_pUniRef; - - case CONV_CHARSET_UNIREF_HEX: - if (m_pUniHex == NULL) - m_pUniHex = new UnicodeHexCharset(UnicodeTable); - return m_pUniHex; - - case CONV_CHARSET_UNI_CSTRING: - if (m_pUniCString == NULL) - m_pUniCString = new UnicodeCStringCharset(UnicodeTable); - return m_pUniCString; - - case CONV_CHARSET_WINCP1258: - if (m_pWinCP1258 == NULL) - m_pWinCP1258 = new WinCP1258Charset(WinCP1258, WinCP1258Pre); - return m_pWinCP1258; - - case CONV_CHARSET_VIQR: + case CONV_CHARSET_UNICODE: + if (m_pUniCharset == NULL) + m_pUniCharset = new UnicodeCharset(UnicodeTable); + return m_pUniCharset; + case CONV_CHARSET_UNIDECOMPOSED: + if (m_pUniCompCharset == NULL) + m_pUniCompCharset = + new UnicodeCompCharset(UnicodeTable, UnicodeComposite); + return m_pUniCompCharset; + case CONV_CHARSET_UNIUTF8: + case CONV_CHARSET_XUTF8: + if (m_pUniUTF8 == NULL) + m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); + return m_pUniUTF8; + + case CONV_CHARSET_UNIREF: + if (m_pUniRef == NULL) + m_pUniRef = new UnicodeRefCharset(UnicodeTable); + return m_pUniRef; + + case CONV_CHARSET_UNIREF_HEX: + if (m_pUniHex == NULL) + m_pUniHex = new UnicodeHexCharset(UnicodeTable); + return m_pUniHex; + + case CONV_CHARSET_UNI_CSTRING: + if (m_pUniCString == NULL) + m_pUniCString = new UnicodeCStringCharset(UnicodeTable); + return m_pUniCString; + + case CONV_CHARSET_WINCP1258: + if (m_pWinCP1258 == NULL) + m_pWinCP1258 = new WinCP1258Charset(WinCP1258, WinCP1258Pre); + return m_pWinCP1258; + + case CONV_CHARSET_VIQR: + if (m_pVIQRCharObj == NULL) + m_pVIQRCharObj = new VIQRCharset(VIQRTable); + return m_pVIQRCharObj; + + case CONV_CHARSET_VNSTANDARD: + if (m_pVnIntCharset == NULL) + m_pVnIntCharset = new VnInternalCharset(); + return m_pVnIntCharset; + + case CONV_CHARSET_UTF8VIQR: + if (m_pUVIQRCharObj == NULL) { if (m_pVIQRCharObj == NULL) m_pVIQRCharObj = new VIQRCharset(VIQRTable); - return m_pVIQRCharObj; - case CONV_CHARSET_VNSTANDARD: - if (m_pVnIntCharset == NULL) - m_pVnIntCharset = new VnInternalCharset(); - return m_pVnIntCharset; - - case CONV_CHARSET_UTF8VIQR: - if (m_pUVIQRCharObj == NULL) { - if (m_pVIQRCharObj == NULL) - m_pVIQRCharObj = new VIQRCharset(VIQRTable); - - if (m_pUniUTF8 == NULL) - m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); - m_pUVIQRCharObj = new UTF8VIQRCharset(m_pUniUTF8, m_pVIQRCharObj); - } - return m_pUVIQRCharObj; - - default: - if (IS_SINGLE_BYTE_CHARSET(charsetIdx)) { - int i = charsetIdx - CONV_CHARSET_TCVN3; - if (m_sgCharsets[i] == NULL) - m_sgCharsets[i] = new SingleByteCharset(SingleByteTables[i]); - return m_sgCharsets[i]; - } else if (IS_DOUBLE_BYTE_CHARSET(charsetIdx)) { - int i = charsetIdx - CONV_CHARSET_VNIWIN; - if (m_dbCharsets[i] == NULL) - m_dbCharsets[i] = new DoubleByteCharset(DoubleByteTables[i]); - return m_dbCharsets[i]; - } + if (m_pUniUTF8 == NULL) + m_pUniUTF8 = new UnicodeUTF8Charset(UnicodeTable); + m_pUVIQRCharObj = new UTF8VIQRCharset(m_pUniUTF8, m_pVIQRCharObj); + } + return m_pUVIQRCharObj; + + default: + if (IS_SINGLE_BYTE_CHARSET(charsetIdx)) { + int i = charsetIdx - CONV_CHARSET_TCVN3; + if (m_sgCharsets[i] == NULL) + m_sgCharsets[i] = new SingleByteCharset(SingleByteTables[i]); + return m_sgCharsets[i]; + } else if (IS_DOUBLE_BYTE_CHARSET(charsetIdx)) { + int i = charsetIdx - CONV_CHARSET_VNIWIN; + if (m_dbCharsets[i] == NULL) + m_dbCharsets[i] = new DoubleByteCharset(DoubleByteTables[i]); + return m_dbCharsets[i]; + } } return NULL; } //------------------------------------------------- -DllExport void VnConvSetOptions(VnConvOptions* pOptions) { +DllExport void VnConvSetOptions(VnConvOptions *pOptions) { VnCharsetLibObj.m_options = *pOptions; } //------------------------------------------------- -DllExport void VnConvGetOptions(VnConvOptions* pOptions) { +DllExport void VnConvGetOptions(VnConvOptions *pOptions) { *pOptions = VnCharsetLibObj.m_options; } //------------------------------------------------- -DllExport void VnConvResetOptions(VnConvOptions* pOptions) { - pOptions->viqrEsc = 1; - pOptions->viqrMixed = 0; - pOptions->toUpper = 0; - pOptions->toLower = 0; +DllExport void VnConvResetOptions(VnConvOptions *pOptions) { + pOptions->viqrEsc = 1; + pOptions->viqrMixed = 0; + pOptions->toUpper = 0; + pOptions->toLower = 0; pOptions->removeTone = 0; - pOptions->smartViqr = 1; + pOptions->smartViqr = 1; } ///////////////////////////////////////////// // Class WinCP1258Charset ///////////////////////////////////////////// -WinCP1258Charset::WinCP1258Charset(UKWORD* compositeChars, UKWORD* precomposedChars) { +WinCP1258Charset::WinCP1258Charset(UKWORD *compositeChars, + UKWORD *precomposedChars) { int i, k; m_toDoubleChar = compositeChars; memset(m_stdMap, 0, 256 * sizeof(UKWORD)); @@ -1068,7 +1129,8 @@ WinCP1258Charset::WinCP1258Charset(UKWORD* compositeChars, UKWORD* precomposedCh else if (m_stdMap[compositeChars[i]] == 0) m_stdMap[compositeChars[i]] = i + 1; - m_vnChars[i] = (i << 16) + compositeChars[i]; // high word is used for StdChar index + m_vnChars[i] = (i << 16) + + compositeChars[i]; // high word is used for StdChar index } m_totalChars = TOTAL_VNCHARS; @@ -1076,8 +1138,9 @@ WinCP1258Charset::WinCP1258Charset(UKWORD* compositeChars, UKWORD* precomposedCh // add precomposed chars to the table for (k = 0, i = TOTAL_VNCHARS; k < TOTAL_VNCHARS; k++) if (precomposedChars[k] != compositeChars[k]) { - if (precomposedChars[k] >> 8) // a 2-byte character - m_stdMap[precomposedChars[k] >> 8] = 0xFFFF; // INVALID_STD_CHAR; + if (precomposedChars[k] >> 8) // a 2-byte character + m_stdMap[precomposedChars[k] >> 8] = + 0xFFFF; // INVALID_STD_CHAR; else if (m_stdMap[precomposedChars[k]] == 0) m_stdMap[precomposedChars[k]] = k + 1; @@ -1093,7 +1156,8 @@ WinCP1258Charset::WinCP1258Charset(UKWORD* compositeChars, UKWORD* precomposedCh // This fuction is basically the same as that of DoubleByteCharset // with m_totalChars is used instead of constant TOTAL_VNCHARS //--------------------------------------------------------------------- -int WinCP1258Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) { +int WinCP1258Charset::nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) { unsigned char ch; // read first byte @@ -1101,7 +1165,7 @@ int WinCP1258Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytes if (!is.getNext(ch)) return 0; bytesRead = 1; - stdChar = m_stdMap[ch]; + stdChar = m_stdMap[ch]; if (stdChar == 0) stdChar = ch; else if (stdChar == 0xFFFF) @@ -1111,10 +1175,12 @@ int WinCP1258Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytes UKBYTE hi; if (is.peekNext(hi) && hi > 0) { // test if a double-byte character is encountered - UKDWORD key = MAKEWORD(ch, hi); - UKDWORD* pChar = (UKDWORD*)bsearch(&key, m_vnChars, m_totalChars, sizeof(UKDWORD), wideCharCompare); + UKDWORD key = MAKEWORD(ch, hi); + UKDWORD *pChar = + (UKDWORD *)bsearch(&key, m_vnChars, m_totalChars, + sizeof(UKDWORD), wideCharCompare); if (pChar) { - stdChar = VnStdCharOffset + HIWORD(*pChar); + stdChar = VnStdCharOffset + HIWORD(*pChar); bytesRead = 2; is.getNext(hi); } @@ -1126,7 +1192,8 @@ int WinCP1258Charset::nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytes //--------------------------------------------------------------------- // This fuction is exactly the same as that of DoubleByteCharset //--------------------------------------------------------------------- -int WinCP1258Charset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) { +int WinCP1258Charset::putChar(ByteOutStream &os, StdVnChar stdChar, + int &outLen) { int ret; if (stdChar >= VnStdCharOffset) { UKWORD wCh = m_toDoubleChar[stdChar - VnStdCharOffset]; @@ -1140,32 +1207,34 @@ int WinCP1258Charset::putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) if (m_stdMap[b] == 0xFFFF) b = PadChar; outLen = 1; - ret = os.putB(b); + ret = os.putB(b); } } else { if (stdChar > 255 || m_stdMap[stdChar]) { outLen = 1; - ret = os.putB((UKBYTE)PadChar); + ret = os.putB((UKBYTE)PadChar); } else { outLen = 1; - ret = os.putB((UKBYTE)stdChar); + ret = os.putB((UKBYTE)stdChar); } } return ret; } -#define IS_ODD(x) (x & 1) +#define IS_ODD(x) (x & 1) #define IS_EVEN(x) (!(x & 1)) StdVnChar StdVnToUpper(StdVnChar ch) { - if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(ch)) + if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && + IS_ODD(ch)) ch -= 1; return ch; } //---------------------------------------- StdVnChar StdVnToLower(StdVnChar ch) { - if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(ch)) + if (ch >= VnStdCharOffset && ch < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && + IS_EVEN(ch)) ch += 1; return ch; } diff --git a/unikey/core/charset.h b/unikey/core/charset.h index f588a680..f40d32b6 100644 --- a/unikey/core/charset.h +++ b/unikey/core/charset.h @@ -27,7 +27,7 @@ #include "pattern.h" #include "vnconv.h" -#define TOTAL_VNCHARS 213 +#define TOTAL_VNCHARS 213 #define TOTAL_ALPHA_VNCHARS 186 #if defined(_WIN32) @@ -60,21 +60,22 @@ typedef uint32_t UKDWORD; #define MAKEWORD(a, b) ((UKWORD)(((UKBYTE)(a)) | ((UKWORD)((UKBYTE)(b))) << 8)) #endif -const StdVnChar VnStdCharOffset = 0x10000; +const StdVnChar VnStdCharOffset = 0x10000; const StdVnChar INVALID_STD_CHAR = 0xFFFFFFFF; // const unsigned char PadChar = '?'; //? is used for VIQR charset -const unsigned char PadChar = '#'; +const unsigned char PadChar = '#'; const unsigned char PadStartQuote = '\"'; -const unsigned char PadEndQuote = '\"'; -const unsigned char PadEllipsis = '.'; +const unsigned char PadEndQuote = '\"'; +const unsigned char PadEllipsis = '.'; -class DllInterface VnCharset { - public: +class DllInterface VnCharset { +public: virtual void startInput() {} virtual void startOutput() {} // virtual UKBYTE *nextInput(UKBYTE *input, int inLen, StdVnChar & stdChar, // int & bytesRead) = 0; - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead) = 0; + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, + int &bytesRead) = 0; //------------------------------------------------------------------------ // put a character to the output after converting it @@ -85,207 +86,208 @@ class DllInterface VnCharset { // maxAvail[in]: max length available. // Returns: next position in output //------------------------------------------------------------------------ - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen) = 0; + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen) = 0; virtual int elementSize(); virtual ~VnCharset() {} }; //-------------------------------------------------- class SingleByteCharset : public VnCharset { - protected: - UKWORD m_stdMap[256]; - unsigned char* m_vnChars; - - public: - SingleByteCharset(unsigned char* vnChars); - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); +protected: + UKWORD m_stdMap[256]; + unsigned char *m_vnChars; + +public: + SingleByteCharset(unsigned char *vnChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- class VnInternalCharset : public VnCharset { - public: +public: VnInternalCharset() {} - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); virtual int elementSize(); }; //-------------------------------------------------- class UnicodeCharset : public VnCharset { - protected: - UKDWORD m_vnChars[TOTAL_VNCHARS]; - UnicodeChar* m_toUnicode; - - public: - UnicodeCharset(UnicodeChar* vnChars); - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); +protected: + UKDWORD m_vnChars[TOTAL_VNCHARS]; + UnicodeChar *m_toUnicode; + +public: + UnicodeCharset(UnicodeChar *vnChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); virtual int elementSize(); }; //-------------------------------------------------- class DoubleByteCharset : public VnCharset { - protected: - UKWORD m_stdMap[256]; +protected: + UKWORD m_stdMap[256]; UKDWORD m_vnChars[TOTAL_VNCHARS]; - UKWORD* m_toDoubleChar; + UKWORD *m_toDoubleChar; - public: - DoubleByteCharset(UKWORD* vnChars); - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); +public: + DoubleByteCharset(UKWORD *vnChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- class UnicodeUTF8Charset : public UnicodeCharset { - public: - UnicodeUTF8Charset(UnicodeChar* vnChars) : UnicodeCharset(vnChars) {} +public: + UnicodeUTF8Charset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- class UnicodeRefCharset : public UnicodeCharset { - public: - UnicodeRefCharset(UnicodeChar* vnChars) : UnicodeCharset(vnChars) {} +public: + UnicodeRefCharset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- class UnicodeHexCharset : public UnicodeRefCharset { - public: - UnicodeHexCharset(UnicodeChar* vnChars) : UnicodeRefCharset(vnChars) {} - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); +public: + UnicodeHexCharset(UnicodeChar *vnChars) : UnicodeRefCharset(vnChars) {} + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- class UnicodeCStringCharset : public UnicodeCharset { - protected: +protected: int m_prevIsHex; - public: - UnicodeCStringCharset(UnicodeChar* vnChars) : UnicodeCharset(vnChars) {} - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); +public: + UnicodeCStringCharset(UnicodeChar *vnChars) : UnicodeCharset(vnChars) {} + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); virtual void startInput(); }; //-------------------------------------------------- class WinCP1258Charset : public VnCharset { - protected: - UKWORD m_stdMap[256]; +protected: + UKWORD m_stdMap[256]; UKDWORD m_vnChars[TOTAL_VNCHARS * 2]; - UKWORD* m_toDoubleChar; - int m_totalChars; + UKWORD *m_toDoubleChar; + int m_totalChars; - public: - WinCP1258Charset(UKWORD* compositeChars, UKWORD* precomposedChars); - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); +public: + WinCP1258Charset(UKWORD *compositeChars, UKWORD *precomposedChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- struct UniCompCharInfo { UKDWORD compChar; - int stdIndex; + int stdIndex; }; class UnicodeCompCharset : public VnCharset { - protected: +protected: UniCompCharInfo m_info[TOTAL_VNCHARS * 2]; - UKDWORD* m_uniCompChars; - int m_totalChars; + UKDWORD *m_uniCompChars; + int m_totalChars; - public: - UnicodeCompCharset(UnicodeChar* uniChars, UKDWORD* uniCompChars); - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); +public: + UnicodeCompCharset(UnicodeChar *uniChars, UKDWORD *uniCompChars); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); virtual int elementSize(); }; //-------------------------------------------------- class VIQRCharset : public VnCharset { - protected: - UKDWORD* m_vnChars; - UKWORD m_stdMap[256]; - int m_atWordBeginning; - int m_escapeBowl; - int m_escapeRoof; - int m_escapeHook; - int m_escapeTone; - int m_gotTone; - int m_escAll; - int m_noOutEsc; - - public: +protected: + UKDWORD *m_vnChars; + UKWORD m_stdMap[256]; + int m_atWordBeginning; + int m_escapeBowl; + int m_escapeRoof; + int m_escapeHook; + int m_escapeTone; + int m_gotTone; + int m_escAll; + int m_noOutEsc; + +public: int m_suspicious; - VIQRCharset(UKDWORD* vnChars); + VIQRCharset(UKDWORD *vnChars); virtual void startInput(); virtual void startOutput(); - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- class UTF8VIQRCharset : public VnCharset { - protected: - VIQRCharset* m_pViqr; - UnicodeUTF8Charset* m_pUtf; +protected: + VIQRCharset *m_pViqr; + UnicodeUTF8Charset *m_pUtf; - public: - UTF8VIQRCharset(UnicodeUTF8Charset* pUtf, VIQRCharset* pViqr); +public: + UTF8VIQRCharset(UnicodeUTF8Charset *pUtf, VIQRCharset *pViqr); virtual void startInput(); virtual void startOutput(); - virtual int nextInput(ByteInStream& is, StdVnChar& stdChar, int& bytesRead); - virtual int putChar(ByteOutStream& os, StdVnChar stdChar, int& outLen); + virtual int nextInput(ByteInStream &is, StdVnChar &stdChar, int &bytesRead); + virtual int putChar(ByteOutStream &os, StdVnChar stdChar, int &outLen); }; //-------------------------------------------------- class DllInterface CVnCharsetLib { - protected: - SingleByteCharset* m_sgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; - DoubleByteCharset* m_dbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; - UnicodeCharset* m_pUniCharset; - UnicodeCompCharset* m_pUniCompCharset; - UnicodeUTF8Charset* m_pUniUTF8; - UnicodeRefCharset* m_pUniRef; - UnicodeHexCharset* m_pUniHex; - VIQRCharset* m_pVIQRCharObj; - UTF8VIQRCharset* m_pUVIQRCharObj; - WinCP1258Charset* m_pWinCP1258; - UnicodeCStringCharset* m_pUniCString; - VnInternalCharset* m_pVnIntCharset; - - public: - PatternList m_VIQREscPatterns, m_VIQROutEscPatterns; +protected: + SingleByteCharset *m_sgCharsets[CONV_TOTAL_SINGLE_CHARSETS]; + DoubleByteCharset *m_dbCharsets[CONV_TOTAL_DOUBLE_CHARSETS]; + UnicodeCharset *m_pUniCharset; + UnicodeCompCharset *m_pUniCompCharset; + UnicodeUTF8Charset *m_pUniUTF8; + UnicodeRefCharset *m_pUniRef; + UnicodeHexCharset *m_pUniHex; + VIQRCharset *m_pVIQRCharObj; + UTF8VIQRCharset *m_pUVIQRCharObj; + WinCP1258Charset *m_pWinCP1258; + UnicodeCStringCharset *m_pUniCString; + VnInternalCharset *m_pVnIntCharset; + +public: + PatternList m_VIQREscPatterns, m_VIQROutEscPatterns; VnConvOptions m_options; CVnCharsetLib(); ~CVnCharsetLib(); - VnCharset* getVnCharset(int charsetIdx); + VnCharset *getVnCharset(int charsetIdx); }; -extern unsigned char SingleByteTables[][TOTAL_VNCHARS]; -extern UKWORD DoubleByteTables[][TOTAL_VNCHARS]; -extern UnicodeChar UnicodeTable[TOTAL_VNCHARS]; -extern UKDWORD VIQRTable[TOTAL_VNCHARS]; -extern UKDWORD UnicodeComposite[TOTAL_VNCHARS]; -extern UKWORD WinCP1258[TOTAL_VNCHARS]; -extern UKWORD WinCP1258Pre[TOTAL_VNCHARS]; +extern unsigned char SingleByteTables[][TOTAL_VNCHARS]; +extern UKWORD DoubleByteTables[][TOTAL_VNCHARS]; +extern UnicodeChar UnicodeTable[TOTAL_VNCHARS]; +extern UKDWORD VIQRTable[TOTAL_VNCHARS]; +extern UKDWORD UnicodeComposite[TOTAL_VNCHARS]; +extern UKWORD WinCP1258[TOTAL_VNCHARS]; +extern UKWORD WinCP1258Pre[TOTAL_VNCHARS]; extern DllInterface CVnCharsetLib VnCharsetLibObj; -extern VnConvOptions VnConvGlobalOptions; -extern int StdVnNoTone[TOTAL_VNCHARS]; -extern int StdVnRootChar[TOTAL_VNCHARS]; +extern VnConvOptions VnConvGlobalOptions; +extern int StdVnNoTone[TOTAL_VNCHARS]; +extern int StdVnRootChar[TOTAL_VNCHARS]; -DllInterface int genConvert(VnCharset& incs, VnCharset& outcs, ByteInStream& input, ByteOutStream& output); +DllInterface int genConvert(VnCharset &incs, VnCharset &outcs, + ByteInStream &input, ByteOutStream &output); -StdVnChar StdVnToUpper(StdVnChar ch); -StdVnChar StdVnToLower(StdVnChar ch); -StdVnChar StdVnGetRoot(StdVnChar ch); +StdVnChar StdVnToUpper(StdVnChar ch); +StdVnChar StdVnToLower(StdVnChar ch); +StdVnChar StdVnGetRoot(StdVnChar ch); #endif diff --git a/unikey/core/convert.cpp b/unikey/core/convert.cpp index 34ceed08..79ca8623 100644 --- a/unikey/core/convert.cpp +++ b/unikey/core/convert.cpp @@ -16,11 +16,12 @@ #include "vnconv.h" -int vnFileStreamConvert(int inCharset, int outCharset, FILE* inf, FILE* outf); +int vnFileStreamConvert(int inCharset, int outCharset, FILE *inf, FILE *outf); -DllExport int genConvert(VnCharset& incs, VnCharset& outcs, ByteInStream& input, ByteOutStream& output) { +DllExport int genConvert(VnCharset &incs, VnCharset &outcs, ByteInStream &input, + ByteOutStream &output) { StdVnChar stdChar; - int bytesRead, bytesWritten; + int bytesRead, bytesWritten; incs.startInput(); outcs.startOutput(); @@ -64,18 +65,19 @@ DllExport int genConvert(VnCharset& incs, VnCharset& outcs, ByteInStream& input, // int VnConvert(int inCharset, int outCharset, UKBYTE *input, UKBYTE *output, // int & inLen, int & maxOutLen) -DllExport int VnConvert(int inCharset, int outCharset, UKBYTE* input, UKBYTE* output, int* pInLen, int* pMaxOutLen) { +DllExport int VnConvert(int inCharset, int outCharset, UKBYTE *input, + UKBYTE *output, int *pInLen, int *pMaxOutLen) { int inLen, maxOutLen; int ret = -1; - inLen = *pInLen; + inLen = *pInLen; maxOutLen = *pMaxOutLen; if (inLen != -1 && inLen < 0) // invalid inLen return ret; - VnCharset* pInCharset = VnCharsetLibObj.getVnCharset(inCharset); - VnCharset* pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); + VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset); + VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); if (!pInCharset || !pOutCharset) return VNCONV_INVALID_CHARSET; @@ -83,9 +85,9 @@ DllExport int VnConvert(int inCharset, int outCharset, UKBYTE* input, UKBYTE* ou StringBIStream is(input, inLen, pInCharset->elementSize()); StringBOStream os(output, maxOutLen); - ret = genConvert(*pInCharset, *pOutCharset, is, os); + ret = genConvert(*pInCharset, *pOutCharset, is, os); *pMaxOutLen = os.getOutBytes(); - *pInLen = is.left(); + *pInLen = is.left(); return ret; } @@ -97,11 +99,12 @@ DllExport int VnConvert(int inCharset, int outCharset, UKBYTE* input, UKBYTE* ou // 0: successful // errCode: if failed //--------------------------------------- -DllExport int VnFileConvert(int inCharset, int outCharset, const char* inFile, const char* outFile) { - FILE* inf = NULL; - FILE* outf = NULL; - int ret = 0; - char tmpName[32]; +DllExport int VnFileConvert(int inCharset, int outCharset, const char *inFile, + const char *outFile) { + FILE *inf = NULL; + FILE *outf = NULL; + int ret = 0; + char tmpName[32]; if (inFile == NULL) { inf = stdin; @@ -125,9 +128,9 @@ DllExport int VnFileConvert(int inCharset, int outCharset, const char* inFile, c strcpy(outDir, outFile); #if defined(_WIN32) - char* p = strrchr(outDir, '\\'); + char *p = strrchr(outDir, '\\'); #else - char* p = strrchr(outDir, '/'); + char *p = strrchr(outDir, '/'); #endif if (p == NULL) @@ -190,9 +193,9 @@ DllExport int VnFileConvert(int inCharset, int outCharset, const char* inFile, c // 0: successful // errCode: if failed //--------------------------------------- -int vnFileStreamConvert(int inCharset, int outCharset, FILE* inf, FILE* outf) { - VnCharset* pInCharset = VnCharsetLibObj.getVnCharset(inCharset); - VnCharset* pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); +int vnFileStreamConvert(int inCharset, int outCharset, FILE *inf, FILE *outf) { + VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset); + VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset); if (!pInCharset || !pOutCharset) return VNCONV_INVALID_CHARSET; @@ -211,11 +214,17 @@ int vnFileStreamConvert(int inCharset, int outCharset, FILE* inf, FILE* outf) { return genConvert(*pInCharset, *pOutCharset, is, os); } -const char* ErrTable[VNCONV_LAST_ERROR] = { - "No error", "Unknown error", "Invalid charset", "Error opening input file", "Error opening output file", "Error writing to output stream", "Not enough memory", +const char *ErrTable[VNCONV_LAST_ERROR] = { + "No error", + "Unknown error", + "Invalid charset", + "Error opening input file", + "Error opening output file", + "Error writing to output stream", + "Not enough memory", }; -DllExport const char* VnConvErrMsg(int errCode) { +DllExport const char *VnConvErrMsg(int errCode) { if (errCode < 0 || errCode >= VNCONV_LAST_ERROR) errCode = VNCONV_UNKNOWN_ERROR; return ErrTable[errCode]; diff --git a/unikey/core/data.cpp b/unikey/core/data.cpp index c4b99eb1..2e3539d5 100644 --- a/unikey/core/data.cpp +++ b/unikey/core/data.cpp @@ -39,17 +39,29 @@ Steps to add a 2-byte charset: low byte is base character, high byte is tone mark (if present). */ extern CharsetNameId CharsetIdMap[]; -extern const int CharsetCount; +extern const int CharsetCount; -CharsetNameId CharsetIdMap[] = {{"BKHCM1", CONV_CHARSET_BKHCM1}, {"BKHCM2", CONV_CHARSET_BKHCM2}, {"ISC", CONV_CHARSET_ISC}, - {"NCR-DEC", CONV_CHARSET_UNIREF}, {"NCR-HEX", CONV_CHARSET_UNIREF_HEX}, {"TCVN3", CONV_CHARSET_TCVN3}, - {"UNI-COMP", CONV_CHARSET_UNIDECOMPOSED}, {"UNICODE", CONV_CHARSET_UNICODE}, {"UTF-8", CONV_CHARSET_UNIUTF8}, - {"UTF8", CONV_CHARSET_UNIUTF8}, {"UVIQR", CONV_CHARSET_UTF8VIQR}, {"VIETWARE-F", CONV_CHARSET_VIETWAREF}, - {"VIETWARE-X", CONV_CHARSET_VIETWAREX}, {"VIQR", CONV_CHARSET_VIQR}, {"VISCII", CONV_CHARSET_VISCII}, - {"VNI-MAC", CONV_CHARSET_VNIMAC}, {"VNI-WIN", CONV_CHARSET_VNIWIN}, {"VPS", CONV_CHARSET_VPS}, - {"WINCP-1258", CONV_CHARSET_WINCP1258}}; +CharsetNameId CharsetIdMap[] = {{"BKHCM1", CONV_CHARSET_BKHCM1}, + {"BKHCM2", CONV_CHARSET_BKHCM2}, + {"ISC", CONV_CHARSET_ISC}, + {"NCR-DEC", CONV_CHARSET_UNIREF}, + {"NCR-HEX", CONV_CHARSET_UNIREF_HEX}, + {"TCVN3", CONV_CHARSET_TCVN3}, + {"UNI-COMP", CONV_CHARSET_UNIDECOMPOSED}, + {"UNICODE", CONV_CHARSET_UNICODE}, + {"UTF-8", CONV_CHARSET_UNIUTF8}, + {"UTF8", CONV_CHARSET_UNIUTF8}, + {"UVIQR", CONV_CHARSET_UTF8VIQR}, + {"VIETWARE-F", CONV_CHARSET_VIETWAREF}, + {"VIETWARE-X", CONV_CHARSET_VIETWAREX}, + {"VIQR", CONV_CHARSET_VIQR}, + {"VISCII", CONV_CHARSET_VISCII}, + {"VNI-MAC", CONV_CHARSET_VNIMAC}, + {"VNI-WIN", CONV_CHARSET_VNIWIN}, + {"VPS", CONV_CHARSET_VPS}, + {"WINCP-1258", CONV_CHARSET_WINCP1258}}; -const int CharsetCount = sizeof(CharsetIdMap) / sizeof(CharsetNameId); +const int CharsetCount = sizeof(CharsetIdMap) / sizeof(CharsetNameId); /* Western symbols that need to be mapped 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, @@ -1351,172 +1363,268 @@ unsigned char SingleByteTables[][TOTAL_VNCHARS] = UKWORD DoubleByteTables[][TOTAL_VNCHARS] = { // VNI-WIN - {0x0041, 0x0061, 0xd941, 0xf961, 0xd841, 0xf861, 0xdb41, 0xfb61, 0xd541, 0xf561, 0xcf41, 0xef61, // a - 0xc241, 0xe261, 0xc141, 0xe161, 0xc041, 0xe061, 0xc541, 0xe561, 0xc341, 0xe361, 0xc441, 0xe461, // a^ - 0xca41, 0xea61, 0xc941, 0xe961, 0xc841, 0xe861, 0xda41, 0xfa61, 0xdc41, 0xfc61, 0xcb41, 0xeb61, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00d1, 0x00f1, // DD, dd - 0x0045, 0x0065, 0xd945, 0xf965, 0xd845, 0xf865, 0xdb45, 0xfb65, 0xd545, 0xf565, 0xcf45, 0xef65, // e - 0xc245, 0xe265, 0xc145, 0xe165, 0xc045, 0xe065, 0xc545, 0xe565, 0xc345, 0xe365, 0xc445, 0xe465, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x00c6, 0x00e6, 0x00d3, 0x00f3, 0x00d2, 0x00f2, // i + {0x0041, 0x0061, 0xd941, 0xf961, 0xd841, 0xf861, 0xdb41, 0xfb61, 0xd541, + 0xf561, 0xcf41, 0xef61, // a + 0xc241, 0xe261, 0xc141, 0xe161, 0xc041, 0xe061, 0xc541, 0xe561, 0xc341, + 0xe361, 0xc441, 0xe461, // a^ + 0xca41, 0xea61, 0xc941, 0xe961, 0xc841, 0xe861, 0xda41, 0xfa61, 0xdc41, + 0xfc61, 0xcb41, 0xeb61, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d1, 0x00f1, // DD, dd + 0x0045, 0x0065, 0xd945, 0xf965, 0xd845, 0xf865, 0xdb45, 0xfb65, 0xd545, + 0xf565, 0xcf45, 0xef65, // e + 0xc245, 0xe265, 0xc145, 0xe165, 0xc045, 0xe065, 0xc545, 0xe565, 0xc345, + 0xe365, 0xc445, 0xe465, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x00c6, 0x00e6, 0x00d3, + 0x00f3, 0x00d2, 0x00f2, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0xd94f, 0xf96f, 0xd84f, 0xf86f, 0xdb4f, 0xfb6f, 0xd54f, 0xf56f, 0xcf4f, 0xef6f, // o - 0xc24f, 0xe26f, 0xc14f, 0xe16f, 0xc04f, 0xe06f, 0xc54f, 0xe56f, 0xc34f, 0xe36f, 0xc44f, 0xe46f, // o^ - 0x00d4, 0x00f4, 0xd9d4, 0xf9f4, 0xd8d4, 0xf8f4, 0xdbd4, 0xfbf4, 0xd5d4, 0xf5f4, 0xcfd4, 0xeff4, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xd94f, 0xf96f, 0xd84f, 0xf86f, 0xdb4f, 0xfb6f, 0xd54f, + 0xf56f, 0xcf4f, 0xef6f, // o + 0xc24f, 0xe26f, 0xc14f, 0xe16f, 0xc04f, 0xe06f, 0xc54f, 0xe56f, 0xc34f, + 0xe36f, 0xc44f, 0xe46f, // o^ + 0x00d4, 0x00f4, 0xd9d4, 0xf9f4, 0xd8d4, 0xf8f4, 0xdbd4, 0xfbf4, 0xd5d4, + 0xf5f4, 0xcfd4, 0xeff4, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xd955, 0xf975, 0xd855, 0xf875, 0xdb55, 0xfb75, 0xd555, 0xf575, 0xcf55, 0xef75, // u - 0x00d6, 0x00f6, 0xd9d6, 0xf9f6, 0xd8d6, 0xf8f6, 0xdbd6, 0xfbf6, 0xd5d6, 0xf5f6, 0xcfd6, 0xeff6, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xd959, 0xf979, 0xd859, 0xf879, 0xdb59, 0xfb79, 0xd559, 0xf579, 0x00ce, 0x00ee, // y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, - 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, - // BKHCM2 - {0x0041, 0x0061, 0xC141, 0xe161, 0xC241, 0xe261, 0xC341, 0xe361, 0xC441, 0xe461, 0xC541, 0xe561, // a - 0x00CA, 0x00EA, 0xCBCA, 0xEBEA, 0xCCCA, 0xECEA, 0xCDCA, 0xEDEA, 0xCECA, 0xEEEA, 0xC5CA, 0xE5EA, // a^ - 0x00D9, 0x00F9, 0xC6D9, 0xE6F9, 0xC7D9, 0xE7F9, 0xC8D9, 0xE8F9, 0xC9D9, 0xE9F9, 0xC5D9, 0xE5F9, 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00C0, 0x00E0, 0x0045, 0x0065, 0xC145, 0xE165, 0xC245, 0xE265, 0xC345, 0xE365, 0xC445, 0xE465, 0xC545, 0xE565, // e - 0x00CF, 0x00EF, 0xCBCF, 0xEBEF, 0xCCCF, 0xECEF, 0xCDCF, 0xEDEF, 0xCECF, 0xEEEF, 0xE5CF, 0xE5EF, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00D1, 0x00F1, 0x00D2, 0x00F2, 0x00D3, 0x00F3, 0x00D4, 0x00F4, 0x00D5, 0x00F5, // i - 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, 0x006e, // J j K k L l M m N n - 0x004F, 0x006F, 0xC14F, 0xE16F, 0xC24F, 0xE26F, 0xC34F, 0xE36F, 0xC44F, 0xE46F, 0xC54F, 0xE56F, // o - 0x00D6, 0x00F6, 0xCBD6, 0xEBF6, 0xCCD6, 0xECF6, 0xCDD6, 0xEDF6, 0xCED6, 0xEEF6, 0xC5D6, 0xE5F6, // o^ - 0x00DA, 0x00FA, 0xC1DA, 0xE1FA, 0xC2DA, 0xE2FA, 0xC3DA, 0xE3FA, 0xC4DA, 0xE4FA, 0xC5DA, 0xE5FA, // o+ - 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xC155, 0xE175, 0xC255, 0xE275, 0xC355, 0xE375, 0xC455, 0xE475, 0xC555, 0xE575, // u - 0x00DB, 0x00FB, 0xC1DB, 0xE1FB, 0xC2DB, 0xE2FB, 0xC3DB, 0xE3FB, 0xC4DB, 0xE4FB, 0xC5DB, 0xE5FB, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xC159, 0xE179, 0xC259, 0xE279, 0xC359, 0xE379, 0xC459, 0xE479, 0xC559, 0xE579, 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xd955, 0xf975, 0xd855, 0xf875, 0xdb55, 0xfb75, 0xd555, + 0xf575, 0xcf55, 0xef75, // u + 0x00d6, 0x00f6, 0xd9d6, 0xf9f6, 0xd8d6, 0xf8f6, 0xdbd6, 0xfbf6, 0xd5d6, + 0xf5f6, 0xcfd6, 0xeff6, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xd959, 0xf979, 0xd859, 0xf879, 0xdb59, 0xfb79, 0xd559, + 0xf579, 0x00ce, 0x00ee, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + // BKHCM2 + {0x0041, 0x0061, 0xC141, 0xe161, 0xC241, 0xe261, 0xC341, + 0xe361, 0xC441, 0xe461, 0xC541, 0xe561, // a + 0x00CA, 0x00EA, 0xCBCA, 0xEBEA, 0xCCCA, 0xECEA, 0xCDCA, + 0xEDEA, 0xCECA, 0xEEEA, 0xC5CA, 0xE5EA, // a^ + 0x00D9, 0x00F9, 0xC6D9, 0xE6F9, 0xC7D9, 0xE7F9, 0xC8D9, + 0xE8F9, 0xC9D9, 0xE9F9, 0xC5D9, 0xE5F9, 0x0042, 0x0062, + 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00C0, 0x00E0, 0x0045, 0x0065, 0xC145, 0xE165, 0xC245, + 0xE265, 0xC345, 0xE365, 0xC445, 0xE465, 0xC545, 0xE565, // e + 0x00CF, 0x00EF, 0xCBCF, 0xEBEF, 0xCCCF, 0xECEF, 0xCDCF, + 0xEDEF, 0xCECF, 0xEEEF, 0xE5CF, 0xE5EF, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00D1, 0x00F1, 0x00D2, 0x00F2, 0x00D3, + 0x00F3, 0x00D4, 0x00F4, 0x00D5, 0x00F5, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, + 0x006d, 0x004e, 0x006e, // J j K k L l M m N n + 0x004F, 0x006F, 0xC14F, 0xE16F, 0xC24F, 0xE26F, 0xC34F, + 0xE36F, 0xC44F, 0xE46F, 0xC54F, 0xE56F, // o + 0x00D6, 0x00F6, 0xCBD6, 0xEBF6, 0xCCD6, 0xECF6, 0xCDD6, + 0xEDF6, 0xCED6, 0xEEF6, 0xC5D6, 0xE5F6, // o^ + 0x00DA, 0x00FA, 0xC1DA, 0xE1FA, 0xC2DA, 0xE2FA, 0xC3DA, + 0xE3FA, 0xC4DA, 0xE4FA, 0xC5DA, 0xE5FA, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, + 0x0073, 0x0054, 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xC155, 0xE175, 0xC255, 0xE275, 0xC355, + 0xE375, 0xC455, 0xE475, 0xC555, 0xE575, // u + 0x00DB, 0x00FB, 0xC1DB, 0xE1FB, 0xC2DB, 0xE2FB, 0xC3DB, + 0xE3FB, 0xC4DB, 0xE4FB, 0xC5DB, 0xE5FB, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xC159, 0xE179, 0xC259, 0xE279, 0xC359, + 0xE379, 0xC459, 0xE479, 0xC559, 0xE579, 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, + 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, + 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, // VIETWARE-X - {0x0041, 0x0061, 0xCF41, 0xEF61, 0xCC41, 0xEC61, 0xCD41, 0xED61, 0xCE41, 0xEE61, 0xDB41, 0xFB61, // a - 0x00C1, 0x00E1, 0xDAC1, 0xFAE1, 0xD6C1, 0xF6E1, 0xD8C1, 0xF8E1, 0xD9C1, 0xF9E1, 0xDBC1, 0xFBE1, // a^ - 0x00C0, 0x00E0, 0xD5C0, 0xF5E0, 0xD2C0, 0xF2E0, 0xD3C0, 0xF3E0, 0xD4C0, 0xF4E0, 0xDBC0, 0xFBE0, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00C2, 0x00E2, 0x0045, 0x0065, 0xCF45, 0xEF65, 0xCC45, 0xEC65, 0xCD45, 0xED65, 0xCE45, 0xEE65, 0xDB45, 0xFB65, // e - 0x00C3, 0x00E3, 0xDAC3, 0xFAE3, 0xD6C3, 0xF6E3, 0xD8C3, 0xF8E3, 0xD9C3, 0xF9E3, 0xDBC3, 0xFBE3, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00CA, 0x00EA, 0x00C7, 0x00E7, 0x00C8, 0x00E8, 0x00C9, 0x00E9, 0x00CB, 0x00EB, // i + {0x0041, 0x0061, 0xCF41, 0xEF61, 0xCC41, 0xEC61, 0xCD41, 0xED61, 0xCE41, + 0xEE61, 0xDB41, 0xFB61, // a + 0x00C1, 0x00E1, 0xDAC1, 0xFAE1, 0xD6C1, 0xF6E1, 0xD8C1, 0xF8E1, 0xD9C1, + 0xF9E1, 0xDBC1, 0xFBE1, // a^ + 0x00C0, 0x00E0, 0xD5C0, 0xF5E0, 0xD2C0, 0xF2E0, 0xD3C0, 0xF3E0, 0xD4C0, + 0xF4E0, 0xDBC0, 0xFBE0, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00C2, 0x00E2, 0x0045, 0x0065, 0xCF45, 0xEF65, 0xCC45, 0xEC65, 0xCD45, + 0xED65, 0xCE45, 0xEE65, 0xDB45, 0xFB65, // e + 0x00C3, 0x00E3, 0xDAC3, 0xFAE3, 0xD6C3, 0xF6E3, 0xD8C3, 0xF8E3, 0xD9C3, + 0xF9E3, 0xDBC3, 0xFBE3, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00CA, 0x00EA, 0x00C7, 0x00E7, 0x00C8, 0x00E8, 0x00C9, + 0x00E9, 0x00CB, 0x00EB, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004F, 0x006F, 0xCF4F, 0xEF6F, 0xCC4F, 0xEC6F, 0xCD4F, 0xED6F, 0xCE4F, 0xEE6F, 0xDC4F, 0xFC6F, // o - 0x00C4, 0x00E4, 0xDAC4, 0xFAE4, 0xD6C4, 0xF6E4, 0xD8C4, 0xF8E4, 0xD9C4, 0xF9E4, 0xDCC4, 0xFCE4, // o^ - 0x00C5, 0x00E5, 0xCFC5, 0xEFE5, 0xCCC5, 0xECE5, 0xCDC5, 0xEDE5, 0xCEC5, 0xEEE5, 0xDCC5, 0xFCE5, // o+ + 0x006e, // J j K k L l M m N n + 0x004F, 0x006F, 0xCF4F, 0xEF6F, 0xCC4F, 0xEC6F, 0xCD4F, 0xED6F, 0xCE4F, + 0xEE6F, 0xDC4F, 0xFC6F, // o + 0x00C4, 0x00E4, 0xDAC4, 0xFAE4, 0xD6C4, 0xF6E4, 0xD8C4, 0xF8E4, 0xD9C4, + 0xF9E4, 0xDCC4, 0xFCE4, // o^ + 0x00C5, 0x00E5, 0xCFC5, 0xEFE5, 0xCCC5, 0xECE5, 0xCDC5, 0xEDE5, 0xCEC5, + 0xEEE5, 0xDCC5, 0xFCE5, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xCF55, 0xEF75, 0xCC55, 0xEC75, 0xCD55, 0xED75, 0xCE55, 0xEE75, 0xDB55, 0xFB75, // u - 0x00C6, 0x00E6, 0xCFC6, 0xEFE6, 0xCCC6, 0xECE6, 0xCDC6, 0xEDE6, 0xCEC6, 0xEEE6, 0xDBC6, 0xFBE6, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xCF59, 0xEF79, 0xCC59, 0xEC79, 0xCD59, 0xED79, 0xCE59, 0xEE79, 0xD159, 0xF179, // Y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, - 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xCF55, 0xEF75, 0xCC55, 0xEC75, 0xCD55, 0xED75, 0xCE55, + 0xEE75, 0xDB55, 0xFB75, // u + 0x00C6, 0x00E6, 0xCFC6, 0xEFE6, 0xCCC6, 0xECE6, 0xCDC6, 0xEDE6, 0xCEC6, + 0xEEE6, 0xDBC6, 0xFBE6, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xCF59, 0xEF79, 0xCC59, 0xEC79, 0xCD59, 0xED79, 0xCE59, + 0xEE79, 0xD159, 0xF179, // Y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}, // VNI-MAC - {0x0041, 0x0061, 0xf441, 0x9d61, 0xaf41, 0xbf61, 0xf341, 0x9e61, 0xcd41, 0x9b61, 0xec41, 0x9561, // a - 0xe541, 0x8961, 0xe741, 0x8761, 0xcb41, 0x8861, 0x8141, 0x8c61, 0xcc41, 0x8b61, 0x8041, 0x8a61, // a^ - 0xe641, 0x9061, 0x8341, 0x8e61, 0xe941, 0x8f61, 0xf241, 0x9c61, 0x8641, 0x9f61, 0xe841, 0x9161, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x0084, 0x0096, // DD, dd - 0x0045, 0x0065, 0xf445, 0x9d65, 0xaf45, 0xbf65, 0xf345, 0x9e65, 0xcd45, 0x9b65, 0xec45, 0x9565, // e - 0xe545, 0x8965, 0xe745, 0x8765, 0xcb45, 0x8865, 0x8145, 0x8c65, 0xcc45, 0x8b65, 0x8045, 0x8a65, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00ea, 0x0092, 0x00ed, 0x0093, 0x00ae, 0x00be, 0x00ee, 0x0097, 0x00f1, 0x0098, // i + {0x0041, 0x0061, 0xf441, 0x9d61, 0xaf41, 0xbf61, 0xf341, 0x9e61, 0xcd41, + 0x9b61, 0xec41, 0x9561, // a + 0xe541, 0x8961, 0xe741, 0x8761, 0xcb41, 0x8861, 0x8141, 0x8c61, 0xcc41, + 0x8b61, 0x8041, 0x8a61, // a^ + 0xe641, 0x9061, 0x8341, 0x8e61, 0xe941, 0x8f61, 0xf241, 0x9c61, 0x8641, + 0x9f61, 0xe841, 0x9161, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x0084, 0x0096, // DD, dd + 0x0045, 0x0065, 0xf445, 0x9d65, 0xaf45, 0xbf65, 0xf345, 0x9e65, 0xcd45, + 0x9b65, 0xec45, 0x9565, // e + 0xe545, 0x8965, 0xe745, 0x8765, 0xcb45, 0x8865, 0x8145, 0x8c65, 0xcc45, + 0x8b65, 0x8045, 0x8a65, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00ea, 0x0092, 0x00ed, 0x0093, 0x00ae, 0x00be, 0x00ee, + 0x0097, 0x00f1, 0x0098, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0xf44f, 0x9d6f, 0xaf4f, 0xbf6f, 0xf34f, 0x9e6f, 0xcd4f, 0x9b6f, 0xec4f, 0x956f, // o - 0xe54f, 0x896f, 0xe74f, 0x876f, 0xcb4f, 0x886f, 0x814f, 0x8c6f, 0xcc4f, 0x8b6f, 0x804f, 0x8a6f, // o^ - 0x00ef, 0x0099, 0xf4ef, 0x9d99, 0xafef, 0xbf99, 0xf3ef, 0x9e99, 0xcdef, 0x9b99, 0xecef, 0x9599, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xf44f, 0x9d6f, 0xaf4f, 0xbf6f, 0xf34f, 0x9e6f, 0xcd4f, + 0x9b6f, 0xec4f, 0x956f, // o + 0xe54f, 0x896f, 0xe74f, 0x876f, 0xcb4f, 0x886f, 0x814f, 0x8c6f, 0xcc4f, + 0x8b6f, 0x804f, 0x8a6f, // o^ + 0x00ef, 0x0099, 0xf4ef, 0x9d99, 0xafef, 0xbf99, 0xf3ef, 0x9e99, 0xcdef, + 0x9b99, 0xecef, 0x9599, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xf455, 0x9d75, 0xaf55, 0xbf75, 0xf355, 0x9e75, 0xcd55, 0x9b75, 0xec55, 0x9575, // u - 0x0085, 0x009a, 0xf485, 0x9d9a, 0xaf85, 0xbf9a, 0xf385, 0x9e9a, 0xcd85, 0x9b9a, 0xec85, 0x959a, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xf459, 0x9d79, 0xaf59, 0xbf79, 0xf359, 0x9e79, 0xcd59, 0x9b79, 0x00eb, 0x0094, // y - 0x005a, 0x007a, // Z z - 0x00db, 0x00e2, 0x00c4, 0x00e3, 0x00c9, 0x00a0, 0x00e0, 0x00f6, 0x00e4, 0x003f, 0x00dc, 0x00ce, 0x003f, 0x00d4, - 0x00d5, 0x00d2, 0x00d3, 0x00a5, 0x00d0, 0x00d1, 0x00f7, 0x00aa, 0x003f, 0x00dd, 0x00cf, 0x003f, 0x00d9}}; + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xf455, 0x9d75, 0xaf55, 0xbf75, 0xf355, 0x9e75, 0xcd55, + 0x9b75, 0xec55, 0x9575, // u + 0x0085, 0x009a, 0xf485, 0x9d9a, 0xaf85, 0xbf9a, 0xf385, 0x9e9a, 0xcd85, + 0x9b9a, 0xec85, 0x959a, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xf459, 0x9d79, 0xaf59, 0xbf79, 0xf359, 0x9e79, 0xcd59, + 0x9b79, 0x00eb, 0x0094, // y + 0x005a, 0x007a, // Z z + 0x00db, 0x00e2, 0x00c4, 0x00e3, 0x00c9, 0x00a0, 0x00e0, 0x00f6, 0x00e4, + 0x003f, 0x00dc, 0x00ce, 0x003f, 0x00d4, 0x00d5, 0x00d2, 0x00d3, 0x00a5, + 0x00d0, 0x00d1, 0x00f7, 0x00aa, 0x003f, 0x00dd, 0x00cf, 0x003f, 0x00d9}}; UKWORD WinCP1258[TOTAL_VNCHARS] = // Windows CP 1258 - {0x0041, 0x0061, 0xec41, 0xec61, 0xcc41, 0xcc61, 0xd241, 0xd261, 0xde41, 0xde61, 0xf241, 0xf261, // a - 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, 0xdee2, 0xf2c2, 0xf2e2, // a^ - 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, 0xdee3, 0xf2c3, 0xf2e3, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00d0, 0x00f0, // DD, dd - 0x0045, 0x0065, 0xec45, 0xec65, 0xcc45, 0xcc65, 0xd245, 0xd265, 0xde45, 0xde65, 0xf245, 0xf265, // e - 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, 0xdeea, 0xf2ca, 0xf2ea, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0xec49, 0xec69, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, 0xde69, 0xf249, 0xf269, // i + {0x0041, 0x0061, 0xec41, 0xec61, 0xcc41, 0xcc61, 0xd241, 0xd261, 0xde41, + 0xde61, 0xf241, 0xf261, // a + 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, + 0xdee2, 0xf2c2, 0xf2e2, // a^ + 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, + 0xdee3, 0xf2c3, 0xf2e3, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d0, 0x00f0, // DD, dd + 0x0045, 0x0065, 0xec45, 0xec65, 0xcc45, 0xcc65, 0xd245, 0xd265, 0xde45, + 0xde65, 0xf245, 0xf265, // e + 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, + 0xdeea, 0xf2ca, 0xf2ea, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0xec49, 0xec69, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, + 0xde69, 0xf249, 0xf269, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0xec4f, 0xec6f, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, 0xde6f, 0xf24f, 0xf26f, // o - 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, 0xdef4, 0xf2d4, 0xf2f4, // o^ - 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, 0xdef5, 0xf2d5, 0xf2f5, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0xec4f, 0xec6f, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, + 0xde6f, 0xf24f, 0xf26f, // o + 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, + 0xdef4, 0xf2d4, 0xf2f4, // o^ + 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, + 0xdef5, 0xf2d5, 0xf2f5, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0xec55, 0xec75, 0xcc55, 0xcc75, 0xd255, 0xd275, 0xde55, 0xde75, 0xf255, 0xf275, // u - 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, 0xdefd, 0xf2dd, 0xf2fd, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, 0xde79, 0xf259, 0xf279, // y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, - 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0xec55, 0xec75, 0xcc55, 0xcc75, 0xd255, 0xd275, 0xde55, + 0xde75, 0xf255, 0xf275, // u + 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, + 0xdefd, 0xf2dd, 0xf2fd, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, + 0xde79, 0xf259, 0xf279, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; UKWORD WinCP1258Pre[TOTAL_VNCHARS] = // Windows CP1258 - with some more precomposed characters - {0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0xd241, 0xd261, 0xde41, 0xde61, 0xf241, 0xf261, // a - 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, 0xdee2, 0xf2c2, 0xf2e2, // a^ - 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, 0xdee3, 0xf2c3, 0xf2e3, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x00d0, 0x00f0, // DD, dd - 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0xd245, 0xd265, 0xde45, 0xde65, 0xf245, 0xf265, // e - 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, 0xdeea, 0xf2ca, 0xf2ea, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00cd, 0x00ed, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, 0xde69, 0xf249, 0xf269, // i + {0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0xd241, 0xd261, 0xde41, + 0xde61, 0xf241, 0xf261, // a + 0x00c2, 0x00e2, 0xecc2, 0xece2, 0xccc2, 0xcce2, 0xd2c2, 0xd2e2, 0xdec2, + 0xdee2, 0xf2c2, 0xf2e2, // a^ + 0x00c3, 0x00e3, 0xecc3, 0xece3, 0xccc3, 0xcce3, 0xd2c3, 0xd2e3, 0xdec3, + 0xdee3, 0xf2c3, 0xf2e3, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x00d0, 0x00f0, // DD, dd + 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0xd245, 0xd265, 0xde45, + 0xde65, 0xf245, 0xf265, // e + 0x00ca, 0x00ea, 0xecca, 0xecea, 0xccca, 0xccea, 0xd2ca, 0xd2ea, 0xdeca, + 0xdeea, 0xf2ca, 0xf2ea, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0xcc49, 0xcc69, 0xd249, 0xd269, 0xde49, + 0xde69, 0xf249, 0xf269, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0x00d3, 0x00f3, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, 0xde6f, 0xf24f, 0xf26f, // o - 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, 0xdef4, 0xf2d4, 0xf2f4, // o^ - 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, 0xdef5, 0xf2d5, 0xf2f5, // o+ + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0x00d3, 0x00f3, 0xcc4f, 0xcc6f, 0xd24f, 0xd26f, 0xde4f, + 0xde6f, 0xf24f, 0xf26f, // o + 0x00d4, 0x00f4, 0xecd4, 0xecf4, 0xccd4, 0xccf4, 0xd2d4, 0xd2f4, 0xded4, + 0xdef4, 0xf2d4, 0xf2f4, // o^ + 0x00d5, 0x00f5, 0xecd5, 0xecf5, 0xccd5, 0xccf5, 0xd2d5, 0xd2f5, 0xded5, + 0xdef5, 0xf2d5, 0xf2f5, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0xd255, 0xd275, 0xde55, 0xde75, 0xf255, 0xf275, // u - 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, 0xdefd, 0xf2dd, 0xf2fd, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, 0xde79, 0xf259, 0xf279, // y - 0x005a, 0x007a, // Z z - 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, - 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0xd255, 0xd275, 0xde55, + 0xde75, 0xf255, 0xf275, // u + 0x00dd, 0x00fd, 0xecdd, 0xecfd, 0xccdd, 0xccfd, 0xd2dd, 0xd2fd, 0xdedd, + 0xdefd, 0xf2dd, 0xf2fd, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0xec59, 0xec79, 0xcc59, 0xcc79, 0xd259, 0xd279, 0xde59, + 0xde79, 0xf259, 0xf279, // y + 0x005a, 0x007a, // Z z + 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, + 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009E, 0x009F}; -UnicodeChar UnicodeTable[TOTAL_VNCHARS] = {0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0x1ea2, 0x1ea3, 0x00c3, 0x00e3, 0x1ea0, 0x1ea1, // a - 0x00c2, 0x00e2, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7, 0x1ea8, 0x1ea9, 0x1eaa, 0x1eab, 0x1eac, 0x1ead, // a^ - 0x0102, 0x0103, 0x1eae, 0x1eaf, 0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, 0x1eb5, 0x1eb6, 0x1eb7, // a( - 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d - 0x0110, 0x0111, // DD, dd - 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0x1eba, 0x1ebb, 0x1ebc, 0x1ebd, 0x1eb8, 0x1eb9, // e - 0x00ca, 0x00ea, 0x1ebe, 0x1ebf, 0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, 0x1ec5, 0x1ec6, 0x1ec7, // e^ - 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x1ec8, 0x1ec9, 0x0128, 0x0129, 0x1eca, 0x1ecb, // i - 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, - 0x006e, // J j K k L l M m N n - 0x004f, 0x006f, 0x00d3, 0x00f3, 0x00d2, 0x00f2, 0x1ece, 0x1ecf, 0x00d5, 0x00f5, 0x1ecc, 0x1ecd, // o - 0x00d4, 0x00f4, 0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, 0x1ed7, 0x1ed8, 0x1ed9, // o^ - 0x01a0, 0x01a1, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf, 0x1ee0, 0x1ee1, 0x1ee2, 0x1ee3, // o+ - 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, - 0x0074, // P p Q q R r S s T t - 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0x1ee6, 0x1ee7, 0x0168, 0x0169, 0x1ee4, 0x1ee5, // u - 0x01af, 0x01b0, 0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, 0x1eef, 0x1ef0, 0x1ef1, // u+ - 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x0059, 0x0079, 0x00dd, 0x00fd, 0x1ef2, 0x1ef3, 0x1ef6, 0x1ef7, 0x1ef8, 0x1ef9, 0x1ef4, 0x1ef5, // y - 0x005a, 0x007a, // Z z - // Symbols that have different code points in Unicode and Western charsets - 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, - 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; +UnicodeChar UnicodeTable[TOTAL_VNCHARS] = { + 0x0041, 0x0061, 0x00c1, 0x00e1, 0x00c0, 0x00e0, 0x1ea2, 0x1ea3, 0x00c3, + 0x00e3, 0x1ea0, 0x1ea1, // a + 0x00c2, 0x00e2, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7, 0x1ea8, 0x1ea9, 0x1eaa, + 0x1eab, 0x1eac, 0x1ead, // a^ + 0x0102, 0x0103, 0x1eae, 0x1eaf, 0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, + 0x1eb5, 0x1eb6, 0x1eb7, // a( + 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d + 0x0110, 0x0111, // DD, dd + 0x0045, 0x0065, 0x00c9, 0x00e9, 0x00c8, 0x00e8, 0x1eba, 0x1ebb, 0x1ebc, + 0x1ebd, 0x1eb8, 0x1eb9, // e + 0x00ca, 0x00ea, 0x1ebe, 0x1ebf, 0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, + 0x1ec5, 0x1ec6, 0x1ec7, // e^ + 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h + 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x1ec8, 0x1ec9, 0x0128, + 0x0129, 0x1eca, 0x1ecb, // i + 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, + 0x006e, // J j K k L l M m N n + 0x004f, 0x006f, 0x00d3, 0x00f3, 0x00d2, 0x00f2, 0x1ece, 0x1ecf, 0x00d5, + 0x00f5, 0x1ecc, 0x1ecd, // o + 0x00d4, 0x00f4, 0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, + 0x1ed7, 0x1ed8, 0x1ed9, // o^ + 0x01a0, 0x01a1, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf, 0x1ee0, + 0x1ee1, 0x1ee2, 0x1ee3, // o+ + 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, + 0x0074, // P p Q q R r S s T t + 0x0055, 0x0075, 0x00da, 0x00fa, 0x00d9, 0x00f9, 0x1ee6, 0x1ee7, 0x0168, + 0x0169, 0x1ee4, 0x1ee5, // u + 0x01af, 0x01b0, 0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, + 0x1eef, 0x1ef0, 0x1ef1, // u+ + 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x + 0x0059, 0x0079, 0x00dd, 0x00fd, 0x1ef2, 0x1ef3, 0x1ef6, 0x1ef7, 0x1ef8, + 0x1ef9, 0x1ef4, 0x1ef5, // y + 0x005a, 0x007a, // Z z + // Symbols that have different code points in Unicode and Western charsets + 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, + 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, + 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; /* unsigned char WesternSymbols[] = @@ -1538,114 +1646,147 @@ unsigned char WesternSymbols[] = + 0x2b */ -UKDWORD VIQRTable[TOTAL_VNCHARS] = {0x41, 0x61, 0x2741, 0x2761, 0x6041, 0x6061, 0x3f41, 0x3f61, 0x7e41, 0x7e61, 0x2e41, 0x2e61, // a - 0x5e41, 0x5e61, 0x275e41, 0x275e61, 0x605e41, 0x605e61, 0x3f5e41, 0x3f5e61, 0x7e5e41, 0x7e5e61, 0x2e5e41, 0x2e5e61, // a^ - 0x2841, 0x2861, 0x272841, 0x272861, 0x602841, 0x602861, 0x3f2841, 0x3f2861, 0x7e2841, 0x7e2861, 0x2e2841, 0x2e2861, // a( - 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, // B b C c D d - 0x4444, 0x6464, // DD, dd - 0x45, 0x65, 0x2745, 0x2765, 0x6045, 0x6065, 0x3f45, 0x3f65, 0x7e45, 0x7e65, 0x2e45, 0x2e65, // e - 0x5e45, 0x5e65, 0x275e45, 0x275e65, 0x605e45, 0x605e65, 0x3f5e45, 0x3f5e65, 0x7e5e45, 0x7e5e65, 0x2e5e45, 0x2e5e65, // e^ - 0x46, 0x66, 0x47, 0x67, 0x48, 0x68, // F f G g H h - 0x49, 0x69, 0x2749, 0x2769, 0x6049, 0x6069, 0x3f49, 0x3f69, 0x7e49, 0x7e69, 0x2e49, 0x2e69, // i - 0x4a, 0x6a, 0x4b, 0x6b, 0x4c, 0x6c, 0x4d, 0x6d, 0x4e, 0x6e, // J j K k L l M m N n - 0x4f, 0x6f, 0x274f, 0x276f, 0x604f, 0x606f, 0x3f4f, 0x3f6f, 0x7e4f, 0x7e6f, 0x2e4f, 0x2e6f, // o - 0x5e4f, 0x5e6f, 0x275e4f, 0x275e6f, 0x605e4f, 0x605e6f, 0x3f5e4f, 0x3f5e6f, 0x7e5e4f, 0x7e5e6f, 0x2e5e4f, 0x2e5e6f, // o^ - 0x2b4f, 0x2b6f, 0x272b4f, 0x272b6f, 0x602b4f, 0x602b6f, 0x3f2b4f, 0x3f2b6f, 0x7e2b4f, 0x7e2b6f, 0x2e2b4f, 0x2e2b6f, // o+ - 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, // P p Q q R r S s T t - 0x55, 0x75, 0x2755, 0x2775, 0x6055, 0x6075, 0x3f55, 0x3f75, 0x7e55, 0x7e75, 0x2e55, 0x2e75, // u - 0x2b55, 0x2b75, 0x272b55, 0x272b75, 0x602b55, 0x602b75, 0x3f2b55, 0x3f2b75, 0x7e2b55, 0x7e2b75, 0x2e2b55, 0x2e2b75, // u+ - 0x56, 0x76, 0x57, 0x77, 0x58, 0x78, // V v W w X x - 0x59, 0x79, 0x2759, 0x2779, 0x6059, 0x6079, 0x3f59, 0x3f79, 0x7e59, 0x7e79, 0x2e59, 0x2e79, 0x5a, 0x7a, // Z z - 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F}; +UKDWORD VIQRTable[TOTAL_VNCHARS] = { + 0x41, 0x61, 0x2741, 0x2761, 0x6041, 0x6061, 0x3f41, + 0x3f61, 0x7e41, 0x7e61, 0x2e41, 0x2e61, // a + 0x5e41, 0x5e61, 0x275e41, 0x275e61, 0x605e41, 0x605e61, 0x3f5e41, + 0x3f5e61, 0x7e5e41, 0x7e5e61, 0x2e5e41, 0x2e5e61, // a^ + 0x2841, 0x2861, 0x272841, 0x272861, 0x602841, 0x602861, 0x3f2841, + 0x3f2861, 0x7e2841, 0x7e2861, 0x2e2841, 0x2e2861, // a( + 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, // B b C c D d + 0x4444, 0x6464, // DD, dd + 0x45, 0x65, 0x2745, 0x2765, 0x6045, 0x6065, 0x3f45, + 0x3f65, 0x7e45, 0x7e65, 0x2e45, 0x2e65, // e + 0x5e45, 0x5e65, 0x275e45, 0x275e65, 0x605e45, 0x605e65, 0x3f5e45, + 0x3f5e65, 0x7e5e45, 0x7e5e65, 0x2e5e45, 0x2e5e65, // e^ + 0x46, 0x66, 0x47, 0x67, 0x48, 0x68, // F f G g H h + 0x49, 0x69, 0x2749, 0x2769, 0x6049, 0x6069, 0x3f49, + 0x3f69, 0x7e49, 0x7e69, 0x2e49, 0x2e69, // i + 0x4a, 0x6a, 0x4b, 0x6b, 0x4c, 0x6c, 0x4d, + 0x6d, 0x4e, 0x6e, // J j K k L l M m N n + 0x4f, 0x6f, 0x274f, 0x276f, 0x604f, 0x606f, 0x3f4f, + 0x3f6f, 0x7e4f, 0x7e6f, 0x2e4f, 0x2e6f, // o + 0x5e4f, 0x5e6f, 0x275e4f, 0x275e6f, 0x605e4f, 0x605e6f, 0x3f5e4f, + 0x3f5e6f, 0x7e5e4f, 0x7e5e6f, 0x2e5e4f, 0x2e5e6f, // o^ + 0x2b4f, 0x2b6f, 0x272b4f, 0x272b6f, 0x602b4f, 0x602b6f, 0x3f2b4f, + 0x3f2b6f, 0x7e2b4f, 0x7e2b6f, 0x2e2b4f, 0x2e2b6f, // o+ + 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, + 0x73, 0x54, 0x74, // P p Q q R r S s T t + 0x55, 0x75, 0x2755, 0x2775, 0x6055, 0x6075, 0x3f55, + 0x3f75, 0x7e55, 0x7e75, 0x2e55, 0x2e75, // u + 0x2b55, 0x2b75, 0x272b55, 0x272b75, 0x602b55, 0x602b75, 0x3f2b55, + 0x3f2b75, 0x7e2b55, 0x7e2b75, 0x2e2b55, 0x2e2b75, // u+ + 0x56, 0x76, 0x57, 0x77, 0x58, 0x78, // V v W w X x + 0x59, 0x79, 0x2759, 0x2779, 0x6059, 0x6079, 0x3f59, + 0x3f79, 0x7e59, 0x7e79, 0x2e59, 0x2e79, 0x5a, 0x7a, // Z z + 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F}; UKDWORD UnicodeComposite[TOTAL_VNCHARS] = { 0x00000041, 0x00000061, 0x03010041, 0x03010061, 0x03000041, 0x03000061, // a 0x03090041, 0x03090061, 0x03030041, 0x03030061, 0x03230041, 0x03230061, // a - 0x000000c2, 0x000000e2, 0x030100c2, 0x030100e2, 0x030000c2, 0x030000e2, 0x030900c2, 0x030900e2, 0x030300c2, 0x030300e2, 0x032300c2, + 0x000000c2, 0x000000e2, 0x030100c2, 0x030100e2, 0x030000c2, 0x030000e2, + 0x030900c2, 0x030900e2, 0x030300c2, 0x030300e2, 0x032300c2, 0x032300e2, // a^ - 0x00000102, 0x00000103, 0x03010102, 0x03010103, 0x03000102, 0x03000103, 0x03090102, 0x03090103, 0x03030102, 0x03030103, 0x03230102, + 0x00000102, 0x00000103, 0x03010102, 0x03010103, 0x03000102, 0x03000103, + 0x03090102, 0x03090103, 0x03030102, 0x03030103, 0x03230102, 0x03230103, // a( 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, // B b C c D d 0x0110, 0x0111, // 0x00d1, 0x00f1, //DD, dd - 0x00000045, 0x00000065, 0x03010045, 0x03010065, 0x03000045, 0x03000065, 0x03090045, 0x03090065, 0x03030045, 0x03030065, 0x03230045, 0x03230065, // e + 0x00000045, 0x00000065, 0x03010045, 0x03010065, 0x03000045, 0x03000065, + 0x03090045, 0x03090065, 0x03030045, 0x03030065, 0x03230045, 0x03230065, // e - 0x000000ca, 0x000000ea, 0x030100ca, 0x030100ea, 0x030000ca, 0x030000ea, 0x030900ca, 0x030900ea, 0x030300ca, 0x030300ea, 0x032300ca, + 0x000000ca, 0x000000ea, 0x030100ca, 0x030100ea, 0x030000ca, 0x030000ea, + 0x030900ca, 0x030900ea, 0x030300ca, 0x030300ea, 0x032300ca, 0x032300ea, // e^ 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068, // F f G g H h - 0x00000049, 0x00000069, 0x03010049, 0x03010069, 0x03000049, 0x03000069, 0x03090049, 0x03090069, 0x03030049, 0x03030069, 0x03230049, 0x03230069, // i + 0x00000049, 0x00000069, 0x03010049, 0x03010069, 0x03000049, 0x03000069, + 0x03090049, 0x03090069, 0x03030049, 0x03030069, 0x03230049, 0x03230069, // i 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d, 0x004e, 0x006e, // J j K k L l M m N n - 0x0000004f, 0x0000006f, 0x0301004f, 0x0301006f, 0x0300004f, 0x0300006f, 0x0309004f, 0x0309006f, 0x0303004f, 0x0303006f, 0x0323004f, 0x0323006f, // o + 0x0000004f, 0x0000006f, 0x0301004f, 0x0301006f, 0x0300004f, 0x0300006f, + 0x0309004f, 0x0309006f, 0x0303004f, 0x0303006f, 0x0323004f, 0x0323006f, // o - 0x000000d4, 0x000000f4, 0x030100d4, 0x030100f4, 0x030000d4, 0x030000f4, 0x030900d4, 0x030900f4, 0x030300d4, 0x030300f4, 0x032300d4, + 0x000000d4, 0x000000f4, 0x030100d4, 0x030100f4, 0x030000d4, 0x030000f4, + 0x030900d4, 0x030900f4, 0x030300d4, 0x030300f4, 0x032300d4, 0x032300f4, // o^ - 0x000001a0, 0x000001a1, 0x030101a0, 0x030101a1, 0x030001a0, 0x030001a1, 0x030901a0, 0x030901a1, 0x030301a0, 0x030301a1, 0x032301a0, + 0x000001a0, 0x000001a1, 0x030101a0, 0x030101a1, 0x030001a0, 0x030001a1, + 0x030901a0, 0x030901a1, 0x030301a0, 0x030301a1, 0x032301a0, 0x032301a1, // o+ 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073, 0x0054, 0x0074, // P p Q q R r S s T t - 0x00000055, 0x00000075, 0x03010055, 0x03010075, 0x03000055, 0x03000075, 0x03090055, 0x03090075, 0x03030055, 0x03030075, 0x03230055, 0x03230075, // u + 0x00000055, 0x00000075, 0x03010055, 0x03010075, 0x03000055, 0x03000075, + 0x03090055, 0x03090075, 0x03030055, 0x03030075, 0x03230055, 0x03230075, // u - 0x000001af, 0x000001b0, 0x030101af, 0x030101b0, 0x030001af, 0x030001b0, 0x030901af, 0x030901b0, 0x030301af, 0x030301b0, 0x032301af, + 0x000001af, 0x000001b0, 0x030101af, 0x030101b0, 0x030001af, 0x030001b0, + 0x030901af, 0x030901b0, 0x030301af, 0x030301b0, 0x032301af, 0x032301b0, // u+ 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078, // V v W w X x - 0x00000059, 0x00000079, 0x03010059, 0x03010079, 0x03000059, 0x03000079, 0x03090059, 0x03090079, 0x03030059, 0x03030079, 0x03230059, 0x03230079, // y - 0x005a, 0x007a, // Z z + 0x00000059, 0x00000079, 0x03010059, 0x03010079, 0x03000059, 0x03000079, + 0x03090059, 0x03090079, 0x03030059, 0x03030079, 0x03230059, 0x03230079, // y + 0x005a, 0x007a, // Z z // Symbols that have different code points in Unicode and Western charsets - 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, - 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; + 0x20AC, 0x20A1, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, + 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, + 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178}; -int StdVnRootChar[TOTAL_VNCHARS] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a^ -> a - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a( -> a - 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] - 40, 41, // DD dd [mapped to D, d] - 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] - 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 4: e^ -> e - 68, 69, 70, 71, 72, 73, // fgh - 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 7: o^ -> o - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 8: o+ -> o - 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst - 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] - 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 10: u+ -> u - 166, 167, 168, 169, 170, 171, // vwx - 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] - 184, 185, // z - 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; +int StdVnRootChar[TOTAL_VNCHARS] = { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a^ -> a + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a( -> a + 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] + 40, 41, // DD dd [mapped to D, d] + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 4: e^ -> e + 68, 69, 70, 71, 72, 73, // fgh + 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 7: o^ -> o + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 8: o+ -> o + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 10: u+ -> u + 166, 167, 168, 169, 170, 171, // vwx + 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] + 184, 185, // z + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; -int StdVnNoTone[TOTAL_VNCHARS] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] - 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, // a^ - 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, // a( - 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] - 42, 43, // DD dd - 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] - 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, // 4: e^ - 68, 69, 70, 71, 72, 73, // fgh - 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn - 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] - 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, // 7: o^ - 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, // 8: o+ - 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst - 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] - 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, // 10: u+ - 166, 167, 168, 169, 170, 171, // vwx - 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] - 184, 185, // z - 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; +int StdVnNoTone[TOTAL_VNCHARS] = { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // a [A=0] + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, // a^ + 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, // a( + 36, 37, 38, 39, 40, 41, // bcd [D=40, d=41] + 42, 43, // DD dd + 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, // 3: e [E = 44] + 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, // 4: e^ + 68, 69, 70, 71, 72, 73, // fgh + 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, // 5: i + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // jklmn + 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, // 6: o [o=96] + 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, // 7: o^ + 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, // 8: o+ + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, // pqrst + 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, // 9: u [U=142] + 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, // 10: u+ + 166, 167, 168, 169, 170, 171, // vwx + 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, // 11: y [Y=172] + 184, 185, // z + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212}; diff --git a/unikey/core/data.h b/unikey/core/data.h index e402ab4d..fa8e97b3 100644 --- a/unikey/core/data.h +++ b/unikey/core/data.h @@ -7,9 +7,12 @@ #define VIETNAMESE_CHARSET_DATA_H // This header defines some special characters -const StdVnChar StdStartQuote = (VnStdCharOffset + 201); // 0x93 in the Western charset +const StdVnChar StdStartQuote = + (VnStdCharOffset + 201); // 0x93 in the Western charset // 201 is the offset of character 0x93 (start quote) in Vn charsets -const StdVnChar StdEndQuote = (VnStdCharOffset + 202); // 0x94 in the Western charset -const StdVnChar StdEllipsis = (VnStdCharOffset + 190); // 0x85 in Western charet. +const StdVnChar StdEndQuote = + (VnStdCharOffset + 202); // 0x94 in the Western charset +const StdVnChar StdEllipsis = + (VnStdCharOffset + 190); // 0x85 in Western charet. #endif diff --git a/unikey/core/inputproc.cpp b/unikey/core/inputproc.cpp index bc8f46f4..545c931f 100644 --- a/unikey/core/inputproc.cpp +++ b/unikey/core/inputproc.cpp @@ -17,58 +17,108 @@ unsigned char WordBreakSyms[] = { '_', '~', '`', '@', '#', '$', '%', '^', '&', '(', ')', '{', '}', '[', ']'}; */ -constexpr UkKeyEvName lexi(VnLexiName v) { - return static_cast(static_cast(vneCount) + static_cast(v)); -} - -const std::unordered_set WordBreakSyms = {',', ';', ':', '.', '\"', '\'', '!', '?', ' ', '<', '>', '=', '+', '-', '*', - '/', '\\', '_', '@', '#', '$', '%', '&', '(', ')', '{', '}', '[', ']', '|'}; // we excluded ~, `, ^ +const std::unordered_set WordBreakSyms = { + ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', '<', + '>', '=', '+', '-', '*', '/', '\\', '_', '@', '#', + '$', '%', '&', '(', ')', '{', '}', '[', ']', '|'}; // we excluded ~, `, ^ -VnLexiName AZLexiUpper[] = {vnl_A, vnl_B, vnl_C, vnl_D, vnl_E, vnl_F, vnl_G, vnl_H, vnl_I, vnl_J, vnl_K, vnl_L, vnl_M, - vnl_N, vnl_O, vnl_P, vnl_Q, vnl_R, vnl_S, vnl_T, vnl_U, vnl_V, vnl_W, vnl_X, vnl_Y, vnl_Z}; +VnLexiName AZLexiUpper[] = {vnl_A, vnl_B, vnl_C, vnl_D, vnl_E, vnl_F, vnl_G, + vnl_H, vnl_I, vnl_J, vnl_K, vnl_L, vnl_M, vnl_N, + vnl_O, vnl_P, vnl_Q, vnl_R, vnl_S, vnl_T, vnl_U, + vnl_V, vnl_W, vnl_X, vnl_Y, vnl_Z}; -VnLexiName AZLexiLower[] = {vnl_a, vnl_b, vnl_c, vnl_d, vnl_e, vnl_f, vnl_g, vnl_h, vnl_i, vnl_j, vnl_k, vnl_l, vnl_m, - vnl_n, vnl_o, vnl_p, vnl_q, vnl_r, vnl_s, vnl_t, vnl_u, vnl_v, vnl_w, vnl_x, vnl_y, vnl_z}; +VnLexiName AZLexiLower[] = {vnl_a, vnl_b, vnl_c, vnl_d, vnl_e, vnl_f, vnl_g, + vnl_h, vnl_i, vnl_j, vnl_k, vnl_l, vnl_m, vnl_n, + vnl_o, vnl_p, vnl_q, vnl_r, vnl_s, vnl_t, vnl_u, + vnl_v, vnl_w, vnl_x, vnl_y, vnl_z}; -UkCharType UkcMap[256]; +UkCharType UkcMap[256]; struct _ascVnLexi { - int asc; + int asc; VnLexiName lexi; }; // List of western characters outside range A-Z that are // also Vietnamese characters -_ascVnLexi AscVnLexiList[] = {{0xC0, vnl_A2}, {0xC1, vnl_A1}, {0xC2, vnl_Ar}, {0xC2, vnl_A4}, {0xC8, vnl_E2}, {0xC9, vnl_E1}, {0xCA, vnl_Er}, {0xCC, vnl_I2}, {0xCD, vnl_I1}, - {0xD2, vnl_O2}, {0xD3, vnl_O1}, {0xD4, vnl_Or}, {0xD5, vnl_O4}, {0xD9, vnl_U2}, {0xDA, vnl_U1}, {0xDD, vnl_Y1}, {0xE0, vnl_a2}, {0xE1, vnl_a1}, - {0xE2, vnl_ar}, {0xE3, vnl_a4}, {0xE8, vnl_e2}, {0xE9, vnl_e1}, {0xEA, vnl_er}, {0xEC, vnl_i2}, {0xED, vnl_i1}, {0xF2, vnl_o2}, {0xF3, vnl_o1}, - {0xF4, vnl_or}, {0xF5, vnl_o4}, {0xF9, vnl_u2}, {0xFA, vnl_u1}, {0xFD, vnl_y1}, {0x00, vnl_nonVnChar}}; +_ascVnLexi AscVnLexiList[] = { + {0xC0, vnl_A2}, {0xC1, vnl_A1}, {0xC2, vnl_Ar}, {0xC2, vnl_A4}, + {0xC8, vnl_E2}, {0xC9, vnl_E1}, {0xCA, vnl_Er}, {0xCC, vnl_I2}, + {0xCD, vnl_I1}, {0xD2, vnl_O2}, {0xD3, vnl_O1}, {0xD4, vnl_Or}, + {0xD5, vnl_O4}, {0xD9, vnl_U2}, {0xDA, vnl_U1}, {0xDD, vnl_Y1}, + {0xE0, vnl_a2}, {0xE1, vnl_a1}, {0xE2, vnl_ar}, {0xE3, vnl_a4}, + {0xE8, vnl_e2}, {0xE9, vnl_e1}, {0xEA, vnl_er}, {0xEC, vnl_i2}, + {0xED, vnl_i1}, {0xF2, vnl_o2}, {0xF3, vnl_o1}, {0xF4, vnl_or}, + {0xF5, vnl_o4}, {0xF9, vnl_u2}, {0xFA, vnl_u1}, {0xFD, vnl_y1}, + {0x00, vnl_nonVnChar}}; VnLexiName IsoVnLexiMap[256]; -bool ClassifierTableInitialized = false; - -DllExport UkKeyMapping TelexMethodMapping[] = {{'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, {'X', vneTone4}, {'J', vneTone5}, - {'W', vne_telex_w}, {'A', vneRoof_a}, {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {'[', lexi(vnl_oh)}, - {']', lexi(vnl_uh)}, {'{', lexi(vnl_Oh)}, {'}', lexi(vnl_Uh)}, {0, vneNormal}}; - -DllExport UkKeyMapping SimpleTelexMethodMapping[] = {{'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, {'X', vneTone4}, {'J', vneTone5}, - {'W', vneHookAll}, {'A', vneRoof_a}, {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; - -DllExport UkKeyMapping SimpleTelex2MethodMapping[] = {{'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, {'X', vneTone4}, {'J', vneTone5}, - {'W', vne_telex_w}, {'A', vneRoof_a}, {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; - -DllExport UkKeyMapping VniMethodMapping[] = {{'0', vneTone0}, {'1', vneTone1}, {'2', vneTone2}, {'3', vneTone3}, {'4', vneTone4}, {'5', vneTone5}, - {'6', vneRoofAll}, {'7', vneHook_uo}, {'8', vneBowl}, {'9', vneDd}, {0, vneNormal}}; - -DllExport UkKeyMapping VIQRMethodMapping[] = {{'0', vneTone0}, {'\'', vneTone1}, {'`', vneTone2}, {'?', vneTone3}, {'~', vneTone4}, {'.', vneTone5}, {'^', vneRoofAll}, - {'+', vneHook_uo}, {'*', vneHook_uo}, {'(', vneBowl}, {'D', vneDd}, {'\\', vneEscChar}, {0, vneNormal}}; - -DllExport UkKeyMapping MsViMethodMapping[] = {{'5', vneTone2}, {'%', vneTone2}, {'6', vneTone3}, {'^', vneTone3}, {'7', vneTone4}, - {'&', vneTone4}, {'8', vneTone1}, {'*', vneTone1}, {'9', vneTone5}, {'(', vneTone5}, - {'1', lexi(vnl_ab)}, {'!', lexi(vnl_Ab)}, {'2', lexi(vnl_ar)}, {'@', lexi(vnl_Ar)}, {'3', lexi(vnl_er)}, - {'#', lexi(vnl_Er)}, {'4', lexi(vnl_or)}, {'$', lexi(vnl_Or)}, {'0', lexi(vnl_dd)}, {')', lexi(vnl_DD)}, - {'[', lexi(vnl_uh)}, {']', lexi(vnl_oh)}, {'{', lexi(vnl_Uh)}, {'}', lexi(vnl_Oh)}, {0, vneNormal}}; +bool ClassifierTableInitialized = false; + +DllExport UkKeyMapping TelexMethodMapping[] = {{'Z', vneTone0}, + {'S', vneTone1}, + {'F', vneTone2}, + {'R', vneTone3}, + {'X', vneTone4}, + {'J', vneTone5}, + {'W', vne_telex_w}, + {'A', vneRoof_a}, + {'E', vneRoof_e}, + {'O', vneRoof_o}, + {'D', vneDd}, + {'[', lexi(vnl_oh)}, + {']', lexi(vnl_uh)}, + {'{', lexi(vnl_Oh)}, + {'}', lexi(vnl_Uh)}, + {0, vneNormal}}; + +DllExport UkKeyMapping SimpleTelexMethodMapping[] = { + {'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, + {'X', vneTone4}, {'J', vneTone5}, {'W', vneHookAll}, {'A', vneRoof_a}, + {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping SimpleTelex2MethodMapping[] = { + {'Z', vneTone0}, {'S', vneTone1}, {'F', vneTone2}, {'R', vneTone3}, + {'X', vneTone4}, {'J', vneTone5}, {'W', vne_telex_w}, {'A', vneRoof_a}, + {'E', vneRoof_e}, {'O', vneRoof_o}, {'D', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping VniMethodMapping[] = { + {'0', vneTone0}, {'1', vneTone1}, {'2', vneTone2}, {'3', vneTone3}, + {'4', vneTone4}, {'5', vneTone5}, {'6', vneRoofAll}, {'7', vneHook_uo}, + {'8', vneBowl}, {'9', vneDd}, {0, vneNormal}}; + +DllExport UkKeyMapping VIQRMethodMapping[] = { + {'0', vneTone0}, {'\'', vneTone1}, {'`', vneTone2}, {'?', vneTone3}, + {'~', vneTone4}, {'.', vneTone5}, {'^', vneRoofAll}, {'+', vneHook_uo}, + {'*', vneHook_uo}, {'(', vneBowl}, {'D', vneDd}, {'\\', vneEscChar}, + {0, vneNormal}}; + +DllExport UkKeyMapping MsViMethodMapping[] = {{'5', vneTone2}, + {'%', vneTone2}, + {'6', vneTone3}, + {'^', vneTone3}, + {'7', vneTone4}, + {'&', vneTone4}, + {'8', vneTone1}, + {'*', vneTone1}, + {'9', vneTone5}, + {'(', vneTone5}, + {'1', lexi(vnl_ab)}, + {'!', lexi(vnl_Ab)}, + {'2', lexi(vnl_ar)}, + {'@', lexi(vnl_Ar)}, + {'3', lexi(vnl_er)}, + {'#', lexi(vnl_Er)}, + {'4', lexi(vnl_or)}, + {'$', lexi(vnl_Or)}, + {'0', lexi(vnl_dd)}, + {')', lexi(vnl_DD)}, + {'[', lexi(vnl_uh)}, + {']', lexi(vnl_oh)}, + {'{', lexi(vnl_Uh)}, + {'}', lexi(vnl_Oh)}, + {0, vneNormal}}; //------------------------------------------- void SetupInputClassifierTable() { @@ -76,7 +126,7 @@ void SetupInputClassifierTable() { ClassifierTableInitialized = true; } unsigned int c; - int i; + int i; for (c = 0; c <= 32; c++) { UkcMap[c] = ukcReset; @@ -138,13 +188,27 @@ void UkInputProcessor::init() { int UkInputProcessor::setIM(UkInputMethod im) { m_im = im; switch (im) { - case UkTelex: useBuiltIn(TelexMethodMapping); break; - case UkSimpleTelex: useBuiltIn(SimpleTelexMethodMapping); break; - case UkSimpleTelex2: useBuiltIn(SimpleTelex2MethodMapping); break; - case UkVni: useBuiltIn(VniMethodMapping); break; - case UkViqr: useBuiltIn(VIQRMethodMapping); break; - case UkMsVi: useBuiltIn(MsViMethodMapping); break; - default: m_im = UkTelex; useBuiltIn(TelexMethodMapping); + case UkTelex: + useBuiltIn(TelexMethodMapping); + break; + case UkSimpleTelex: + useBuiltIn(SimpleTelexMethodMapping); + break; + case UkSimpleTelex2: + useBuiltIn(SimpleTelex2MethodMapping); + break; + case UkVni: + useBuiltIn(VniMethodMapping); + break; + case UkViqr: + useBuiltIn(VIQRMethodMapping); + break; + case UkMsVi: + useBuiltIn(MsViMethodMapping); + break; + default: + m_im = UkTelex; + useBuiltIn(TelexMethodMapping); } return 1; } @@ -166,7 +230,7 @@ void UkResetKeyMap(int keyMap[256]) { } //------------------------------------------- -void UkInputProcessor::useBuiltIn(UkKeyMapping* map) { +void UkInputProcessor::useBuiltIn(UkKeyMapping *map) { UkResetKeyMap(m_keyMap); for (int i = 0; map[i].key; i++) { m_keyMap[map[i].key] = map[i].action; @@ -181,15 +245,15 @@ void UkInputProcessor::useBuiltIn(UkKeyMapping* map) { } //------------------------------------------- -void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent& ev) { +void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev) { ev.keyCode = keyCode; if (keyCode == 0) { ev.evType = vneNormal; - ev.vnSym = vnl_nonVnChar; + ev.vnSym = vnl_nonVnChar; ev.chType = ukcWordBreak; } else if (keyCode > 255) { ev.evType = vneNormal; - ev.vnSym = IsoToVnLexi(keyCode); + ev.vnSym = IsoToVnLexi(keyCode); ev.chType = (ev.vnSym == vnl_nonVnChar) ? ukcNonVn : ukcVn; } else { ev.chType = UkcMap[keyCode]; @@ -201,7 +265,7 @@ void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent& ev) { if (ev.evType >= vneCount) { ev.chType = ukcVn; - ev.vnSym = (VnLexiName)(ev.evType - vneCount); + ev.vnSym = (VnLexiName)(ev.evType - vneCount); ev.evType = vneMapChar; } else { ev.vnSym = IsoToVnLexi(keyCode); @@ -214,10 +278,10 @@ void UkInputProcessor::keyCodeToEvent(unsigned int keyCode, UkKeyEvent& ev) { // Key strokes are simply considered character input, not action keys as in // keyCodeToEvent method //---------------------------------------------------------------- -void UkInputProcessor::keyCodeToSymbol(unsigned int keyCode, UkKeyEvent& ev) { +void UkInputProcessor::keyCodeToSymbol(unsigned int keyCode, UkKeyEvent &ev) { ev.keyCode = keyCode; - ev.evType = vneNormal; - ev.vnSym = IsoToVnLexi(keyCode); + ev.evType = vneNormal; + ev.vnSym = IsoToVnLexi(keyCode); if (keyCode > 255) { ev.chType = (ev.vnSym == vnl_nonVnChar) ? ukcNonVn : ukcVn; } else { diff --git a/unikey/core/inputproc.h b/unikey/core/inputproc.h index 5dc71e9c..dabfbcb8 100644 --- a/unikey/core/inputproc.h +++ b/unikey/core/inputproc.h @@ -48,60 +48,57 @@ enum UkKeyEvName { vneCount // just to count how many event types there are }; -enum UkCharType { - ukcVn, - ukcWordBreak, - ukcNonVn, - ukcReset -}; +enum UkCharType { ukcVn, ukcWordBreak, ukcNonVn, ukcReset }; struct UkKeyEvent { - int evType; - UkCharType chType; - VnLexiName vnSym; // meaningful only when chType==ukcVn + int evType; + UkCharType chType; + VnLexiName vnSym; // meaningful only when chType==ukcVn unsigned int keyCode; - int tone; // meaningful only when this is a vowel + int tone; // meaningful only when this is a vowel }; struct UkKeyMapping { unsigned char key; - int action; + int action; }; /////////////////////////////////////////// class UkInputProcessor { - public: +public: // don't do anything with constructor, because // this object can be allocated in shared memory // Use init method instead // UkInputProcessor(); - void init(); + void init(); - UkInputMethod getIM() const { - return m_im; - } + UkInputMethod getIM() const { return m_im; } - void keyCodeToEvent(unsigned int keyCode, UkKeyEvent& ev); - void keyCodeToSymbol(unsigned int keyCode, UkKeyEvent& ev); - int setIM(UkInputMethod im); - int setIM(int map[256]); - void getKeyMap(int map[256]) const; + void keyCodeToEvent(unsigned int keyCode, UkKeyEvent &ev); + void keyCodeToSymbol(unsigned int keyCode, UkKeyEvent &ev); + int setIM(UkInputMethod im); + int setIM(int map[256]); + void getKeyMap(int map[256]) const; UkCharType getCharType(unsigned int keyCode) const; - protected: - static bool m_classInit; +protected: + static bool m_classInit; UkInputMethod m_im; - int m_keyMap[256]; + int m_keyMap[256]; - void useBuiltIn(UkKeyMapping* map); + void useBuiltIn(UkKeyMapping *map); }; -void UkResetKeyMap(int keyMap[256]); -void SetupInputClassifierTable(); +inline constexpr UkKeyEvName lexi(VnLexiName v) { + return static_cast( + static_cast(vneCount) + static_cast(v)); +} +void UkResetKeyMap(int keyMap[256]); +void SetupInputClassifierTable(); DllInterface extern UkKeyMapping TelexMethodMapping[]; DllInterface extern UkKeyMapping SimpleTelexMethodMapping[]; @@ -110,8 +107,8 @@ DllInterface extern UkKeyMapping VniMethodMapping[]; DllInterface extern UkKeyMapping VIQRMethodMapping[]; DllInterface extern UkKeyMapping MsViMethodMapping[]; -extern VnLexiName IsoVnLexiMap[]; -inline VnLexiName IsoToVnLexi(unsigned int keyCode) { +extern VnLexiName IsoVnLexiMap[]; +inline VnLexiName IsoToVnLexi(unsigned int keyCode) { return (keyCode >= 256) ? vnl_nonVnChar : IsoVnLexiMap[keyCode]; } diff --git a/unikey/core/keycons.h b/unikey/core/keycons.h index 0900a168..cdfcb329 100644 --- a/unikey/core/keycons.h +++ b/unikey/core/keycons.h @@ -10,8 +10,8 @@ #define MAX_MACRO_KEY_LEN 16 // #define MAX_MACRO_TEXT_LEN 256 #define MAX_MACRO_TEXT_LEN 1024 -#define MAX_MACRO_ITEMS 1024 -#define MAX_MACRO_LINE (MAX_MACRO_TEXT_LEN + MAX_MACRO_KEY_LEN) +#define MAX_MACRO_ITEMS 1024 +#define MAX_MACRO_LINE (MAX_MACRO_TEXT_LEN + MAX_MACRO_KEY_LEN) #define MACRO_MEM_SIZE (1024 * 128) // 128 KB @@ -39,34 +39,31 @@ struct UnikeyOptions { int autoNonVnRestore; }; -#define UKOPT_FLAG_ALL 0xFFFFFFFF +#define UKOPT_FLAG_ALL 0xFFFFFFFF #define UKOPT_FLAG_FREE_STYLE 0x00000001 // #define UKOPT_FLAG_MANUAL_TONE 0x00000002 -#define UKOPT_FLAG_MODERN 0x00000004 -#define UKOPT_FLAG_MACRO_ENABLED 0x00000008 -#define UKOPT_FLAG_USE_CLIPBOARD 0x00000010 -#define UKOPT_FLAG_ALWAYS_MACRO 0x00000020 -#define UKOPT_FLAG_STRICT_SPELL 0x00000040 -#define UKOPT_FLAG_USE_IME 0x00000080 +#define UKOPT_FLAG_MODERN 0x00000004 +#define UKOPT_FLAG_MACRO_ENABLED 0x00000008 +#define UKOPT_FLAG_USE_CLIPBOARD 0x00000010 +#define UKOPT_FLAG_ALWAYS_MACRO 0x00000020 +#define UKOPT_FLAG_STRICT_SPELL 0x00000040 +#define UKOPT_FLAG_USE_IME 0x00000080 #define UKOPT_FLAG_SPELLCHECK_ENABLED 0x00000100 #if defined(WIN32) typedef struct _UnikeySysInfo UnikeySysInfo; struct _UnikeySysInfo { - int switchKey; + int switchKey; HHOOK keyHook; HHOOK mouseHook; - HWND hMainDlg; - UINT iconMsgId; + HWND hMainDlg; + UINT iconMsgId; HICON hVietIcon, hEnIcon; - int unicodePlatform; + int unicodePlatform; DWORD winMajorVersion, winMinorVersion; }; #endif -typedef enum { - UkCharOutput, - UkKeyOutput -} UkOutputType; +typedef enum { UkCharOutput, UkKeyOutput } UkOutputType; #endif diff --git a/unikey/core/mactab.cpp b/unikey/core/mactab.cpp index b54fd536..990637b2 100644 --- a/unikey/core/mactab.cpp +++ b/unikey/core/mactab.cpp @@ -16,22 +16,28 @@ using namespace std; //--------------------------------------------------------------- void CMacroTable::init() { - m_memSize = MACRO_MEM_SIZE; - m_count = 0; + m_memSize = MACRO_MEM_SIZE; + m_count = 0; m_occupied = 0; } //--------------------------------------------------------------- -char* MacCompareStartMem; +char *MacCompareStartMem; -#define STD_TO_LOWER(x) (((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && !((x) & 1)) ? (x + 1) : (x)) +#define STD_TO_LOWER(x) \ + (((x) >= VnStdCharOffset && \ + (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && !((x) & 1)) \ + ? (x + 1) \ + : (x)) -int macCompare(const void* p1, const void* p2) { - StdVnChar* s1 = (StdVnChar*)((char*)MacCompareStartMem + ((MacroDef*)p1)->keyOffset); - StdVnChar* s2 = (StdVnChar*)((char*)MacCompareStartMem + ((MacroDef*)p2)->keyOffset); +int macCompare(const void *p1, const void *p2) { + StdVnChar *s1 = + (StdVnChar *)((char *)MacCompareStartMem + ((MacroDef *)p1)->keyOffset); + StdVnChar *s2 = + (StdVnChar *)((char *)MacCompareStartMem + ((MacroDef *)p2)->keyOffset); - int i; - StdVnChar ls1, ls2; + int i; + StdVnChar ls1, ls2; for (i = 0; s1[i] != 0 && s2[i] != 0; i++) { ls1 = STD_TO_LOWER(s1[i]); @@ -53,12 +59,13 @@ int macCompare(const void* p1, const void* p2) { } //--------------------------------------------------------------- -int macKeyCompare(const void* key, const void* ele) { - StdVnChar* s1 = (StdVnChar*)key; - StdVnChar* s2 = (StdVnChar*)((char*)MacCompareStartMem + ((MacroDef*)ele)->keyOffset); +int macKeyCompare(const void *key, const void *ele) { + StdVnChar *s1 = (StdVnChar *)key; + StdVnChar *s2 = (StdVnChar *)((char *)MacCompareStartMem + + ((MacroDef *)ele)->keyOffset); - StdVnChar ls1, ls2; - int i; + StdVnChar ls1, ls2; + int i; for (i = 0; s1[i] != 0 && s2[i] != 0; i++) { ls1 = STD_TO_LOWER(s1[i]); ls2 = STD_TO_LOWER(s2[i]); @@ -79,11 +86,12 @@ int macKeyCompare(const void* key, const void* ele) { } //--------------------------------------------------------------- -const StdVnChar* CMacroTable::lookup(StdVnChar* key) { +const StdVnChar *CMacroTable::lookup(StdVnChar *key) { MacCompareStartMem = m_macroMem; - MacroDef* p = (MacroDef*)bsearch(key, m_table, m_count, sizeof(MacroDef), macKeyCompare); + MacroDef *p = (MacroDef *)bsearch(key, m_table, m_count, sizeof(MacroDef), + macKeyCompare); if (p) - return (StdVnChar*)(m_macroMem + p->textOffset); + return (StdVnChar *)(m_macroMem + p->textOffset); return 0; } @@ -94,7 +102,7 @@ const StdVnChar* CMacroTable::lookup(StdVnChar* key) { // // Header format: ;[DO NOT DELETE THIS LINE]***version=n //---------------------------------------------------------------------------- -bool CMacroTable::readHeader(FILE* f, int& version) { +bool CMacroTable::readHeader(FILE *f, int &version) { char line[MAX_MACRO_LINE]; if (!fgets(line, sizeof(line), f)) { if (feof(f)) { @@ -106,9 +114,10 @@ bool CMacroTable::readHeader(FILE* f, int& version) { } // if BOM is available, skip it - char* p = line; + char *p = line; size_t len = strlen(line); - if (len >= 3 && (unsigned char)line[0] == 0xEF && (unsigned char)line[1] == 0xBB && (unsigned char)line[2] == 0xBF) { + if (len >= 3 && (unsigned char)line[0] == 0xEF && + (unsigned char)line[1] == 0xBB && (unsigned char)line[2] == 0xBF) { p += 3; } @@ -129,16 +138,18 @@ bool CMacroTable::readHeader(FILE* f, int& version) { } //---------------------------------------------------------------- -void CMacroTable::writeHeader(FILE* f) { +void CMacroTable::writeHeader(FILE *f) { #if defined(WIN32) - fprintf(f, "\xEF\xBB\xBF;DO NOT DELETE THIS LINE*** version=%d ***\n", UKMACRO_VERSION_UTF8); + fprintf(f, "\xEF\xBB\xBF;DO NOT DELETE THIS LINE*** version=%d ***\n", + UKMACRO_VERSION_UTF8); #else - fprintf(f, "DO NOT DELETE THIS LINE*** version=%d ***\n", UKMACRO_VERSION_UTF8); + fprintf(f, "DO NOT DELETE THIS LINE*** version=%d ***\n", + UKMACRO_VERSION_UTF8); #endif } //--------------------------------------------------------------- -int CMacroTable::loadFromFile(const char* fname) { - FILE* f; +int CMacroTable::loadFromFile(const char *fname) { + FILE *f; #if defined(WIN32) f = _tfopen(fname, _TEXT("rt")); #else @@ -147,7 +158,7 @@ int CMacroTable::loadFromFile(const char* fname) { if (f == NULL) return 0; - char line[MAX_MACRO_LINE]; + char line[MAX_MACRO_LINE]; size_t len; resetContent(); @@ -180,13 +191,13 @@ int CMacroTable::loadFromFile(const char* fname) { } //--------------------------------------------------------------- -int CMacroTable::writeToFile(const char* fname) { - FILE* f; +int CMacroTable::writeToFile(const char *fname) { + FILE *f; f = fopen(fname, "w"); return writeToFp(f); } -int CMacroTable::writeToFp(FILE* f) { +int CMacroTable::writeToFp(FILE *f) { int ret; int inLen, maxOutLen; @@ -199,19 +210,21 @@ int CMacroTable::writeToFp(FILE* f) { writeHeader(f); - UKBYTE* p; + UKBYTE *p; for (int i = 0; i < m_count; i++) { - p = (UKBYTE*)m_macroMem + m_table[i].keyOffset; - inLen = -1; + p = (UKBYTE *)m_macroMem + m_table[i].keyOffset; + inLen = -1; maxOutLen = sizeof(key); - ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, (UKBYTE*)p, (UKBYTE*)key, &inLen, &maxOutLen); + ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, + (UKBYTE *)p, (UKBYTE *)key, &inLen, &maxOutLen); if (ret != 0) continue; - p = (UKBYTE*)m_macroMem + m_table[i].textOffset; - inLen = -1; + p = (UKBYTE *)m_macroMem + m_table[i].textOffset; + inLen = -1; maxOutLen = sizeof(text); - ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, p, (UKBYTE*)text, &inLen, &maxOutLen); + ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8, p, + (UKBYTE *)text, &inLen, &maxOutLen); if (ret != 0) continue; if (i < m_count - 1) @@ -226,11 +239,11 @@ int CMacroTable::writeToFp(FILE* f) { } //--------------------------------------------------------------- -int CMacroTable::addItem(const void* key, const void* text, int charset) { - int ret; - int inLen, maxOutLen; - int offset = m_occupied; - char* p = m_macroMem + offset; +int CMacroTable::addItem(const void *key, const void *text, int charset) { + int ret; + int inLen, maxOutLen; + int offset = m_occupied; + char *p = m_macroMem + offset; if (m_count >= MAX_MACRO_ITEMS) return -1; @@ -238,11 +251,12 @@ int CMacroTable::addItem(const void* key, const void* text, int charset) { m_table[m_count].keyOffset = offset; // Convert macro key to VN standard - inLen = -1; // input is null-terminated + inLen = -1; // input is null-terminated maxOutLen = MAX_MACRO_KEY_LEN * sizeof(StdVnChar); if (maxOutLen + offset > m_memSize) maxOutLen = m_memSize - offset; - ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE*)key, (UKBYTE*)p, &inLen, &maxOutLen); + ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE *)key, + (UKBYTE *)p, &inLen, &maxOutLen); if (ret != 0) return -1; @@ -251,11 +265,12 @@ int CMacroTable::addItem(const void* key, const void* text, int charset) { // convert macro text to VN standard m_table[m_count].textOffset = offset; - inLen = -1; // input is null-terminated - maxOutLen = MAX_MACRO_TEXT_LEN * sizeof(StdVnChar); + inLen = -1; // input is null-terminated + maxOutLen = MAX_MACRO_TEXT_LEN * sizeof(StdVnChar); if (maxOutLen + offset > m_memSize) maxOutLen = m_memSize - offset; - ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE*)text, (UKBYTE*)p, &inLen, &maxOutLen); + ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD, (UKBYTE *)text, + (UKBYTE *)p, &inLen, &maxOutLen); if (ret != 0) return -1; @@ -268,11 +283,11 @@ int CMacroTable::addItem(const void* key, const void* text, int charset) { // add a new macro into the sorted macro table // item format: key:text (key and text are separated by a colon) //--------------------------------------------------------------- -int CMacroTable::addItem(const char* item, int charset) { +int CMacroTable::addItem(const char *item, int charset) { char key[MAX_MACRO_KEY_LEN]; // Parse the input item - char* pos = (char*)strchr(item, ':'); + char *pos = (char *)strchr(item, ':'); if (pos == NULL) return -1; int keyLen = (int)(pos - item); @@ -286,19 +301,19 @@ int CMacroTable::addItem(const char* item, int charset) { //--------------------------------------------------------------- void CMacroTable::resetContent() { m_occupied = 0; - m_count = 0; + m_count = 0; } //--------------------------------------------------------------- -const StdVnChar* CMacroTable::getKey(int idx) const { +const StdVnChar *CMacroTable::getKey(int idx) const { if (idx < 0 || idx >= m_count) return 0; - return (StdVnChar*)(m_macroMem + m_table[idx].keyOffset); + return (StdVnChar *)(m_macroMem + m_table[idx].keyOffset); } //--------------------------------------------------------------- -const StdVnChar* CMacroTable::getText(int idx) const { +const StdVnChar *CMacroTable::getText(int idx) const { if (idx < 0 || idx >= m_count) return 0; - return (StdVnChar*)(m_macroMem + m_table[idx].textOffset); + return (StdVnChar *)(m_macroMem + m_table[idx].textOffset); } diff --git a/unikey/core/mactab.h b/unikey/core/mactab.h index f9ac6ea1..3fc53e1e 100644 --- a/unikey/core/mactab.h +++ b/unikey/core/mactab.h @@ -32,31 +32,29 @@ typedef char TCHAR; #endif class DllInterface CMacroTable { - public: - void init(); - int loadFromFile(const char* fname); - int writeToFile(const char* fname); - int writeToFp(FILE* f); - - const StdVnChar* lookup(StdVnChar* key); - const StdVnChar* getKey(int idx) const; - const StdVnChar* getText(int idx) const; - int getCount() const { - return m_count; - } +public: + void init(); + int loadFromFile(const char *fname); + int writeToFile(const char *fname); + int writeToFp(FILE *f); + + const StdVnChar *lookup(StdVnChar *key); + const StdVnChar *getKey(int idx) const; + const StdVnChar *getText(int idx) const; + int getCount() const { return m_count; } void resetContent(); - int addItem(const char* item, int charset); - int addItem(const void* key, const void* text, int charset); + int addItem(const char *item, int charset); + int addItem(const void *key, const void *text, int charset); - protected: - bool readHeader(FILE* f, int& version); - void writeHeader(FILE* f); +protected: + bool readHeader(FILE *f, int &version); + void writeHeader(FILE *f); MacroDef m_table[MAX_MACRO_ITEMS]; - char m_macroMem[MACRO_MEM_SIZE]; + char m_macroMem[MACRO_MEM_SIZE]; - int m_count; - int m_memSize, m_occupied; + int m_count; + int m_memSize, m_occupied; }; #endif diff --git a/unikey/core/pattern.cpp b/unikey/core/pattern.cpp index e061f761..30adc091 100644 --- a/unikey/core/pattern.cpp +++ b/unikey/core/pattern.cpp @@ -12,14 +12,14 @@ //---------------------------- void PatternState::reset() { - m_pos = 0; + m_pos = 0; m_found = 0; } //---------------------------- -void PatternState::init(char* pattern) { - m_pos = 0; - m_found = 0; +void PatternState::init(char *pattern) { + m_pos = 0; + m_found = 0; m_pattern = pattern; int i = 0, j = -1; @@ -45,13 +45,13 @@ int PatternState::foundAtNextChar(char ch) { if (m_pattern[m_pos] == 0) { m_found++; m_pos = m_border[m_pos]; - ret = 1; + ret = 1; } return ret; } //----------------------------------------------------- -void PatternList::init(char** patterns, int count) { +void PatternList::init(char **patterns, int count) { m_count = count; delete[] m_patterns; m_patterns = new PatternState[count]; diff --git a/unikey/core/pattern.h b/unikey/core/pattern.h index 43bfc4b6..611e43c9 100644 --- a/unikey/core/pattern.h +++ b/unikey/core/pattern.h @@ -19,26 +19,27 @@ #define MAX_PATTERN_LEN 40 class DllInterface PatternState { - public: - char* m_pattern; - int m_border[MAX_PATTERN_LEN + 1]; - int m_pos; - int m_found; - void init(char* pattern); - void reset(); - int foundAtNextChar(char ch); // get next input char, returns 1 if pattern is found. +public: + char *m_pattern; + int m_border[MAX_PATTERN_LEN + 1]; + int m_pos; + int m_found; + void init(char *pattern); + void reset(); + int foundAtNextChar( + char ch); // get next input char, returns 1 if pattern is found. }; class DllInterface PatternList { - public: - PatternState* m_patterns; - int m_count; - void init(char** patterns, int count); - int foundAtNextChar(char ch); - void reset(); +public: + PatternState *m_patterns; + int m_count; + void init(char **patterns, int count); + int foundAtNextChar(char ch); + void reset(); PatternList() { - m_count = 0; + m_count = 0; m_patterns = 0; } diff --git a/unikey/core/ukengine.cpp b/unikey/core/ukengine.cpp index 2b4485f7..0e619be7 100644 --- a/unikey/core/ukengine.cpp +++ b/unikey/core/ukengine.cpp @@ -25,189 +25,803 @@ using namespace std; #define ENTER_CHAR 13 -#define IS_ODD(x) (x & 1) +#define IS_ODD(x) (x & 1) #define IS_EVEN(x) (!(x & 1)) -#define IS_STD_VN_LOWER(x) ((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(x)) -#define IS_STD_VN_UPPER(x) ((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(x)) +#define IS_STD_VN_LOWER(x) \ + ((x) >= VnStdCharOffset && \ + (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(x)) +#define IS_STD_VN_UPPER(x) \ + ((x) >= VnStdCharOffset && \ + (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(x)) -bool IsVnVowel[vnl_lastChar]; +bool IsVnVowel[vnl_lastChar]; extern VnLexiName AZLexiUpper[]; // defined in inputproc.cpp extern VnLexiName AZLexiLower[]; // see vnconv/data.cpp for explanation of these characters -unsigned char SpecialWesternChars[] = {0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F, 0x00}; +unsigned char SpecialWesternChars[] = { + 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, + 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9E, 0x9F, 0x00}; -StdVnChar IsoStdVnCharMap[256]; +StdVnChar IsoStdVnCharMap[256]; inline StdVnChar IsoToStdVnChar(int keyCode) { return (keyCode < 256) ? IsoStdVnCharMap[keyCode] : keyCode; } struct VowelSeqInfo { - int len; - int complete; - int conSuffix; // allow consonnant suffix + int len; + int complete; + int conSuffix; // allow consonnant suffix VnLexiName v[3]; - VowelSeq sub[3]; + VowelSeq sub[3]; - int roofPos; - VowelSeq withRoof; + int roofPos; + VowelSeq withRoof; - int hookPos; - VowelSeq withHook; // hook & bowl + int hookPos; + VowelSeq withHook; // hook & bowl }; -VowelSeqInfo VSeqList[] = {{1, 1, 1, {vnl_a, vnl_nonVnChar, vnl_nonVnChar}, {vs_a, vs_nil, vs_nil}, -1, vs_ar, -1, vs_ab}, - {1, 1, 1, {vnl_ar, vnl_nonVnChar, vnl_nonVnChar}, {vs_ar, vs_nil, vs_nil}, 0, vs_nil, -1, vs_ab}, - {1, 1, 1, {vnl_ab, vnl_nonVnChar, vnl_nonVnChar}, {vs_ab, vs_nil, vs_nil}, -1, vs_ar, 0, vs_nil}, - {1, 1, 1, {vnl_e, vnl_nonVnChar, vnl_nonVnChar}, {vs_e, vs_nil, vs_nil}, -1, vs_er, -1, vs_nil}, - {1, 1, 1, {vnl_er, vnl_nonVnChar, vnl_nonVnChar}, {vs_er, vs_nil, vs_nil}, 0, vs_nil, -1, vs_nil}, - {1, 1, 1, {vnl_i, vnl_nonVnChar, vnl_nonVnChar}, {vs_i, vs_nil, vs_nil}, -1, vs_nil, -1, vs_nil}, - {1, 1, 1, {vnl_o, vnl_nonVnChar, vnl_nonVnChar}, {vs_o, vs_nil, vs_nil}, -1, vs_or, -1, vs_oh}, - {1, 1, 1, {vnl_or, vnl_nonVnChar, vnl_nonVnChar}, {vs_or, vs_nil, vs_nil}, 0, vs_nil, -1, vs_oh}, - {1, 1, 1, {vnl_oh, vnl_nonVnChar, vnl_nonVnChar}, {vs_oh, vs_nil, vs_nil}, -1, vs_or, 0, vs_nil}, - {1, 1, 1, {vnl_u, vnl_nonVnChar, vnl_nonVnChar}, {vs_u, vs_nil, vs_nil}, -1, vs_nil, -1, vs_uh}, - {1, 1, 1, {vnl_uh, vnl_nonVnChar, vnl_nonVnChar}, {vs_uh, vs_nil, vs_nil}, -1, vs_nil, 0, vs_nil}, - {1, 1, 1, {vnl_y, vnl_nonVnChar, vnl_nonVnChar}, {vs_y, vs_nil, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_a, vnl_i, vnl_nonVnChar}, {vs_a, vs_ai, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_a, vnl_o, vnl_nonVnChar}, {vs_a, vs_ao, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_a, vnl_u, vnl_nonVnChar}, {vs_a, vs_au, vs_nil}, -1, vs_aru, -1, vs_nil}, - {2, 1, 0, {vnl_a, vnl_y, vnl_nonVnChar}, {vs_a, vs_ay, vs_nil}, -1, vs_ary, -1, vs_nil}, - {2, 1, 0, {vnl_ar, vnl_u, vnl_nonVnChar}, {vs_ar, vs_aru, vs_nil}, 0, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_ar, vnl_y, vnl_nonVnChar}, {vs_ar, vs_ary, vs_nil}, 0, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_e, vnl_o, vnl_nonVnChar}, {vs_e, vs_eo, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 0, 0, {vnl_e, vnl_u, vnl_nonVnChar}, {vs_e, vs_eu, vs_nil}, -1, vs_eru, -1, vs_nil}, - {2, 1, 0, {vnl_er, vnl_u, vnl_nonVnChar}, {vs_er, vs_eru, vs_nil}, 0, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_i, vnl_a, vnl_nonVnChar}, {vs_i, vs_ia, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 0, 1, {vnl_i, vnl_e, vnl_nonVnChar}, {vs_i, vs_ie, vs_nil}, -1, vs_ier, -1, vs_nil}, - {2, 1, 1, {vnl_i, vnl_er, vnl_nonVnChar}, {vs_i, vs_ier, vs_nil}, 1, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_i, vnl_u, vnl_nonVnChar}, {vs_i, vs_iu, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 1, 1, {vnl_o, vnl_a, vnl_nonVnChar}, {vs_o, vs_oa, vs_nil}, -1, vs_nil, -1, vs_oab}, - {2, 1, 1, {vnl_o, vnl_ab, vnl_nonVnChar}, {vs_o, vs_oab, vs_nil}, -1, vs_nil, 1, vs_nil}, - {2, 1, 1, {vnl_o, vnl_e, vnl_nonVnChar}, {vs_o, vs_oe, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_o, vnl_i, vnl_nonVnChar}, {vs_o, vs_oi, vs_nil}, -1, vs_ori, -1, vs_ohi}, - {2, 1, 0, {vnl_or, vnl_i, vnl_nonVnChar}, {vs_or, vs_ori, vs_nil}, 0, vs_nil, -1, vs_ohi}, - {2, 1, 0, {vnl_oh, vnl_i, vnl_nonVnChar}, {vs_oh, vs_ohi, vs_nil}, -1, vs_ori, 0, vs_nil}, - {2, 1, 1, {vnl_u, vnl_a, vnl_nonVnChar}, {vs_u, vs_ua, vs_nil}, -1, vs_uar, -1, vs_uha}, - {2, 1, 1, {vnl_u, vnl_ar, vnl_nonVnChar}, {vs_u, vs_uar, vs_nil}, 1, vs_nil, -1, vs_nil}, - {2, 0, 1, {vnl_u, vnl_e, vnl_nonVnChar}, {vs_u, vs_ue, vs_nil}, -1, vs_uer, -1, vs_nil}, - {2, 1, 1, {vnl_u, vnl_er, vnl_nonVnChar}, {vs_u, vs_uer, vs_nil}, 1, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_u, vnl_i, vnl_nonVnChar}, {vs_u, vs_ui, vs_nil}, -1, vs_nil, -1, vs_uhi}, - {2, 0, 1, {vnl_u, vnl_o, vnl_nonVnChar}, {vs_u, vs_uo, vs_nil}, -1, vs_uor, -1, vs_uho}, - {2, 1, 1, {vnl_u, vnl_or, vnl_nonVnChar}, {vs_u, vs_uor, vs_nil}, 1, vs_nil, -1, vs_uoh}, - {2, 1, 1, {vnl_u, vnl_oh, vnl_nonVnChar}, {vs_u, vs_uoh, vs_nil}, -1, vs_uor, 1, vs_uhoh}, - {2, 0, 0, {vnl_u, vnl_u, vnl_nonVnChar}, {vs_u, vs_uu, vs_nil}, -1, vs_nil, -1, vs_uhu}, - {2, 1, 1, {vnl_u, vnl_y, vnl_nonVnChar}, {vs_u, vs_uy, vs_nil}, -1, vs_nil, -1, vs_nil}, - {2, 1, 0, {vnl_uh, vnl_a, vnl_nonVnChar}, {vs_uh, vs_uha, vs_nil}, -1, vs_nil, 0, vs_nil}, - {2, 1, 0, {vnl_uh, vnl_i, vnl_nonVnChar}, {vs_uh, vs_uhi, vs_nil}, -1, vs_nil, 0, vs_nil}, - {2, 0, 1, {vnl_uh, vnl_o, vnl_nonVnChar}, {vs_uh, vs_uho, vs_nil}, -1, vs_nil, 0, vs_uhoh}, - {2, 1, 1, {vnl_uh, vnl_oh, vnl_nonVnChar}, {vs_uh, vs_uhoh, vs_nil}, -1, vs_nil, 0, vs_nil}, - {2, 1, 0, {vnl_uh, vnl_u, vnl_nonVnChar}, {vs_uh, vs_uhu, vs_nil}, -1, vs_nil, 0, vs_nil}, - {2, 0, 1, {vnl_y, vnl_e, vnl_nonVnChar}, {vs_y, vs_ye, vs_nil}, -1, vs_yer, -1, vs_nil}, - {2, 1, 1, {vnl_y, vnl_er, vnl_nonVnChar}, {vs_y, vs_yer, vs_nil}, 1, vs_nil, -1, vs_nil}, - {3, 0, 0, {vnl_i, vnl_e, vnl_u}, {vs_i, vs_ie, vs_ieu}, -1, vs_ieru, -1, vs_nil}, - {3, 1, 0, {vnl_i, vnl_er, vnl_u}, {vs_i, vs_ier, vs_ieru}, 1, vs_nil, -1, vs_nil}, - {3, 1, 0, {vnl_o, vnl_a, vnl_i}, {vs_o, vs_oa, vs_oai}, -1, vs_nil, -1, vs_nil}, - {3, 1, 0, {vnl_o, vnl_a, vnl_y}, {vs_o, vs_oa, vs_oay}, -1, vs_nil, -1, vs_nil}, - {3, 1, 0, {vnl_o, vnl_e, vnl_o}, {vs_o, vs_oe, vs_oeo}, -1, vs_nil, -1, vs_nil}, - {3, 0, 0, {vnl_u, vnl_a, vnl_y}, {vs_u, vs_ua, vs_uay}, -1, vs_uary, -1, vs_nil}, - {3, 1, 0, {vnl_u, vnl_ar, vnl_y}, {vs_u, vs_uar, vs_uary}, 1, vs_nil, -1, vs_nil}, - {3, 0, 0, {vnl_u, vnl_o, vnl_i}, {vs_u, vs_uo, vs_uoi}, -1, vs_uori, -1, vs_uhoi}, - {3, 0, 0, {vnl_u, vnl_o, vnl_u}, {vs_u, vs_uo, vs_uou}, -1, vs_nil, -1, vs_uhou}, - {3, 1, 0, {vnl_u, vnl_or, vnl_i}, {vs_u, vs_uor, vs_uori}, 1, vs_nil, -1, vs_uohi}, - {3, 0, 0, {vnl_u, vnl_oh, vnl_i}, {vs_u, vs_uoh, vs_uohi}, -1, vs_uori, 1, vs_uhohi}, - {3, 0, 0, {vnl_u, vnl_oh, vnl_u}, {vs_u, vs_uoh, vs_uohu}, -1, vs_nil, 1, vs_uhohu}, - {3, 1, 0, {vnl_u, vnl_y, vnl_a}, {vs_u, vs_uy, vs_uya}, -1, vs_nil, -1, vs_nil}, - {3, 0, 1, {vnl_u, vnl_y, vnl_e}, {vs_u, vs_uy, vs_uye}, -1, vs_uyer, -1, vs_nil}, - {3, 1, 1, {vnl_u, vnl_y, vnl_er}, {vs_u, vs_uy, vs_uyer}, 2, vs_nil, -1, vs_nil}, - {3, 1, 0, {vnl_u, vnl_y, vnl_u}, {vs_u, vs_uy, vs_uyu}, -1, vs_nil, -1, vs_nil}, - {3, 0, 0, {vnl_uh, vnl_o, vnl_i}, {vs_uh, vs_uho, vs_uhoi}, -1, vs_nil, 0, vs_uhohi}, - {3, 0, 0, {vnl_uh, vnl_o, vnl_u}, {vs_uh, vs_uho, vs_uhou}, -1, vs_nil, 0, vs_uhohu}, - {3, 1, 0, {vnl_uh, vnl_oh, vnl_i}, {vs_uh, vs_uhoh, vs_uhohi}, -1, vs_nil, 0, vs_nil}, - {3, 1, 0, {vnl_uh, vnl_oh, vnl_u}, {vs_uh, vs_uhoh, vs_uhohu}, -1, vs_nil, 0, vs_nil}, - {3, 0, 0, {vnl_y, vnl_e, vnl_u}, {vs_y, vs_ye, vs_yeu}, -1, vs_yeru, -1, vs_nil}, - {3, 1, 0, {vnl_y, vnl_er, vnl_u}, {vs_y, vs_yer, vs_yeru}, 1, vs_nil, -1, vs_nil}}; +VowelSeqInfo VSeqList[] = {{1, + 1, + 1, + {vnl_a, vnl_nonVnChar, vnl_nonVnChar}, + {vs_a, vs_nil, vs_nil}, + -1, + vs_ar, + -1, + vs_ab}, + {1, + 1, + 1, + {vnl_ar, vnl_nonVnChar, vnl_nonVnChar}, + {vs_ar, vs_nil, vs_nil}, + 0, + vs_nil, + -1, + vs_ab}, + {1, + 1, + 1, + {vnl_ab, vnl_nonVnChar, vnl_nonVnChar}, + {vs_ab, vs_nil, vs_nil}, + -1, + vs_ar, + 0, + vs_nil}, + {1, + 1, + 1, + {vnl_e, vnl_nonVnChar, vnl_nonVnChar}, + {vs_e, vs_nil, vs_nil}, + -1, + vs_er, + -1, + vs_nil}, + {1, + 1, + 1, + {vnl_er, vnl_nonVnChar, vnl_nonVnChar}, + {vs_er, vs_nil, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {1, + 1, + 1, + {vnl_i, vnl_nonVnChar, vnl_nonVnChar}, + {vs_i, vs_nil, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {1, + 1, + 1, + {vnl_o, vnl_nonVnChar, vnl_nonVnChar}, + {vs_o, vs_nil, vs_nil}, + -1, + vs_or, + -1, + vs_oh}, + {1, + 1, + 1, + {vnl_or, vnl_nonVnChar, vnl_nonVnChar}, + {vs_or, vs_nil, vs_nil}, + 0, + vs_nil, + -1, + vs_oh}, + {1, + 1, + 1, + {vnl_oh, vnl_nonVnChar, vnl_nonVnChar}, + {vs_oh, vs_nil, vs_nil}, + -1, + vs_or, + 0, + vs_nil}, + {1, + 1, + 1, + {vnl_u, vnl_nonVnChar, vnl_nonVnChar}, + {vs_u, vs_nil, vs_nil}, + -1, + vs_nil, + -1, + vs_uh}, + {1, + 1, + 1, + {vnl_uh, vnl_nonVnChar, vnl_nonVnChar}, + {vs_uh, vs_nil, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {1, + 1, + 1, + {vnl_y, vnl_nonVnChar, vnl_nonVnChar}, + {vs_y, vs_nil, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_i, vnl_nonVnChar}, + {vs_a, vs_ai, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_o, vnl_nonVnChar}, + {vs_a, vs_ao, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_u, vnl_nonVnChar}, + {vs_a, vs_au, vs_nil}, + -1, + vs_aru, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_a, vnl_y, vnl_nonVnChar}, + {vs_a, vs_ay, vs_nil}, + -1, + vs_ary, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_ar, vnl_u, vnl_nonVnChar}, + {vs_ar, vs_aru, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_ar, vnl_y, vnl_nonVnChar}, + {vs_ar, vs_ary, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_e, vnl_o, vnl_nonVnChar}, + {vs_e, vs_eo, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 0, + 0, + {vnl_e, vnl_u, vnl_nonVnChar}, + {vs_e, vs_eu, vs_nil}, + -1, + vs_eru, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_er, vnl_u, vnl_nonVnChar}, + {vs_er, vs_eru, vs_nil}, + 0, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_i, vnl_a, vnl_nonVnChar}, + {vs_i, vs_ia, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 0, + 1, + {vnl_i, vnl_e, vnl_nonVnChar}, + {vs_i, vs_ie, vs_nil}, + -1, + vs_ier, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_i, vnl_er, vnl_nonVnChar}, + {vs_i, vs_ier, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_i, vnl_u, vnl_nonVnChar}, + {vs_i, vs_iu, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_o, vnl_a, vnl_nonVnChar}, + {vs_o, vs_oa, vs_nil}, + -1, + vs_nil, + -1, + vs_oab}, + {2, + 1, + 1, + {vnl_o, vnl_ab, vnl_nonVnChar}, + {vs_o, vs_oab, vs_nil}, + -1, + vs_nil, + 1, + vs_nil}, + {2, + 1, + 1, + {vnl_o, vnl_e, vnl_nonVnChar}, + {vs_o, vs_oe, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_o, vnl_i, vnl_nonVnChar}, + {vs_o, vs_oi, vs_nil}, + -1, + vs_ori, + -1, + vs_ohi}, + {2, + 1, + 0, + {vnl_or, vnl_i, vnl_nonVnChar}, + {vs_or, vs_ori, vs_nil}, + 0, + vs_nil, + -1, + vs_ohi}, + {2, + 1, + 0, + {vnl_oh, vnl_i, vnl_nonVnChar}, + {vs_oh, vs_ohi, vs_nil}, + -1, + vs_ori, + 0, + vs_nil}, + {2, + 1, + 1, + {vnl_u, vnl_a, vnl_nonVnChar}, + {vs_u, vs_ua, vs_nil}, + -1, + vs_uar, + -1, + vs_uha}, + {2, + 1, + 1, + {vnl_u, vnl_ar, vnl_nonVnChar}, + {vs_u, vs_uar, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {2, + 0, + 1, + {vnl_u, vnl_e, vnl_nonVnChar}, + {vs_u, vs_ue, vs_nil}, + -1, + vs_uer, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_u, vnl_er, vnl_nonVnChar}, + {vs_u, vs_uer, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_u, vnl_i, vnl_nonVnChar}, + {vs_u, vs_ui, vs_nil}, + -1, + vs_nil, + -1, + vs_uhi}, + {2, + 0, + 1, + {vnl_u, vnl_o, vnl_nonVnChar}, + {vs_u, vs_uo, vs_nil}, + -1, + vs_uor, + -1, + vs_uho}, + {2, + 1, + 1, + {vnl_u, vnl_or, vnl_nonVnChar}, + {vs_u, vs_uor, vs_nil}, + 1, + vs_nil, + -1, + vs_uoh}, + {2, + 1, + 1, + {vnl_u, vnl_oh, vnl_nonVnChar}, + {vs_u, vs_uoh, vs_nil}, + -1, + vs_uor, + 1, + vs_uhoh}, + {2, + 0, + 0, + {vnl_u, vnl_u, vnl_nonVnChar}, + {vs_u, vs_uu, vs_nil}, + -1, + vs_nil, + -1, + vs_uhu}, + {2, + 1, + 1, + {vnl_u, vnl_y, vnl_nonVnChar}, + {vs_u, vs_uy, vs_nil}, + -1, + vs_nil, + -1, + vs_nil}, + {2, + 1, + 0, + {vnl_uh, vnl_a, vnl_nonVnChar}, + {vs_uh, vs_uha, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 1, + 0, + {vnl_uh, vnl_i, vnl_nonVnChar}, + {vs_uh, vs_uhi, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 0, + 1, + {vnl_uh, vnl_o, vnl_nonVnChar}, + {vs_uh, vs_uho, vs_nil}, + -1, + vs_nil, + 0, + vs_uhoh}, + {2, + 1, + 1, + {vnl_uh, vnl_oh, vnl_nonVnChar}, + {vs_uh, vs_uhoh, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 1, + 0, + {vnl_uh, vnl_u, vnl_nonVnChar}, + {vs_uh, vs_uhu, vs_nil}, + -1, + vs_nil, + 0, + vs_nil}, + {2, + 0, + 1, + {vnl_y, vnl_e, vnl_nonVnChar}, + {vs_y, vs_ye, vs_nil}, + -1, + vs_yer, + -1, + vs_nil}, + {2, + 1, + 1, + {vnl_y, vnl_er, vnl_nonVnChar}, + {vs_y, vs_yer, vs_nil}, + 1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_i, vnl_e, vnl_u}, + {vs_i, vs_ie, vs_ieu}, + -1, + vs_ieru, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_i, vnl_er, vnl_u}, + {vs_i, vs_ier, vs_ieru}, + 1, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_o, vnl_a, vnl_i}, + {vs_o, vs_oa, vs_oai}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_o, vnl_a, vnl_y}, + {vs_o, vs_oa, vs_oay}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_o, vnl_e, vnl_o}, + {vs_o, vs_oe, vs_oeo}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_u, vnl_a, vnl_y}, + {vs_u, vs_ua, vs_uay}, + -1, + vs_uary, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_u, vnl_ar, vnl_y}, + {vs_u, vs_uar, vs_uary}, + 1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_u, vnl_o, vnl_i}, + {vs_u, vs_uo, vs_uoi}, + -1, + vs_uori, + -1, + vs_uhoi}, + {3, + 0, + 0, + {vnl_u, vnl_o, vnl_u}, + {vs_u, vs_uo, vs_uou}, + -1, + vs_nil, + -1, + vs_uhou}, + {3, + 1, + 0, + {vnl_u, vnl_or, vnl_i}, + {vs_u, vs_uor, vs_uori}, + 1, + vs_nil, + -1, + vs_uohi}, + {3, + 0, + 0, + {vnl_u, vnl_oh, vnl_i}, + {vs_u, vs_uoh, vs_uohi}, + -1, + vs_uori, + 1, + vs_uhohi}, + {3, + 0, + 0, + {vnl_u, vnl_oh, vnl_u}, + {vs_u, vs_uoh, vs_uohu}, + -1, + vs_nil, + 1, + vs_uhohu}, + {3, + 1, + 0, + {vnl_u, vnl_y, vnl_a}, + {vs_u, vs_uy, vs_uya}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 1, + {vnl_u, vnl_y, vnl_e}, + {vs_u, vs_uy, vs_uye}, + -1, + vs_uyer, + -1, + vs_nil}, + {3, + 1, + 1, + {vnl_u, vnl_y, vnl_er}, + {vs_u, vs_uy, vs_uyer}, + 2, + vs_nil, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_u, vnl_y, vnl_u}, + {vs_u, vs_uy, vs_uyu}, + -1, + vs_nil, + -1, + vs_nil}, + {3, + 0, + 0, + {vnl_uh, vnl_o, vnl_i}, + {vs_uh, vs_uho, vs_uhoi}, + -1, + vs_nil, + 0, + vs_uhohi}, + {3, + 0, + 0, + {vnl_uh, vnl_o, vnl_u}, + {vs_uh, vs_uho, vs_uhou}, + -1, + vs_nil, + 0, + vs_uhohu}, + {3, + 1, + 0, + {vnl_uh, vnl_oh, vnl_i}, + {vs_uh, vs_uhoh, vs_uhohi}, + -1, + vs_nil, + 0, + vs_nil}, + {3, + 1, + 0, + {vnl_uh, vnl_oh, vnl_u}, + {vs_uh, vs_uhoh, vs_uhohu}, + -1, + vs_nil, + 0, + vs_nil}, + {3, + 0, + 0, + {vnl_y, vnl_e, vnl_u}, + {vs_y, vs_ye, vs_yeu}, + -1, + vs_yeru, + -1, + vs_nil}, + {3, + 1, + 0, + {vnl_y, vnl_er, vnl_u}, + {vs_y, vs_yer, vs_yeru}, + 1, + vs_nil, + -1, + vs_nil}}; struct ConSeqInfo { - int len; + int len; VnLexiName c[3]; - bool suffix; + bool suffix; }; -ConSeqInfo CSeqList[] = {{1, {vnl_b, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_c, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_c, vnl_h, vnl_nonVnChar}, true}, {1, {vnl_d, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_dd, vnl_nonVnChar, vnl_nonVnChar}, false}, {2, {vnl_d, vnl_z, vnl_nonVnChar}, false}, - {1, {vnl_g, vnl_nonVnChar, vnl_nonVnChar}, false}, {2, {vnl_g, vnl_h, vnl_nonVnChar}, false}, - {2, {vnl_g, vnl_i, vnl_nonVnChar}, false}, {3, {vnl_g, vnl_i, vnl_n}, false}, - {1, {vnl_h, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_k, vnl_nonVnChar, vnl_nonVnChar}, false}, - {2, {vnl_k, vnl_h, vnl_nonVnChar}, false}, {1, {vnl_l, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_m, vnl_nonVnChar, vnl_nonVnChar}, true}, {1, {vnl_n, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_n, vnl_g, vnl_nonVnChar}, true}, {3, {vnl_n, vnl_g, vnl_h}, false}, - {2, {vnl_n, vnl_h, vnl_nonVnChar}, true}, {1, {vnl_p, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_p, vnl_h, vnl_nonVnChar}, false}, {1, {vnl_q, vnl_nonVnChar, vnl_nonVnChar}, false}, - {2, {vnl_q, vnl_u, vnl_nonVnChar}, false}, {1, {vnl_r, vnl_nonVnChar, vnl_nonVnChar}, false}, - {1, {vnl_s, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_t, vnl_nonVnChar, vnl_nonVnChar}, true}, - {2, {vnl_t, vnl_h, vnl_nonVnChar}, false}, {2, {vnl_t, vnl_r, vnl_nonVnChar}, false}, - {1, {vnl_v, vnl_nonVnChar, vnl_nonVnChar}, false}, {1, {vnl_x, vnl_nonVnChar, vnl_nonVnChar}, false}}; - -const int VSeqCount = sizeof(VSeqList) / sizeof(VowelSeqInfo); +ConSeqInfo CSeqList[] = {{1, {vnl_b, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_c, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_c, vnl_h, vnl_nonVnChar}, true}, + {1, {vnl_d, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_dd, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_d, vnl_z, vnl_nonVnChar}, false}, + {1, {vnl_g, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_g, vnl_h, vnl_nonVnChar}, false}, + {2, {vnl_g, vnl_i, vnl_nonVnChar}, false}, + {3, {vnl_g, vnl_i, vnl_n}, false}, + {1, {vnl_h, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_k, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_k, vnl_h, vnl_nonVnChar}, false}, + {1, {vnl_l, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_m, vnl_nonVnChar, vnl_nonVnChar}, true}, + {1, {vnl_n, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_n, vnl_g, vnl_nonVnChar}, true}, + {3, {vnl_n, vnl_g, vnl_h}, false}, + {2, {vnl_n, vnl_h, vnl_nonVnChar}, true}, + {1, {vnl_p, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_p, vnl_h, vnl_nonVnChar}, false}, + {1, {vnl_q, vnl_nonVnChar, vnl_nonVnChar}, false}, + {2, {vnl_q, vnl_u, vnl_nonVnChar}, false}, + {1, {vnl_r, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_s, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_t, vnl_nonVnChar, vnl_nonVnChar}, true}, + {2, {vnl_t, vnl_h, vnl_nonVnChar}, false}, + {2, {vnl_t, vnl_r, vnl_nonVnChar}, false}, + {1, {vnl_v, vnl_nonVnChar, vnl_nonVnChar}, false}, + {1, {vnl_x, vnl_nonVnChar, vnl_nonVnChar}, false}}; + +const int VSeqCount = sizeof(VSeqList) / sizeof(VowelSeqInfo); struct VSeqPair { VnLexiName v[3]; - VowelSeq vs; + VowelSeq vs; }; -VSeqPair SortedVSeqList[VSeqCount]; +VSeqPair SortedVSeqList[VSeqCount]; const int CSeqCount = sizeof(CSeqList) / sizeof(ConSeqInfo); struct CSeqPair { VnLexiName c[3]; - ConSeq cs; + ConSeq cs; }; CSeqPair SortedCSeqList[CSeqCount]; struct VCPair { VowelSeq v; - ConSeq c; + ConSeq c; }; -VCPair VCPairList[] = {{vs_a, cs_c}, {vs_a, cs_ch}, {vs_a, cs_m}, {vs_a, cs_n}, {vs_a, cs_ng}, {vs_a, cs_nh}, {vs_a, cs_p}, {vs_a, cs_t}, {vs_ar, cs_c}, - {vs_ar, cs_m}, {vs_ar, cs_n}, {vs_ar, cs_ng}, {vs_ar, cs_p}, {vs_ar, cs_t}, {vs_ab, cs_c}, {vs_ab, cs_m}, {vs_ab, cs_n}, {vs_ab, cs_ng}, - {vs_ab, cs_p}, {vs_ab, cs_t}, - - {vs_e, cs_c}, {vs_e, cs_ch}, {vs_e, cs_m}, {vs_e, cs_n}, {vs_e, cs_ng}, {vs_e, cs_nh}, {vs_e, cs_p}, {vs_e, cs_t}, {vs_er, cs_c}, - {vs_er, cs_ch}, {vs_er, cs_m}, {vs_er, cs_n}, {vs_er, cs_nh}, {vs_er, cs_p}, {vs_er, cs_t}, - - {vs_i, cs_c}, {vs_i, cs_ch}, {vs_i, cs_m}, {vs_i, cs_n}, {vs_i, cs_nh}, {vs_i, cs_p}, {vs_i, cs_t}, - - {vs_o, cs_c}, {vs_o, cs_m}, {vs_o, cs_n}, {vs_o, cs_ng}, {vs_o, cs_p}, {vs_o, cs_t}, {vs_or, cs_c}, {vs_or, cs_m}, {vs_or, cs_n}, - {vs_or, cs_ng}, {vs_or, cs_p}, {vs_or, cs_t}, {vs_oh, cs_m}, {vs_oh, cs_n}, {vs_oh, cs_p}, {vs_oh, cs_t}, - - {vs_u, cs_c}, {vs_u, cs_m}, {vs_u, cs_n}, {vs_u, cs_ng}, {vs_u, cs_p}, {vs_u, cs_t}, {vs_uh, cs_c}, {vs_uh, cs_m}, {vs_uh, cs_n}, - {vs_uh, cs_ng}, {vs_uh, cs_t}, - - {vs_y, cs_t}, {vs_ie, cs_c}, {vs_ie, cs_m}, {vs_ie, cs_n}, {vs_ie, cs_ng}, {vs_ie, cs_p}, {vs_ie, cs_t}, {vs_ier, cs_c}, {vs_ier, cs_m}, - {vs_ier, cs_n}, {vs_ier, cs_ng}, {vs_ier, cs_p}, {vs_ier, cs_t}, - - {vs_oa, cs_c}, {vs_oa, cs_ch}, {vs_oa, cs_m}, {vs_oa, cs_n}, {vs_oa, cs_ng}, {vs_oa, cs_nh}, {vs_oa, cs_p}, {vs_oa, cs_t}, {vs_oab, cs_c}, - {vs_oab, cs_m}, {vs_oab, cs_n}, {vs_oab, cs_ng}, {vs_oab, cs_t}, - - {vs_oe, cs_n}, {vs_oe, cs_t}, - - {vs_ua, cs_n}, {vs_ua, cs_ng}, {vs_ua, cs_t}, {vs_uar, cs_n}, {vs_uar, cs_ng}, {vs_uar, cs_t}, - - {vs_ue, cs_c}, {vs_ue, cs_ch}, {vs_ue, cs_n}, {vs_ue, cs_nh}, {vs_uer, cs_c}, {vs_uer, cs_ch}, {vs_uer, cs_n}, {vs_uer, cs_nh}, - - {vs_uo, cs_c}, {vs_uo, cs_m}, {vs_uo, cs_n}, {vs_uo, cs_ng}, {vs_uo, cs_p}, {vs_uo, cs_t}, {vs_uor, cs_c}, {vs_uor, cs_m}, {vs_uor, cs_n}, - {vs_uor, cs_ng}, {vs_uor, cs_t}, {vs_uho, cs_c}, {vs_uho, cs_m}, {vs_uho, cs_n}, {vs_uho, cs_ng}, {vs_uho, cs_p}, {vs_uho, cs_t}, {vs_uhoh, cs_c}, - {vs_uhoh, cs_m}, {vs_uhoh, cs_n}, {vs_uhoh, cs_ng}, {vs_uhoh, cs_p}, {vs_uhoh, cs_t}, - - {vs_uy, cs_c}, {vs_uy, cs_ch}, {vs_uy, cs_n}, {vs_uy, cs_nh}, {vs_uy, cs_p}, {vs_uy, cs_t}, - - {vs_ye, cs_m}, {vs_ye, cs_n}, {vs_ye, cs_ng}, {vs_ye, cs_p}, {vs_ye, cs_t}, {vs_yer, cs_m}, {vs_yer, cs_n}, {vs_yer, cs_ng}, {vs_yer, cs_t}, - - {vs_uye, cs_n}, {vs_uye, cs_t}, {vs_uyer, cs_n}, {vs_uyer, cs_t} +VCPair VCPairList[] = {{vs_a, cs_c}, {vs_a, cs_ch}, {vs_a, cs_m}, + {vs_a, cs_n}, {vs_a, cs_ng}, {vs_a, cs_nh}, + {vs_a, cs_p}, {vs_a, cs_t}, {vs_ar, cs_c}, + {vs_ar, cs_m}, {vs_ar, cs_n}, {vs_ar, cs_ng}, + {vs_ar, cs_p}, {vs_ar, cs_t}, {vs_ab, cs_c}, + {vs_ab, cs_m}, {vs_ab, cs_n}, {vs_ab, cs_ng}, + {vs_ab, cs_p}, {vs_ab, cs_t}, + + {vs_e, cs_c}, {vs_e, cs_ch}, {vs_e, cs_m}, + {vs_e, cs_n}, {vs_e, cs_ng}, {vs_e, cs_nh}, + {vs_e, cs_p}, {vs_e, cs_t}, {vs_er, cs_c}, + {vs_er, cs_ch}, {vs_er, cs_m}, {vs_er, cs_n}, + {vs_er, cs_nh}, {vs_er, cs_p}, {vs_er, cs_t}, + + {vs_i, cs_c}, {vs_i, cs_ch}, {vs_i, cs_m}, + {vs_i, cs_n}, {vs_i, cs_nh}, {vs_i, cs_p}, + {vs_i, cs_t}, + + {vs_o, cs_c}, {vs_o, cs_m}, {vs_o, cs_n}, + {vs_o, cs_ng}, {vs_o, cs_p}, {vs_o, cs_t}, + {vs_or, cs_c}, {vs_or, cs_m}, {vs_or, cs_n}, + {vs_or, cs_ng}, {vs_or, cs_p}, {vs_or, cs_t}, + {vs_oh, cs_m}, {vs_oh, cs_n}, {vs_oh, cs_p}, + {vs_oh, cs_t}, + + {vs_u, cs_c}, {vs_u, cs_m}, {vs_u, cs_n}, + {vs_u, cs_ng}, {vs_u, cs_p}, {vs_u, cs_t}, + {vs_uh, cs_c}, {vs_uh, cs_m}, {vs_uh, cs_n}, + {vs_uh, cs_ng}, {vs_uh, cs_t}, + + {vs_y, cs_t}, {vs_ie, cs_c}, {vs_ie, cs_m}, + {vs_ie, cs_n}, {vs_ie, cs_ng}, {vs_ie, cs_p}, + {vs_ie, cs_t}, {vs_ier, cs_c}, {vs_ier, cs_m}, + {vs_ier, cs_n}, {vs_ier, cs_ng}, {vs_ier, cs_p}, + {vs_ier, cs_t}, + + {vs_oa, cs_c}, {vs_oa, cs_ch}, {vs_oa, cs_m}, + {vs_oa, cs_n}, {vs_oa, cs_ng}, {vs_oa, cs_nh}, + {vs_oa, cs_p}, {vs_oa, cs_t}, {vs_oab, cs_c}, + {vs_oab, cs_m}, {vs_oab, cs_n}, {vs_oab, cs_ng}, + {vs_oab, cs_t}, + + {vs_oe, cs_n}, {vs_oe, cs_t}, + + {vs_ua, cs_n}, {vs_ua, cs_ng}, {vs_ua, cs_t}, + {vs_uar, cs_n}, {vs_uar, cs_ng}, {vs_uar, cs_t}, + + {vs_ue, cs_c}, {vs_ue, cs_ch}, {vs_ue, cs_n}, + {vs_ue, cs_nh}, {vs_uer, cs_c}, {vs_uer, cs_ch}, + {vs_uer, cs_n}, {vs_uer, cs_nh}, + + {vs_uo, cs_c}, {vs_uo, cs_m}, {vs_uo, cs_n}, + {vs_uo, cs_ng}, {vs_uo, cs_p}, {vs_uo, cs_t}, + {vs_uor, cs_c}, {vs_uor, cs_m}, {vs_uor, cs_n}, + {vs_uor, cs_ng}, {vs_uor, cs_t}, {vs_uho, cs_c}, + {vs_uho, cs_m}, {vs_uho, cs_n}, {vs_uho, cs_ng}, + {vs_uho, cs_p}, {vs_uho, cs_t}, {vs_uhoh, cs_c}, + {vs_uhoh, cs_m}, {vs_uhoh, cs_n}, {vs_uhoh, cs_ng}, + {vs_uhoh, cs_p}, {vs_uhoh, cs_t}, + + {vs_uy, cs_c}, {vs_uy, cs_ch}, {vs_uy, cs_n}, + {vs_uy, cs_nh}, {vs_uy, cs_p}, {vs_uy, cs_t}, + + {vs_ye, cs_m}, {vs_ye, cs_n}, {vs_ye, cs_ng}, + {vs_ye, cs_p}, {vs_ye, cs_t}, {vs_yer, cs_m}, + {vs_yer, cs_n}, {vs_yer, cs_ng}, {vs_yer, cs_t}, + + {vs_uye, cs_n}, {vs_uye, cs_t}, {vs_uyer, cs_n}, + {vs_uyer, cs_t} }; @@ -215,7 +829,7 @@ const int VCPairCount = sizeof(VCPairList) / sizeof(VCPair); // TODO: auto-complete: e.g. luan -> lua^n -typedef int (UkEngine::*UkKeyProc)(UkKeyEvent& ev); +typedef int (UkEngine::*UkKeyProc)(UkKeyEvent &ev); UkKeyProc UkKeyProcList[vneCount] = { &UkEngine::processRoof, // vneRoofAll @@ -240,15 +854,17 @@ UkKeyProc UkKeyProcList[vneCount] = { &UkEngine::processAppend // vneNormal }; -VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2 = vnl_nonVnChar, VnLexiName v3 = vnl_nonVnChar); -ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2 = vnl_nonVnChar, VnLexiName c3 = vnl_nonVnChar); +VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2 = vnl_nonVnChar, + VnLexiName v3 = vnl_nonVnChar); +ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2 = vnl_nonVnChar, + VnLexiName c3 = vnl_nonVnChar); -bool UkEngine::m_classInit = false; +bool UkEngine::m_classInit = false; //------------------------------------------------ -int tripleVowelCompare(const void* p1, const void* p2) { - VSeqPair* t1 = (VSeqPair*)p1; - VSeqPair* t2 = (VSeqPair*)p2; +int tripleVowelCompare(const void *p1, const void *p2) { + VSeqPair *t1 = (VSeqPair *)p1; + VSeqPair *t2 = (VSeqPair *)p2; for (int i = 0; i < 3; i++) { if (t1->v[i] < t2->v[i]) @@ -260,9 +876,9 @@ int tripleVowelCompare(const void* p1, const void* p2) { } //------------------------------------------------ -int tripleConCompare(const void* p1, const void* p2) { - CSeqPair* t1 = (CSeqPair*)p1; - CSeqPair* t2 = (CSeqPair*)p2; +int tripleConCompare(const void *p1, const void *p2) { + CSeqPair *t1 = (CSeqPair *)p1; + CSeqPair *t2 = (CSeqPair *)p2; for (int i = 0; i < 3; i++) { if (t1->c[i] < t2->c[i]) @@ -274,9 +890,9 @@ int tripleConCompare(const void* p1, const void* p2) { } //------------------------------------------------ -int VCPairCompare(const void* p1, const void* p2) { - VCPair* t1 = (VCPair*)p1; - VCPair* t2 = (VCPair*)p2; +int VCPairCompare(const void *p1, const void *p2) { + VCPair *t1 = (VCPair *)p1; + VCPair *t2 = (VCPair *)p2; if (t1->v < t2->v) return -1; @@ -295,18 +911,22 @@ bool isValidCV(ConSeq c, VowelSeq v) { if (c == cs_nil || v == vs_nil) return true; - VowelSeqInfo& vInfo = VSeqList[v]; + VowelSeqInfo &vInfo = VSeqList[v]; // gi doesn't go with i // qu doesn't go with u, uh // q doesn't go with any vowel - if ((c == cs_gi && vInfo.v[0] == vnl_i) || (c == cs_qu && (vInfo.v[0] == vnl_u || vInfo.v[0] == vnl_uh)) || (c == cs_q)) + if ((c == cs_gi && vInfo.v[0] == vnl_i) || + (c == cs_qu && (vInfo.v[0] == vnl_u || vInfo.v[0] == vnl_uh)) || + (c == cs_q)) return false; // k can only go with the following vowel sequences if (c == cs_k) { - static VowelSeq kVseq[] = {vs_e, vs_i, vs_y, vs_er, vs_eo, vs_eu, vs_eru, vs_ia, vs_ie, vs_ier, vs_ieu, vs_ieru, vs_nil}; - int i; + static VowelSeq kVseq[] = {vs_e, vs_i, vs_y, vs_er, vs_eo, + vs_eu, vs_eru, vs_ia, vs_ie, vs_ier, + vs_ieu, vs_ieru, vs_nil}; + int i; for (i = 0; kVseq[i] != vs_nil && kVseq[i] != v; i++) ; return (kVseq[i] != vs_nil); @@ -321,11 +941,11 @@ bool isValidVC(VowelSeq v, ConSeq c) { if (v == vs_nil || c == cs_nil) return true; - VowelSeqInfo& vInfo = VSeqList[v]; + VowelSeqInfo &vInfo = VSeqList[v]; if (!vInfo.conSuffix) return false; - ConSeqInfo& cInfo = CSeqList[c]; + ConSeqInfo &cInfo = CSeqList[c]; if (!cInfo.suffix) return false; @@ -363,7 +983,8 @@ bool isValidCVC(ConSeq c1, VowelSeq v, ConSeq c2) { return true; // gieng, gie^ng - if (c1 == cs_gi && (v == vs_e || v == vs_er) && (c2 == cs_n || c2 == cs_ng)) + if (c1 == cs_gi && (v == vs_e || v == vs_er) && + (c2 == cs_n || c2 == cs_ng)) return true; } return false; @@ -394,7 +1015,8 @@ void engineClassInit() { unsigned char ch; for (ch = 'a'; ch <= 'z'; ch++) { - if (ch != 'a' && ch != 'e' && ch != 'i' && ch != 'o' && ch != 'u' && ch != 'y') { + if (ch != 'a' && ch != 'e' && ch != 'i' && ch != 'o' && ch != 'u' && + ch != 'y') { IsVnVowel[AZLexiLower[ch - 'a']] = false; IsVnVowel[AZLexiUpper[ch - 'a']] = false; } @@ -410,7 +1032,8 @@ VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2, VnLexiName v3) { key.v[1] = v2; key.v[2] = v3; - VSeqPair* pInfo = (VSeqPair*)bsearch(&key, SortedVSeqList, VSeqCount, sizeof(VSeqPair), tripleVowelCompare); + VSeqPair *pInfo = (VSeqPair *)bsearch(&key, SortedVSeqList, VSeqCount, + sizeof(VSeqPair), tripleVowelCompare); if (pInfo == 0) return vs_nil; return pInfo->vs; @@ -423,47 +1046,55 @@ ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2, VnLexiName c3) { key.c[1] = c2; key.c[2] = c3; - CSeqPair* pInfo = (CSeqPair*)bsearch(&key, SortedCSeqList, CSeqCount, sizeof(CSeqPair), tripleConCompare); + CSeqPair *pInfo = (CSeqPair *)bsearch(&key, SortedCSeqList, CSeqCount, + sizeof(CSeqPair), tripleConCompare); if (pInfo == 0) return cs_nil; return pInfo->cs; } //------------------------------------------------------------------ -int UkEngine::processRoof(UkKeyEvent& ev) { +int UkEngine::processRoof(UkKeyEvent &ev) { if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0) return processAppend(ev); VnLexiName target; switch (ev.evType) { - case vneRoof_a: target = vnl_ar; break; - case vneRoof_e: target = vnl_er; break; - case vneRoof_o: target = vnl_or; break; - default: target = vnl_nonVnChar; + case vneRoof_a: + target = vnl_ar; + break; + case vneRoof_e: + target = vnl_er; + break; + case vneRoof_o: + target = vnl_or; + break; + default: + target = vnl_nonVnChar; } VowelSeq vs, newVs; - int i, vStart, vEnd; - int curTonePos, newTonePos, tone; - int changePos; - bool roofRemoved = false; - - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - (VSeqList[vs].len - 1); + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + int changePos; + bool roofRemoved = false; + + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - (VSeqList[vs].len - 1); curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; bool doubleChangeUO = false; if (vs == vs_uho || vs == vs_uhoh || vs == vs_uhoi || vs == vs_uhohi) { // special cases: u+o+ -> uo^, u+o -> uo^, u+o+i -> uo^i, u+oi -> uo^i - newVs = lookupVSeq(vnl_u, vnl_or, VSeqList[vs].v[2]); + newVs = lookupVSeq(vnl_u, vnl_or, VSeqList[vs].v[2]); doubleChangeUO = true; } else { newVs = VSeqList[vs].withRoof; } - VowelSeqInfo* pInfo; + VowelSeqInfo *pInfo; if (newVs == vs_nil) { if (VSeqList[vs].roofPos == -1) @@ -472,10 +1103,12 @@ int UkEngine::processRoof(UkKeyEvent& ev) { // a roof already exists -> undo roof VnLexiName curCh = m_buffer[vStart + VSeqList[vs].roofPos].vnSym; if (target != vnl_nonVnChar && curCh != target) - return processAppend(ev); // specific roof and the roof character don't match + return processAppend( + ev); // specific roof and the roof character don't match - VnLexiName newCh = (curCh == vnl_ar) ? vnl_a : ((curCh == vnl_er) ? vnl_e : vnl_o); - changePos = vStart + VSeqList[vs].roofPos; + VnLexiName newCh = + (curCh == vnl_ar) ? vnl_a : ((curCh == vnl_er) ? vnl_e : vnl_o); + changePos = vStart + VSeqList[vs].roofPos; if (!m_pCtrl->options.freeMarking && changePos != m_current) return processAppend(ev); @@ -484,13 +1117,16 @@ int UkEngine::processRoof(UkKeyEvent& ev) { m_buffer[changePos].vnSym = newCh; if (VSeqList[vs].len == 3) - newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, m_buffer[vStart + 2].vnSym); + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, + m_buffer[vStart + 2].vnSym); else if (VSeqList[vs].len == 2) - newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); else newVs = lookupVSeq(m_buffer[vStart].vnSym); - pInfo = &VSeqList[newVs]; + pInfo = &VSeqList[newVs]; roofRemoved = true; } else { pInfo = &VSeqList[newVs]; @@ -498,9 +1134,9 @@ int UkEngine::processRoof(UkKeyEvent& ev) { return processAppend(ev); // check validity of new VC and CV - bool valid = true; - ConSeq c1 = cs_nil; - ConSeq c2 = cs_nil; + bool valid = true; + ConSeq c1 = cs_nil; + ConSeq c2 = cs_nil; if (m_buffer[m_current].c1Offset != -1) c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; @@ -520,7 +1156,7 @@ int UkEngine::processRoof(UkKeyEvent& ev) { return processAppend(ev); markChange(changePos); if (doubleChangeUO) { - m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart].vnSym = vnl_u; m_buffer[vStart + 1].vnSym = vnl_or; } else { m_buffer[changePos].vnSym = pInfo->v[pInfo->roofPos]; @@ -562,117 +1198,122 @@ int UkEngine::processRoof(UkKeyEvent& ev) { //------------------------------------------------------------------ // can only be called from processHook //------------------------------------------------------------------ -int UkEngine::processHookWithUO(UkKeyEvent& ev) { +int UkEngine::processHookWithUO(UkKeyEvent &ev) { VowelSeq vs, newVs; - int i, vStart, vEnd; - int curTonePos, newTonePos, tone; - bool hookRemoved = false; - bool removeWithUndo = true; - bool toneRemoved = false; + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + bool hookRemoved = false; + bool removeWithUndo = true; + bool toneRemoved = false; (void)toneRemoved; // fix warning - VnLexiName* v; + VnLexiName *v; if (!m_pCtrl->options.freeMarking && m_buffer[m_current].vOffset != 0) return processAppend(ev); - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - (VSeqList[vs].len - 1); - v = VSeqList[vs].v; + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - (VSeqList[vs].len - 1); + v = VSeqList[vs].v; curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; switch (ev.evType) { - case vneHook_u: - if (v[0] == vnl_u) { + case vneHook_u: + if (v[0] == vnl_u) { + newVs = VSeqList[vs].withHook; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + } else { // v[0] = vnl_uh, -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + hookRemoved = true; + toneRemoved = (m_buffer[vStart].tone != 0); + } + break; + case vneHook_o: + if (v[1] == vnl_o || v[1] == vnl_or) { + if (vEnd == m_current && VSeqList[vs].len == 2 && + m_buffer[m_current].form == vnw_cv && + m_buffer[m_current - 2].cseq == cs_th) { + // o|o^ -> o+ newVs = VSeqList[vs].withHook; + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { + newVs = lookupVSeq(vnl_uh, vnl_oh, v[2]); + if (v[0] == vnl_u) { + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } + } + } else { // v[1] = vnl_oh, -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); + if (v[0] == vnl_uh) { markChange(vStart); - m_buffer[vStart].vnSym = vnl_uh; - } else { // v[0] = vnl_uh, -> uo - newVs = lookupVSeq(vnl_u, vnl_o, v[2]); - markChange(vStart); - m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + } else { + markChange(vStart + 1); m_buffer[vStart + 1].vnSym = vnl_o; - hookRemoved = true; - toneRemoved = (m_buffer[vStart].tone != 0); } - break; - case vneHook_o: + hookRemoved = true; + toneRemoved = (m_buffer[vStart + 1].tone != 0); + } + break; + default: // vneHookAll, vneHookUO: + if (v[0] == vnl_u) { if (v[1] == vnl_o || v[1] == vnl_or) { - if (vEnd == m_current && VSeqList[vs].len == 2 && m_buffer[m_current].form == vnw_cv && m_buffer[m_current - 2].cseq == cs_th) { - // o|o^ -> o+ - newVs = VSeqList[vs].withHook; + // uo -> uo+ if prefixed by "h", "kh", "th", or stand alone + if ((vs == vs_uo || vs == vs_uor) && vEnd == m_current && + ((m_buffer[m_current].form == vnw_cv && + (m_buffer[m_current - 2].cseq == cs_h || + m_buffer[m_current - 2].cseq == cs_kh || + m_buffer[m_current - 2].cseq == cs_th)) || + m_buffer[m_current].form == vnw_v)) { + newVs = vs_uoh; markChange(vStart + 1); m_buffer[vStart + 1].vnSym = vnl_oh; } else { - newVs = lookupVSeq(vnl_uh, vnl_oh, v[2]); - if (v[0] == vnl_u) { - markChange(vStart); - m_buffer[vStart].vnSym = vnl_uh; - m_buffer[vStart + 1].vnSym = vnl_oh; - } else { - markChange(vStart + 1); - m_buffer[vStart + 1].vnSym = vnl_oh; - } - } - } else { // v[1] = vnl_oh, -> uo - newVs = lookupVSeq(vnl_u, vnl_o, v[2]); - if (v[0] == vnl_uh) { - markChange(vStart); - m_buffer[vStart].vnSym = vnl_u; - m_buffer[vStart + 1].vnSym = vnl_o; - } else { - markChange(vStart + 1); - m_buffer[vStart + 1].vnSym = vnl_o; - } - hookRemoved = true; - toneRemoved = (m_buffer[vStart + 1].tone != 0); - } - break; - default: // vneHookAll, vneHookUO: - if (v[0] == vnl_u) { - if (v[1] == vnl_o || v[1] == vnl_or) { - // uo -> uo+ if prefixed by "h", "kh", "th", or stand alone - if ((vs == vs_uo || vs == vs_uor) && vEnd == m_current && - ((m_buffer[m_current].form == vnw_cv && - (m_buffer[m_current - 2].cseq == cs_h || m_buffer[m_current - 2].cseq == cs_kh || m_buffer[m_current - 2].cseq == cs_th)) || - m_buffer[m_current].form == vnw_v)) { - newVs = vs_uoh; - markChange(vStart + 1); - m_buffer[vStart + 1].vnSym = vnl_oh; - } else { - // uo -> u+o+ - newVs = VSeqList[vs].withHook; - markChange(vStart); - m_buffer[vStart].vnSym = vnl_uh; - newVs = VSeqList[newVs].withHook; - m_buffer[vStart + 1].vnSym = vnl_oh; - } - } else { // uo+ -> u+o+ + // uo -> u+o+ newVs = VSeqList[vs].withHook; markChange(vStart); m_buffer[vStart].vnSym = vnl_uh; - } - } else { // v[0] == vnl_uh - if (v[1] == vnl_o) { // u+o -> u+o+ - newVs = VSeqList[vs].withHook; - markChange(vStart + 1); + newVs = VSeqList[newVs].withHook; m_buffer[vStart + 1].vnSym = vnl_oh; - } else { // v[1] == vnl_oh, u+o+ -> uo - newVs = lookupVSeq(vnl_u, vnl_o, v[2]); // vs_uo; - markChange(vStart); - m_buffer[vStart].vnSym = vnl_u; - m_buffer[vStart + 1].vnSym = vnl_o; - hookRemoved = true; - toneRemoved = (m_buffer[vStart].tone != 0 || m_buffer[vStart + 1].tone != 0); } + } else { // uo+ -> u+o+ + newVs = VSeqList[vs].withHook; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_uh; } - break; + } else { // v[0] == vnl_uh + if (v[1] == vnl_o) { // u+o -> u+o+ + newVs = VSeqList[vs].withHook; + markChange(vStart + 1); + m_buffer[vStart + 1].vnSym = vnl_oh; + } else { // v[1] == vnl_oh, u+o+ -> uo + newVs = lookupVSeq(vnl_u, vnl_o, v[2]); // vs_uo; + markChange(vStart); + m_buffer[vStart].vnSym = vnl_u; + m_buffer[vStart + 1].vnSym = vnl_o; + hookRemoved = true; + toneRemoved = (m_buffer[vStart].tone != 0 || + m_buffer[vStart + 1].tone != 0); + } + } + break; } - VowelSeqInfo* p = &VSeqList[newVs]; + VowelSeqInfo *p = &VSeqList[newVs]; for (i = 0; i < p->len; i++) { // update sub-sequences m_buffer[vStart + i].vseq = p->sub[i]; } @@ -707,29 +1348,31 @@ int UkEngine::processHookWithUO(UkKeyEvent& ev) { } //------------------------------------------------------------------ -int UkEngine::processHook(UkKeyEvent& ev) { +int UkEngine::processHook(UkKeyEvent &ev) { if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0) return processAppend(ev); - VowelSeq vs, newVs; - int i, vStart, vEnd; - int curTonePos, newTonePos, tone; - int changePos; - bool hookRemoved = false; - VowelSeqInfo* pInfo; - VnLexiName* v; + VowelSeq vs, newVs; + int i, vStart, vEnd; + int curTonePos, newTonePos, tone; + int changePos; + bool hookRemoved = false; + VowelSeqInfo *pInfo; + VnLexiName *v; vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; + vs = m_buffer[vEnd].vseq; v = VSeqList[vs].v; - if (VSeqList[vs].len > 1 && ev.evType != vneBowl && (v[0] == vnl_u || v[0] == vnl_uh) && (v[1] == vnl_o || v[1] == vnl_oh || v[1] == vnl_or)) + if (VSeqList[vs].len > 1 && ev.evType != vneBowl && + (v[0] == vnl_u || v[0] == vnl_uh) && + (v[1] == vnl_o || v[1] == vnl_oh || v[1] == vnl_or)) return processHookWithUO(ev); - vStart = vEnd - (VSeqList[vs].len - 1); + vStart = vEnd - (VSeqList[vs].len - 1); curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; newVs = VSeqList[vs].withHook; if (newVs == vs_nil) { @@ -738,66 +1381,70 @@ int UkEngine::processHook(UkKeyEvent& ev) { // a hook already exists -> undo hook VnLexiName curCh = m_buffer[vStart + VSeqList[vs].hookPos].vnSym; - VnLexiName newCh = (curCh == vnl_ab) ? vnl_a : ((curCh == vnl_uh) ? vnl_u : vnl_o); - changePos = vStart + VSeqList[vs].hookPos; + VnLexiName newCh = + (curCh == vnl_ab) ? vnl_a : ((curCh == vnl_uh) ? vnl_u : vnl_o); + changePos = vStart + VSeqList[vs].hookPos; if (!m_pCtrl->options.freeMarking && changePos != m_current) return processAppend(ev); switch (ev.evType) { - case vneHook_u: - if (curCh != vnl_uh) - return processAppend(ev); - break; - case vneHook_o: - if (curCh != vnl_oh) - return processAppend(ev); - break; - case vneBowl: - if (curCh != vnl_ab) - return processAppend(ev); - break; - default: - if (ev.evType == vneHook_uo && curCh == vnl_ab) - return processAppend(ev); + case vneHook_u: + if (curCh != vnl_uh) + return processAppend(ev); + break; + case vneHook_o: + if (curCh != vnl_oh) + return processAppend(ev); + break; + case vneBowl: + if (curCh != vnl_ab) + return processAppend(ev); + break; + default: + if (ev.evType == vneHook_uo && curCh == vnl_ab) + return processAppend(ev); } markChange(changePos); m_buffer[changePos].vnSym = newCh; if (VSeqList[vs].len == 3) - newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, m_buffer[vStart + 2].vnSym); + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym, + m_buffer[vStart + 2].vnSym); else if (VSeqList[vs].len == 2) - newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); + newVs = + lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart + 1].vnSym); else newVs = lookupVSeq(m_buffer[vStart].vnSym); - pInfo = &VSeqList[newVs]; + pInfo = &VSeqList[newVs]; hookRemoved = true; } else { pInfo = &VSeqList[newVs]; switch (ev.evType) { - case vneHook_u: - if (pInfo->v[pInfo->hookPos] != vnl_uh) - return processAppend(ev); - break; - case vneHook_o: - if (pInfo->v[pInfo->hookPos] != vnl_oh) - return processAppend(ev); - break; - case vneBowl: - if (pInfo->v[pInfo->hookPos] != vnl_ab) - return processAppend(ev); - break; - default: // vneHook_uo, vneHookAll - if (ev.evType == vneHook_uo && pInfo->v[pInfo->hookPos] == vnl_ab) - return processAppend(ev); + case vneHook_u: + if (pInfo->v[pInfo->hookPos] != vnl_uh) + return processAppend(ev); + break; + case vneHook_o: + if (pInfo->v[pInfo->hookPos] != vnl_oh) + return processAppend(ev); + break; + case vneBowl: + if (pInfo->v[pInfo->hookPos] != vnl_ab) + return processAppend(ev); + break; + default: // vneHook_uo, vneHookAll + if (ev.evType == vneHook_uo && pInfo->v[pInfo->hookPos] == vnl_ab) + return processAppend(ev); } // check validity of new VC and CV - bool valid = true; - ConSeq c1 = cs_nil; - ConSeq c2 = cs_nil; + bool valid = true; + ConSeq c1 = cs_nil; + ConSeq c2 = cs_nil; if (m_buffer[m_current].c1Offset != -1) c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; @@ -851,14 +1498,15 @@ int UkEngine::processHook(UkKeyEvent& ev) { //---------------------------------------------------------- int UkEngine::getTonePosition(VowelSeq vs, bool terminated) const { - VowelSeqInfo& info = VSeqList[vs]; + VowelSeqInfo &info = VSeqList[vs]; if (info.len == 1) return 0; if (info.roofPos != -1) return info.roofPos; if (info.hookPos != -1) { - if (vs == vs_uhoh || vs == vs_uhohi || vs == vs_uhohu) // u+o+, u+o+u, u+o+i + if (vs == vs_uhoh || vs == vs_uhohi || + vs == vs_uhohu) // u+o+, u+o+u, u+o+i return 1; return info.hookPos; } @@ -866,25 +1514,28 @@ int UkEngine::getTonePosition(VowelSeq vs, bool terminated) const { if (info.len == 3) return 1; - if (m_pCtrl->options.modernStyle && (vs == vs_oa || vs == vs_oe || vs == vs_uy)) + if (m_pCtrl->options.modernStyle && + (vs == vs_oa || vs == vs_oe || vs == vs_uy)) return 1; return terminated ? 0 : 1; } //---------------------------------------------------------- -int UkEngine::processTone(UkKeyEvent& ev) { +int UkEngine::processTone(UkKeyEvent &ev) { if (m_current < 0 || !m_pCtrl->vietKey) return processAppend(ev); - if (m_buffer[m_current].form == vnw_c && (m_buffer[m_current].cseq == cs_gi || m_buffer[m_current].cseq == cs_gin)) { + if (m_buffer[m_current].form == vnw_c && + (m_buffer[m_current].cseq == cs_gi || + m_buffer[m_current].cseq == cs_gin)) { int p = (m_buffer[m_current].cseq == cs_gi) ? m_current : m_current - 1; if (m_buffer[p].tone == 0 && ev.tone == 0) return processAppend(ev); markChange(p); if (m_buffer[p].tone == ev.tone) { m_buffer[p].tone = 0; - m_singleMode = false; + m_singleMode = false; processAppend(ev); m_reverted = true; return 1; @@ -896,23 +1547,26 @@ int UkEngine::processTone(UkKeyEvent& ev) { if (m_buffer[m_current].vOffset < 0) return processAppend(ev); - int vEnd; + int vEnd; VowelSeq vs; - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - VowelSeqInfo& info = VSeqList[vs]; - if (m_pCtrl->options.spellCheckEnabled && !m_pCtrl->options.freeMarking && !info.complete) + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + VowelSeqInfo &info = VSeqList[vs]; + if (m_pCtrl->options.spellCheckEnabled && !m_pCtrl->options.freeMarking && + !info.complete) return processAppend(ev); - if (m_buffer[m_current].form == vnw_vc || m_buffer[m_current].form == vnw_cvc) { + if (m_buffer[m_current].form == vnw_vc || + m_buffer[m_current].form == vnw_cvc) { ConSeq cs = m_buffer[m_current].cseq; - if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && (ev.tone == 2 || ev.tone == 3 || ev.tone == 4)) + if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && + (ev.tone == 2 || ev.tone == 3 || ev.tone == 4)) return processAppend(ev); // c, ch, p, t suffixes don't allow ` ? ~ } int toneOffset = getTonePosition(vs, vEnd == m_current); - int tonePos = vEnd - (info.len - 1) + toneOffset; + int tonePos = vEnd - (info.len - 1) + toneOffset; if (m_buffer[tonePos].tone == 0 && ev.tone == 0) return processAppend(ev); @@ -920,7 +1574,7 @@ int UkEngine::processTone(UkKeyEvent& ev) { if (m_buffer[tonePos].tone == ev.tone) { markChange(tonePos); m_buffer[tonePos].tone = 0; - m_singleMode = false; + m_singleMode = false; processAppend(ev); m_reverted = true; return 1; @@ -932,7 +1586,7 @@ int UkEngine::processTone(UkKeyEvent& ev) { } //---------------------------------------------------------- -int UkEngine::processDd(UkKeyEvent& ev) { +int UkEngine::processDd(UkKeyEvent &ev) { if (!m_pCtrl->vietKey || m_current < 0) return processAppend(ev); @@ -940,17 +1594,19 @@ int UkEngine::processDd(UkKeyEvent& ev) { // we want to allow dd even in non-vn sequence, because dd is used a lot in // abbreviation we allow dd only if preceding character is not a vowel - if (m_buffer[m_current].form == vnw_nonVn && m_buffer[m_current].vnSym == vnl_d && - (m_buffer[m_current - 1].vnSym == vnl_nonVnChar || !IsVnVowel[m_buffer[m_current - 1].vnSym])) { + if (m_buffer[m_current].form == vnw_nonVn && + m_buffer[m_current].vnSym == vnl_d && + (m_buffer[m_current - 1].vnSym == vnl_nonVnChar || + !IsVnVowel[m_buffer[m_current - 1].vnSym])) { m_singleMode = true; - pos = m_current; + pos = m_current; markChange(pos); - m_buffer[pos].cseq = cs_dd; - m_buffer[pos].vnSym = vnl_dd; - m_buffer[pos].form = vnw_c; + m_buffer[pos].cseq = cs_dd; + m_buffer[pos].vnSym = vnl_dd; + m_buffer[pos].form = vnw_c; m_buffer[pos].c1Offset = 0; m_buffer[pos].c2Offset = -1; - m_buffer[pos].vOffset = -1; + m_buffer[pos].vOffset = -1; return 1; } @@ -964,7 +1620,7 @@ int UkEngine::processDd(UkKeyEvent& ev) { if (m_buffer[pos].cseq == cs_d) { markChange(pos); - m_buffer[pos].cseq = cs_dd; + m_buffer[pos].cseq = cs_dd; m_buffer[pos].vnSym = vnl_dd; // never spellcheck a word which starts with dd, because it's used alot // in abbreviation @@ -975,9 +1631,9 @@ int UkEngine::processDd(UkKeyEvent& ev) { if (m_buffer[pos].cseq == cs_dd) { // undo dd markChange(pos); - m_buffer[pos].cseq = cs_d; + m_buffer[pos].cseq = cs_d; m_buffer[pos].vnSym = vnl_d; - m_singleMode = false; + m_singleMode = false; processAppend(ev); m_reverted = true; return 1; @@ -1005,8 +1661,8 @@ inline VnLexiName vnToLower(VnLexiName x) { } //---------------------------------------------------------- -int UkEngine::processMapChar(UkKeyEvent& ev) { - int capsLockOn = 0; +int UkEngine::processMapChar(UkKeyEvent &ev) { + int capsLockOn = 0; int shiftPressed = 0; if (m_keyCheckFunc) m_keyCheckFunc(&shiftPressed, &capsLockOn); @@ -1018,7 +1674,8 @@ int UkEngine::processMapChar(UkKeyEvent& ev) { if (!m_pCtrl->vietKey) return ret; - if (m_current >= 0 && m_buffer[m_current].form != vnw_empty && m_buffer[m_current].form != vnw_nonVn) { + if (m_current >= 0 && m_buffer[m_current].form != vnw_empty && + m_buffer[m_current].form != vnw_nonVn) { return 1; } @@ -1027,9 +1684,9 @@ int UkEngine::processMapChar(UkKeyEvent& ev) { // mapChar doesn't apply m_current--; - WordInfo& entry = m_buffer[m_current]; + WordInfo &entry = m_buffer[m_current]; - bool undo = false; + bool undo = false; // test if undo is needed if (entry.form != vnw_empty && entry.form != vnw_nonVn) { VnLexiName prevSym = entry.vnSym; @@ -1038,20 +1695,22 @@ int UkEngine::processMapChar(UkKeyEvent& ev) { } if (prevSym == ev.vnSym) { if (entry.form != vnw_c) { - int vStart, vEnd, curTonePos, newTonePos, tone; + int vStart, vEnd, curTonePos, newTonePos, tone; VowelSeq vs, newVs; - vEnd = m_current - entry.vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - VSeqList[vs].len + 1; + vEnd = m_current - entry.vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - VSeqList[vs].len + 1; curTonePos = vStart + getTonePosition(vs, vEnd == m_current); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; markChange(m_current); m_current--; // check if tone position is needed - if (tone != 0 && m_current >= 0 && (m_buffer[m_current].form == vnw_v || m_buffer[m_current].form == vnw_cv)) { - newVs = m_buffer[m_current].vseq; + if (tone != 0 && m_current >= 0 && + (m_buffer[m_current].form == vnw_v || + m_buffer[m_current].form == vnw_cv)) { + newVs = m_buffer[m_current].vseq; newTonePos = vStart + getTonePosition(newVs, true); if (newTonePos != curTonePos) { markChange(newTonePos); @@ -1070,56 +1729,56 @@ int UkEngine::processMapChar(UkKeyEvent& ev) { ev.evType = vneNormal; ev.chType = m_pCtrl->input.getCharType(ev.keyCode); - ev.vnSym = IsoToVnLexi(ev.keyCode); - ret = processAppend(ev); + ev.vnSym = IsoToVnLexi(ev.keyCode); + ret = processAppend(ev); if (undo) { m_singleMode = false; - m_reverted = true; + m_reverted = true; return 1; } return ret; } //---------------------------------------------------------- -int UkEngine::processTelexW(UkKeyEvent& ev) { +int UkEngine::processTelexW(UkKeyEvent &ev) { if (!m_pCtrl->vietKey) return processAppend(ev); - int ret; + int ret; static bool usedAsMapChar = false; - int capsLockOn = 0; - int shiftPressed = 0; + int capsLockOn = 0; + int shiftPressed = 0; if (m_keyCheckFunc) m_keyCheckFunc(&shiftPressed, &capsLockOn); if (usedAsMapChar) { ev.evType = vneMapChar; - ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; + ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; if (capsLockOn) ev.vnSym = changeCase(ev.vnSym); ev.chType = ukcVn; - ret = processMapChar(ev); + ret = processMapChar(ev); if (ret == 0) { if (m_current >= 0) m_current--; usedAsMapChar = false; - ev.evType = vneHookAll; + ev.evType = vneHookAll; return processHook(ev); } return ret; } - ev.evType = vneHookAll; + ev.evType = vneHookAll; usedAsMapChar = false; - ret = processHook(ev); + ret = processHook(ev); if (ret == 0) { if (m_current >= 0) m_current--; ev.evType = vneMapChar; - ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; + ev.vnSym = isupper(ev.keyCode) ? vnl_Uh : vnl_uh; if (capsLockOn) ev.vnSym = changeCase(ev.vnSym); - ev.chType = ukcVn; + ev.chType = ukcVn; usedAsMapChar = true; return processMapChar(ev); } @@ -1127,274 +1786,306 @@ int UkEngine::processTelexW(UkKeyEvent& ev) { } //---------------------------------------------------------- -int UkEngine::checkEscapeVIQR(UkKeyEvent& ev) { +int UkEngine::checkEscapeVIQR(UkKeyEvent &ev) { if (m_current < 0) return 0; - WordInfo& entry = m_buffer[m_current]; - int escape = 0; + WordInfo &entry = m_buffer[m_current]; + int escape = 0; if (entry.form == vnw_v || entry.form == vnw_cv) { switch (ev.keyCode) { - case '^': escape = (entry.vnSym == vnl_a || entry.vnSym == vnl_o || entry.vnSym == vnl_e); break; - case '(': escape = (entry.vnSym == vnl_a); break; - case '+': escape = (entry.vnSym == vnl_o || entry.vnSym == vnl_u); break; - case '\'': - case '`': - case '?': - case '~': - case '.': escape = (entry.tone == 0); break; + case '^': + escape = (entry.vnSym == vnl_a || entry.vnSym == vnl_o || + entry.vnSym == vnl_e); + break; + case '(': + escape = (entry.vnSym == vnl_a); + break; + case '+': + escape = (entry.vnSym == vnl_o || entry.vnSym == vnl_u); + break; + case '\'': + case '`': + case '?': + case '~': + case '.': + escape = (entry.tone == 0); + break; } } else if (entry.form == vnw_nonVn) { unsigned char ch = toupper(entry.keyCode); switch (ev.keyCode) { - case '^': escape = (ch == 'A' || ch == 'O' || ch == 'E'); break; - case '(': escape = (ch == 'A'); break; - case '+': escape = (ch == 'O' || ch == 'U'); break; - case '\'': - case '`': - case '?': - case '~': - case '.': escape = (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'Y'); break; + case '^': + escape = (ch == 'A' || ch == 'O' || ch == 'E'); + break; + case '(': + escape = (ch == 'A'); + break; + case '+': + escape = (ch == 'O' || ch == 'U'); + break; + case '\'': + case '`': + case '?': + case '~': + case '.': + escape = (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || + ch == 'U' || ch == 'Y'); + break; } } if (escape) { m_current++; - WordInfo* p = &m_buffer[m_current]; - p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + WordInfo *p = &m_buffer[m_current]; + p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; p->c1Offset = p->c2Offset = p->vOffset = -1; - p->keyCode = '?'; - p->vnSym = vnl_nonVnChar; + p->keyCode = '?'; + p->vnSym = vnl_nonVnChar; m_current++; p++; - p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; p->c1Offset = p->c2Offset = p->vOffset = -1; - p->keyCode = ev.keyCode; - p->vnSym = vnl_nonVnChar; + p->keyCode = ev.keyCode; + p->vnSym = vnl_nonVnChar; // write output - m_pOutBuf[0] = '\\'; - m_pOutBuf[1] = ev.keyCode; - *m_pOutSize = 2; + m_pOutBuf[0] = '\\'; + m_pOutBuf[1] = ev.keyCode; + *m_pOutSize = 2; m_outputWritten = true; } return escape; } //---------------------------------------------------------- -int UkEngine::processAppend(UkKeyEvent& ev) { +int UkEngine::processAppend(UkKeyEvent &ev) { int ret = 0; switch (ev.chType) { - case ukcReset: + case ukcReset: #if defined(_WIN32) - if (ev.keyCode == ENTER_CHAR) { - if (m_pCtrl->options.macroEnabled && macroMatch(ev)) - return 1; - } -#endif - reset(); - return 0; - case ukcWordBreak: m_singleMode = false; return processWordEnd(ev); - case ukcNonVn: { - if (m_pCtrl->vietKey && m_pCtrl->charsetId == CONV_CHARSET_VIQR && checkEscapeVIQR(ev)) + if (ev.keyCode == ENTER_CHAR) { + if (m_pCtrl->options.macroEnabled && macroMatch(ev)) return 1; - - m_current++; - WordInfo& entry = m_buffer[m_current]; - entry.form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - entry.keyCode = ev.keyCode; - entry.vnSym = vnToLower(ev.vnSym); - entry.tone = 0; - entry.caps = (entry.vnSym != ev.vnSym); - if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; } - case ukcVn: { - if (IsVnVowel[ev.vnSym]) { - VnLexiName v = (VnLexiName)StdVnNoTone[vnToLower(ev.vnSym)]; - if (m_current >= 0 && m_buffer[m_current].form == vnw_c && ((m_buffer[m_current].cseq == cs_q && v == vnl_u) || (m_buffer[m_current].cseq == cs_g && v == vnl_i))) { - return appendConsonnant(ev); // process u after q, i after g as consonnants - } - return appendVowel(ev); +#endif + reset(); + return 0; + case ukcWordBreak: + m_singleMode = false; + return processWordEnd(ev); + case ukcNonVn: { + if (m_pCtrl->vietKey && m_pCtrl->charsetId == CONV_CHARSET_VIQR && + checkEscapeVIQR(ev)) + return 1; + + m_current++; + WordInfo &entry = m_buffer[m_current]; + entry.form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + entry.keyCode = ev.keyCode; + entry.vnSym = vnToLower(ev.vnSym); + entry.tone = 0; + entry.caps = (entry.vnSym != ev.vnSym); + if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + } + case ukcVn: { + if (IsVnVowel[ev.vnSym]) { + VnLexiName v = (VnLexiName)StdVnNoTone[vnToLower(ev.vnSym)]; + if (m_current >= 0 && m_buffer[m_current].form == vnw_c && + ((m_buffer[m_current].cseq == cs_q && v == vnl_u) || + (m_buffer[m_current].cseq == cs_g && v == vnl_i))) { + return appendConsonnant( + ev); // process u after q, i after g as consonnants } - return appendConsonnant(ev); - } break; + return appendVowel(ev); + } + return appendConsonnant(ev); + } break; } return ret; } //---------------------------------------------------------- -int UkEngine::appendVowel(UkKeyEvent& ev) { +int UkEngine::appendVowel(UkKeyEvent &ev) { bool autoCompleted = false; - bool complexEvent = false; + bool complexEvent = false; m_current++; - WordInfo& entry = m_buffer[m_current]; + WordInfo &entry = m_buffer[m_current]; VnLexiName lowerSym = vnToLower(ev.vnSym); - VnLexiName canSym = (VnLexiName)StdVnNoTone[lowerSym]; + VnLexiName canSym = (VnLexiName)StdVnNoTone[lowerSym]; - entry.vnSym = canSym; - entry.caps = (lowerSym != ev.vnSym); - entry.tone = (lowerSym - canSym) / 2; + entry.vnSym = canSym; + entry.caps = (lowerSym != ev.vnSym); + entry.tone = (lowerSym - canSym) / 2; entry.keyCode = ev.keyCode; if (m_current == 0 || !m_pCtrl->vietKey) { - entry.form = vnw_v; + entry.form = vnw_v; entry.c1Offset = entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = lookupVSeq(canSym); + entry.vOffset = 0; + entry.vseq = lookupVSeq(canSym); - if (!m_pCtrl->vietKey || ((m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && isalpha(entry.keyCode))) { + if (!m_pCtrl->vietKey || + ((m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && + isalpha(entry.keyCode))) { return 0; } markChange(m_current); return 1; } - WordInfo& prev = m_buffer[m_current - 1]; - VowelSeq vs, newVs; - ConSeq cs; - int prevTonePos; - int tone, newTone, tonePos, newTonePos; + WordInfo &prev = m_buffer[m_current - 1]; + VowelSeq vs, newVs; + ConSeq cs; + int prevTonePos; + int tone, newTone, tonePos, newTonePos; switch (prev.form) { - case vnw_empty: - entry.form = vnw_v; - entry.c1Offset = entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = newVs = lookupVSeq(canSym); - break; + case vnw_empty: + entry.form = vnw_v; + entry.c1Offset = entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs = lookupVSeq(canSym); + break; + + case vnw_nonVn: + case vnw_cvc: + case vnw_vc: + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; - case vnw_nonVn: - case vnw_cvc: - case vnw_vc: - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - break; + case vnw_v: + case vnw_cv: + vs = prev.vseq; - case vnw_v: - case vnw_cv: - vs = prev.vseq; + prevTonePos = (m_current - 1) - (VSeqList[vs].len - 1) + + getTonePosition(vs, true); + tone = m_buffer[prevTonePos].tone; - prevTonePos = (m_current - 1) - (VSeqList[vs].len - 1) + getTonePosition(vs, true); - tone = m_buffer[prevTonePos].tone; + // u+o/uo+ + u/i -> u+o+ + u/i + if ((vs == vs_uoh || vs == vs_uho) && + (lowerSym == vnl_i || lowerSym == vnl_u)) { + if (vs == vs_uho) { + markChange(m_current - 1); + prev.vnSym = vnl_oh; + prev.vseq = vs_uhoh; + } else { + markChange(m_current - 2); + m_buffer[m_current - 2].vnSym = vnl_uh; + m_buffer[m_current - 2].vseq = vs_uh; + } - // u+o/uo+ + u/i -> u+o+ + u/i - if ((vs == vs_uoh || vs == vs_uho) && (lowerSym == vnl_i || lowerSym == vnl_u)) { - if (vs == vs_uho) { - markChange(m_current - 1); - prev.vnSym = vnl_oh; - prev.vseq = vs_uhoh; - } else { - markChange(m_current - 2); - m_buffer[m_current - 2].vnSym = vnl_uh; - m_buffer[m_current - 2].vseq = vs_uh; - } + vs = vs_uhoh; + complexEvent = true; + } - vs = vs_uhoh; - complexEvent = true; - } + if (lowerSym != canSym && tone != 0) // new sym has a tone, but there's + // is already a preceeding tone + newVs = vs_nil; + else { + if (VSeqList[vs].len == 3) + newVs = vs_nil; + else if (VSeqList[vs].len == 2) + newVs = + lookupVSeq(VSeqList[vs].v[0], VSeqList[vs].v[1], canSym); + else + newVs = lookupVSeq(VSeqList[vs].v[0], canSym); + } - if (lowerSym != canSym && tone != 0) // new sym has a tone, but there's - // is already a preceeding tone + if (newVs != vs_nil && prev.form == vnw_cv) { + cs = m_buffer[m_current - 1 - prev.c1Offset].cseq; + if (!isValidCV(cs, newVs)) newVs = vs_nil; - else { - if (VSeqList[vs].len == 3) - newVs = vs_nil; - else if (VSeqList[vs].len == 2) - newVs = lookupVSeq(VSeqList[vs].v[0], VSeqList[vs].v[1], canSym); - else - newVs = lookupVSeq(VSeqList[vs].v[0], canSym); - } + } - if (newVs != vs_nil && prev.form == vnw_cv) { - cs = m_buffer[m_current - 1 - prev.c1Offset].cseq; - if (!isValidCV(cs, newVs)) - newVs = vs_nil; - } + if (newVs == vs_nil) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + break; + } - if (newVs == vs_nil) { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - break; + entry.form = prev.form; + if (prev.form == vnw_cv) + entry.c1Offset = prev.c1Offset + 1; + else + entry.c1Offset = -1; + entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs; + entry.tone = 0; + + newTone = (lowerSym - canSym) / 2; + if (tone == 0) { + if (newTone != 0) { + tone = newTone; + tonePos = getTonePosition(newVs, true) + + ((m_current - 1) - VSeqList[vs].len + 1); + markChange(tonePos); + m_buffer[tonePos].tone = tone; + return 1; } - - entry.form = prev.form; - if (prev.form == vnw_cv) - entry.c1Offset = prev.c1Offset + 1; - else - entry.c1Offset = -1; - entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = newVs; - entry.tone = 0; - - newTone = (lowerSym - canSym) / 2; - if (tone == 0) { - if (newTone != 0) { - tone = newTone; - tonePos = getTonePosition(newVs, true) + ((m_current - 1) - VSeqList[vs].len + 1); - markChange(tonePos); - m_buffer[tonePos].tone = tone; - return 1; - } - } else { - newTonePos = getTonePosition(newVs, true) + ((m_current - 1) - VSeqList[vs].len + 1); - if (newTonePos != prevTonePos) { - markChange(prevTonePos); - m_buffer[prevTonePos].tone = 0; - markChange(newTonePos); - if (newTone != 0) - tone = newTone; - m_buffer[newTonePos].tone = tone; - return 1; - } - if (newTone != 0 && newTone != tone) { + } else { + newTonePos = getTonePosition(newVs, true) + + ((m_current - 1) - VSeqList[vs].len + 1); + if (newTonePos != prevTonePos) { + markChange(prevTonePos); + m_buffer[prevTonePos].tone = 0; + markChange(newTonePos); + if (newTone != 0) tone = newTone; - markChange(prevTonePos); - m_buffer[prevTonePos].tone = tone; - return 1; - } - } - - break; - case vnw_c: - newVs = lookupVSeq(canSym); - cs = prev.cseq; - if (!isValidCV(cs, newVs)) { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - break; + m_buffer[newTonePos].tone = tone; + return 1; } - - entry.form = vnw_cv; - entry.c1Offset = 1; - entry.c2Offset = -1; - entry.vOffset = 0; - entry.vseq = newVs; - - if (cs == cs_gi && prev.tone != 0) { - if (entry.tone == 0) - entry.tone = prev.tone; - markChange(m_current - 1); - prev.tone = 0; + if (newTone != 0 && newTone != tone) { + tone = newTone; + markChange(prevTonePos); + m_buffer[prevTonePos].tone = tone; return 1; } + } + break; + case vnw_c: + newVs = lookupVSeq(canSym); + cs = prev.cseq; + if (!isValidCV(cs, newVs)) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; break; + } + + entry.form = vnw_cv; + entry.c1Offset = 1; + entry.c2Offset = -1; + entry.vOffset = 0; + entry.vseq = newVs; + + if (cs == cs_gi && prev.tone != 0) { + if (entry.tone == 0) + entry.tone = prev.tone; + markChange(m_current - 1); + prev.tone = 0; + return 1; + } + + break; } if (complexEvent) { return 1; } - if (!autoCompleted && (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && isalpha(entry.keyCode)) { + if (!autoCompleted && (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && + isalpha(entry.keyCode)) { return 0; } @@ -1403,173 +2094,175 @@ int UkEngine::appendVowel(UkKeyEvent& ev) { } //---------------------------------------------------------- -int UkEngine::appendConsonnant(UkKeyEvent& ev) { +int UkEngine::appendConsonnant(UkKeyEvent &ev) { bool complexEvent = false; m_current++; - WordInfo& entry = m_buffer[m_current]; + WordInfo &entry = m_buffer[m_current]; VnLexiName lowerSym = vnToLower(ev.vnSym); - entry.vnSym = lowerSym; - entry.caps = (lowerSym != ev.vnSym); + entry.vnSym = lowerSym; + entry.caps = (lowerSym != ev.vnSym); entry.keyCode = ev.keyCode; - entry.tone = 0; + entry.tone = 0; if (m_current == 0 || !m_pCtrl->vietKey) { - entry.form = vnw_c; + entry.form = vnw_c; entry.c1Offset = 0; entry.c2Offset = -1; - entry.vOffset = -1; - entry.cseq = lookupCSeq(lowerSym); + entry.vOffset = -1; + entry.cseq = lookupCSeq(lowerSym); if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) return 0; markChange(m_current); return 1; } - ConSeq cs, newCs, c1; - VowelSeq vs, newVs; - bool isValid; + ConSeq cs, newCs, c1; + VowelSeq vs, newVs; + bool isValid; - WordInfo& prev = m_buffer[m_current - 1]; + WordInfo &prev = m_buffer[m_current - 1]; switch (prev.form) { - case vnw_nonVn: - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; - case vnw_empty: - entry.form = vnw_c; - entry.c1Offset = 0; - entry.c2Offset = -1; - entry.vOffset = -1; - entry.cseq = lookupCSeq(lowerSym); - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; - case vnw_v: - case vnw_cv: - vs = prev.vseq; - newVs = vs; - if (vs == vs_uoh || vs == vs_uho) { - newVs = vs_uhoh; - } + case vnw_nonVn: + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_empty: + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + entry.cseq = lookupCSeq(lowerSym); + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_v: + case vnw_cv: + vs = prev.vseq; + newVs = vs; + if (vs == vs_uoh || vs == vs_uho) { + newVs = vs_uhoh; + } - c1 = cs_nil; - if (prev.c1Offset != -1) - c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; + c1 = cs_nil; + if (prev.c1Offset != -1) + c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; - newCs = lookupCSeq(lowerSym); - isValid = isValidCVC(c1, newVs, newCs); - - if (isValid) { - // check u+o -> u+o+ - if (vs == vs_uho) { - markChange(m_current - 1); - prev.vnSym = vnl_oh; - prev.vseq = vs_uhoh; - complexEvent = true; - } else if (vs == vs_uoh) { - markChange(m_current - 2); - m_buffer[m_current - 2].vnSym = vnl_uh; - m_buffer[m_current - 2].vseq = vs_uh; - prev.vseq = vs_uhoh; - complexEvent = true; - } + newCs = lookupCSeq(lowerSym); + isValid = isValidCVC(c1, newVs, newCs); - if (prev.form == vnw_v) { - entry.form = vnw_vc; - entry.c1Offset = -1; - entry.c2Offset = 0; - entry.vOffset = 1; - } else { // prev == vnw_cv - entry.form = vnw_cvc; - entry.c1Offset = prev.c1Offset + 1; - entry.c2Offset = 0; - entry.vOffset = 1; - } - entry.cseq = newCs; - - // reposition tone if needed - int oldIdx = (m_current - 1) - (VSeqList[vs].len - 1) + getTonePosition(vs, true); - if (m_buffer[oldIdx].tone != 0) { - int newIdx = (m_current - 1) - (VSeqList[newVs].len - 1) + getTonePosition(newVs, false); - if (newIdx != oldIdx) { - markChange(newIdx); - m_buffer[newIdx].tone = m_buffer[oldIdx].tone; - markChange(oldIdx); - m_buffer[oldIdx].tone = 0; - return 1; - } - } - } else { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + if (isValid) { + // check u+o -> u+o+ + if (vs == vs_uho) { + markChange(m_current - 1); + prev.vnSym = vnl_oh; + prev.vseq = vs_uhoh; + complexEvent = true; + } else if (vs == vs_uoh) { + markChange(m_current - 2); + m_buffer[m_current - 2].vnSym = vnl_uh; + m_buffer[m_current - 2].vseq = vs_uh; + prev.vseq = vs_uhoh; + complexEvent = true; } - if (complexEvent) { - return 1; + if (prev.form == vnw_v) { + entry.form = vnw_vc; + entry.c1Offset = -1; + entry.c2Offset = 0; + entry.vOffset = 1; + } else { // prev == vnw_cv + entry.form = vnw_cvc; + entry.c1Offset = prev.c1Offset + 1; + entry.c2Offset = 0; + entry.vOffset = 1; } + entry.cseq = newCs; + + // reposition tone if needed + int oldIdx = (m_current - 1) - (VSeqList[vs].len - 1) + + getTonePosition(vs, true); + if (m_buffer[oldIdx].tone != 0) { + int newIdx = (m_current - 1) - (VSeqList[newVs].len - 1) + + getTonePosition(newVs, false); + if (newIdx != oldIdx) { + markChange(newIdx); + m_buffer[newIdx].tone = m_buffer[oldIdx].tone; + markChange(oldIdx); + m_buffer[oldIdx].tone = 0; + return 1; + } + } + } else { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + } - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); + if (complexEvent) { return 1; - case vnw_c: - case vnw_vc: - case vnw_cvc: - cs = prev.cseq; - if (CSeqList[cs].len == 3) - newCs = cs_nil; - else if (CSeqList[cs].len == 2) - newCs = lookupCSeq(CSeqList[cs].c[0], CSeqList[cs].c[1], lowerSym); - else - newCs = lookupCSeq(CSeqList[cs].c[0], lowerSym); + } - if (newCs != cs_nil && (prev.form == vnw_vc || prev.form == vnw_cvc)) { - // Check CVC combination - c1 = cs_nil; - if (prev.c1Offset != -1) - c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; + case vnw_c: + case vnw_vc: + case vnw_cvc: + cs = prev.cseq; + if (CSeqList[cs].len == 3) + newCs = cs_nil; + else if (CSeqList[cs].len == 2) + newCs = lookupCSeq(CSeqList[cs].c[0], CSeqList[cs].c[1], lowerSym); + else + newCs = lookupCSeq(CSeqList[cs].c[0], lowerSym); - int vIdx = (m_current - 1) - prev.vOffset; - vs = m_buffer[vIdx].vseq; - isValid = isValidCVC(c1, vs, newCs); + if (newCs != cs_nil && (prev.form == vnw_vc || prev.form == vnw_cvc)) { + // Check CVC combination + c1 = cs_nil; + if (prev.c1Offset != -1) + c1 = m_buffer[m_current - 1 - prev.c1Offset].cseq; - if (!isValid) - newCs = cs_nil; - } + int vIdx = (m_current - 1) - prev.vOffset; + vs = m_buffer[vIdx].vseq; + isValid = isValidCVC(c1, vs, newCs); - if (newCs == cs_nil) { - entry.form = vnw_nonVn; - entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - } else { - if (prev.form == vnw_c) { - entry.form = vnw_c; - entry.c1Offset = 0; - entry.c2Offset = -1; - entry.vOffset = -1; - } else if (prev.form == vnw_vc) { - entry.form = vnw_vc; - entry.c1Offset = -1; - entry.c2Offset = 0; - entry.vOffset = prev.vOffset + 1; - } else { // vnw_cvc - entry.form = vnw_cvc; - entry.c1Offset = prev.c1Offset + 1; - entry.c2Offset = 0; - entry.vOffset = prev.vOffset + 1; - } - entry.cseq = newCs; + if (!isValid) + newCs = cs_nil; + } + + if (newCs == cs_nil) { + entry.form = vnw_nonVn; + entry.c1Offset = entry.c2Offset = entry.vOffset = -1; + } else { + if (prev.form == vnw_c) { + entry.form = vnw_c; + entry.c1Offset = 0; + entry.c2Offset = -1; + entry.vOffset = -1; + } else if (prev.form == vnw_vc) { + entry.form = vnw_vc; + entry.c1Offset = -1; + entry.c2Offset = 0; + entry.vOffset = prev.vOffset + 1; + } else { // vnw_cvc + entry.form = vnw_cvc; + entry.c1Offset = prev.c1Offset + 1; + entry.c2Offset = 0; + entry.vOffset = prev.vOffset + 1; } - if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) - return 0; - markChange(m_current); - return 1; + entry.cseq = newCs; + } + if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) + return 0; + markChange(m_current); + return 1; } if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) @@ -1579,8 +2272,10 @@ int UkEngine::appendConsonnant(UkKeyEvent& ev) { } //---------------------------------------------------------- -int UkEngine::processEscChar(UkKeyEvent& ev) { - if (m_pCtrl->vietKey && m_current >= 0 && m_buffer[m_current].form != vnw_empty && m_buffer[m_current].form != vnw_nonVn) { +int UkEngine::processEscChar(UkKeyEvent &ev) { + if (m_pCtrl->vietKey && m_current >= 0 && + m_buffer[m_current].form != vnw_empty && + m_buffer[m_current].form != vnw_nonVn) { m_toEscape = true; } return processAppend(ev); @@ -1597,39 +2292,42 @@ void UkEngine::pass(int keyCode) { // This can be called only after other processing have been done. // The new event is supposed to be put into m_buffer already //--------------------------------------------- -int UkEngine::processNoSpellCheck(UkKeyEvent& ev) { - WordInfo& entry = m_buffer[m_current]; +int UkEngine::processNoSpellCheck(UkKeyEvent &ev) { + WordInfo &entry = m_buffer[m_current]; if (IsVnVowel[entry.vnSym]) { - entry.form = vnw_v; - entry.vOffset = 0; - entry.vseq = lookupVSeq(entry.vnSym); + entry.form = vnw_v; + entry.vOffset = 0; + entry.vseq = lookupVSeq(entry.vnSym); entry.c1Offset = entry.c2Offset = -1; } else { - entry.form = vnw_c; + entry.form = vnw_c; entry.c1Offset = 0; entry.c2Offset = -1; - entry.vOffset = -1; - entry.cseq = lookupCSeq(entry.vnSym); + entry.vOffset = -1; + entry.cseq = lookupCSeq(entry.vnSym); } - if (ev.evType == vneNormal && ((entry.keyCode >= 'a' && entry.keyCode <= 'z') || (entry.keyCode >= 'A' && entry.keyCode <= 'Z'))) + if (ev.evType == vneNormal && + ((entry.keyCode >= 'a' && entry.keyCode <= 'z') || + (entry.keyCode >= 'A' && entry.keyCode <= 'Z'))) return 0; markChange(m_current); return 1; } //---------------------------------------------------------- -int UkEngine::process(unsigned int keyCode, int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType) { +int UkEngine::process(unsigned int keyCode, int &backs, unsigned char *outBuf, + int &outSize, UkOutputType &outType) { UkKeyEvent ev; prepareBuffer(); - m_backs = 0; - m_changePos = m_current + 1; - m_pOutBuf = outBuf; - m_pOutSize = &outSize; + m_backs = 0; + m_changePos = m_current + 1; + m_pOutBuf = outBuf; + m_pOutSize = &outSize; m_outputWritten = false; - m_reverted = false; - m_keyRestored = false; - m_keyRestoring = false; - m_outType = UkCharOutput; + m_reverted = false; + m_keyRestored = false; + m_keyRestoring = false; + m_outType = UkCharOutput; m_pCtrl->input.keyCodeToEvent(keyCode, ev); @@ -1638,7 +2336,8 @@ int UkEngine::process(unsigned int keyCode, int& backs, unsigned char* outBuf, i ret = (this->*UkKeyProcList[ev.evType])(ev); } else { m_toEscape = false; - if (m_current < 0 || ev.evType == vneNormal || ev.evType == vneEscChar) { + if (m_current < 0 || ev.evType == vneNormal || + ev.evType == vneEscChar) { ret = processAppend(ev); } else { m_current--; @@ -1649,7 +2348,9 @@ int UkEngine::process(unsigned int keyCode, int& backs, unsigned char* outBuf, i } } - if (m_pCtrl->vietKey && m_current >= 0 && m_buffer[m_current].form == vnw_nonVn && ev.chType == ukcVn && (!m_pCtrl->options.spellCheckEnabled || m_singleMode)) { + if (m_pCtrl->vietKey && m_current >= 0 && + m_buffer[m_current].form == vnw_nonVn && ev.chType == ukcVn && + (!m_pCtrl->options.spellCheckEnabled || m_singleMode)) { // The spell check has failed, but because we are in non-spellcheck // mode, we consider the new character as the beginning of a new word @@ -1668,12 +2369,12 @@ int UkEngine::process(unsigned int keyCode, int& backs, unsigned char* outBuf, i if (m_current >= 0) { ev.chType = m_pCtrl->input.getCharType(ev.keyCode); m_keyCurrent++; - m_keyStrokes[m_keyCurrent].ev = ev; + m_keyStrokes[m_keyCurrent].ev = ev; m_keyStrokes[m_keyCurrent].converted = (ret && !m_keyRestored); } if (ret == 0) { - backs = 0; + backs = 0; outSize = 0; outType = m_outType; return 0; @@ -1688,26 +2389,29 @@ int UkEngine::process(unsigned int keyCode, int& backs, unsigned char* outBuf, i return ret; } //---------------------------------------------------------- -void UkEngine::rebuildChar(VnLexiName ch, int& backs, unsigned char* outBuf, int& outSize) { - static const std::unordered_map map{{vnl_Ar, vneRoof_a}, {vnl_Ab, vneBowl}, {vnl_DD, vneDd}, {vnl_Er, vneRoof_e}, - {vnl_Or, vneRoof_o}, {vnl_Oh, vneHook_o}, {vnl_Uh, vneHook_u}}; +void UkEngine::rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, + int &outSize) { + static const std::unordered_map map{ + {vnl_Ar, vneRoof_a}, {vnl_Ab, vneBowl}, {vnl_DD, vneDd}, + {vnl_Er, vneRoof_e}, {vnl_Or, vneRoof_o}, {vnl_Oh, vneHook_o}, + {vnl_Uh, vneHook_u}}; if (ch == vnl_nonVnChar) { return; } prepareBuffer(); - m_backs = 0; + m_backs = 0; m_changePos = m_current + 1; - m_pOutBuf = outBuf; - m_pOutSize = &outSize; + m_pOutBuf = outBuf; + m_pOutSize = &outSize; UkKeyEvent ev; - auto rootChar = StdVnRootChar[ch]; - auto noToneChar = StdVnNoTone[ch]; + auto rootChar = StdVnRootChar[ch]; + auto noToneChar = StdVnNoTone[ch]; - auto keyCode = UnicodeTable[rootChar]; + auto keyCode = UnicodeTable[rootChar]; m_pCtrl->input.keyCodeToEvent(keyCode, ev); // root char @@ -1715,11 +2419,13 @@ void UkEngine::rebuildChar(VnLexiName ch, int& backs, unsigned char* outBuf, int // add root char to key strokes m_keyCurrent++; - m_keyStrokes[m_keyCurrent].ev = ev; + m_keyStrokes[m_keyCurrent].ev = ev; m_keyStrokes[m_keyCurrent].converted = true; // modify vowel - auto it = map.find(noToneChar % 2 == 0 ? static_cast(noToneChar) : static_cast(noToneChar - 1)); + auto it = + map.find(noToneChar % 2 == 0 ? static_cast(noToneChar) + : static_cast(noToneChar - 1)); if (it != map.end()) { ev.evType = it->second; (this->*UkKeyProcList[ev.evType])(ev); @@ -1729,7 +2435,7 @@ void UkEngine::rebuildChar(VnLexiName ch, int& backs, unsigned char* outBuf, int auto tone = (ch - noToneChar) / 2; if (tone >= 1 && tone <= 5) { ev.evType = vneTone0 + tone; - ev.tone = tone; + ev.tone = tone; (this->*UkKeyProcList[ev.evType])(ev); } @@ -1744,12 +2450,12 @@ void UkEngine::rebuildChar(VnLexiName ch, int& backs, unsigned char* outBuf, int // outSize: [in] size of buffer in bytes // [out] bytes written to buffer //---------------------------------------------------------- -int UkEngine::writeOutput(unsigned char* outBuf, int& outSize) { - StdVnChar stdChar; - int i, bytesWritten; - int ret = 1; +int UkEngine::writeOutput(unsigned char *outBuf, int &outSize) { + StdVnChar stdChar; + int i, bytesWritten; + int ret = 1; StringBOStream os(outBuf, outSize); - VnCharset* pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); + VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); pCharset->startOutput(); for (i = m_changePos; i <= m_current; i++) { @@ -1782,13 +2488,14 @@ int UkEngine::getSeqSteps(int first, int last) const { if (last < first) return 0; - if (m_pCtrl->charsetId == CONV_CHARSET_XUTF8 || m_pCtrl->charsetId == CONV_CHARSET_UNICODE) + if (m_pCtrl->charsetId == CONV_CHARSET_XUTF8 || + m_pCtrl->charsetId == CONV_CHARSET_UNICODE) return (last - first + 1); StringBOStream os(0, 0); - int i, bytesWritten; + int i, bytesWritten; - VnCharset* pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); + VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId); pCharset->startOutput(); for (i = first; i <= last; i++) { @@ -1833,49 +2540,56 @@ void UkEngine::synchKeyStrokeBuffer() { // in character buffer, we have reached a word break, // so we also need to move key stroke pointer backward to corresponding // word break - while (m_keyCurrent >= 0 && m_keyStrokes[m_keyCurrent].ev.chType != ukcWordBreak) { + while (m_keyCurrent >= 0 && + m_keyStrokes[m_keyCurrent].ev.chType != ukcWordBreak) { m_keyCurrent--; } } } //--------------------------------------------- -int UkEngine::processBackspace(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType) { +int UkEngine::processBackspace(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType) { outType = UkCharOutput; if (!m_pCtrl->vietKey || m_current < 0) { - backs = 0; + backs = 0; outSize = 0; return 0; } - m_backs = 0; + m_backs = 0; m_changePos = m_current + 1; markChange(m_current); - if (m_current == 0 || m_buffer[m_current].form == vnw_empty || m_buffer[m_current].form == vnw_nonVn || m_buffer[m_current].form == vnw_c || - m_buffer[m_current - 1].form == vnw_c || m_buffer[m_current - 1].form == vnw_cvc || m_buffer[m_current - 1].form == vnw_vc) { + if (m_current == 0 || m_buffer[m_current].form == vnw_empty || + m_buffer[m_current].form == vnw_nonVn || + m_buffer[m_current].form == vnw_c || + m_buffer[m_current - 1].form == vnw_c || + m_buffer[m_current - 1].form == vnw_cvc || + m_buffer[m_current - 1].form == vnw_vc) { m_current--; - backs = m_backs; + backs = m_backs; outSize = 0; synchKeyStrokeBuffer(); return (backs > 1); } VowelSeq vs, newVs; - int curTonePos, newTonePos, tone, vStart, vEnd; + int curTonePos, newTonePos, tone, vStart, vEnd; - vEnd = m_current - m_buffer[m_current].vOffset; - vs = m_buffer[vEnd].vseq; - vStart = vEnd - VSeqList[vs].len + 1; - newVs = m_buffer[m_current - 1].vseq; + vEnd = m_current - m_buffer[m_current].vOffset; + vs = m_buffer[vEnd].vseq; + vStart = vEnd - VSeqList[vs].len + 1; + newVs = m_buffer[m_current - 1].vseq; curTonePos = vStart + getTonePosition(vs, vEnd == m_current); newTonePos = vStart + getTonePosition(newVs, true); - tone = m_buffer[curTonePos].tone; + tone = m_buffer[curTonePos].tone; - if (tone == 0 || curTonePos == newTonePos || (curTonePos == m_current && m_buffer[m_current].tone != 0)) { + if (tone == 0 || curTonePos == newTonePos || + (curTonePos == m_current && m_buffer[m_current].tone != 0)) { m_current--; - backs = m_backs; + backs = m_backs; outSize = 0; synchKeyStrokeBuffer(); return (backs > 1); @@ -1894,16 +2608,14 @@ int UkEngine::processBackspace(int& backs, unsigned char* outBuf, int& outSize, //------------------------------------------------ void UkEngine::reset() { - m_current = -1; + m_current = -1; m_keyCurrent = -1; m_singleMode = false; - m_toEscape = false; + m_toEscape = false; } //------------------------------------------------ -void UkEngine::resetKeyBuf() { - m_keyCurrent = -1; -} +void UkEngine::resetKeyBuf() { m_keyCurrent = -1; } //------------------------------------------------ UkEngine::UkEngine() { @@ -1911,16 +2623,16 @@ UkEngine::UkEngine() { engineClassInit(); m_classInit = true; } - m_pCtrl = 0; - m_bufSize = MAX_UK_ENGINE; - m_keyBufSize = MAX_UK_ENGINE; - m_current = -1; - m_keyCurrent = -1; - m_singleMode = false; + m_pCtrl = 0; + m_bufSize = MAX_UK_ENGINE; + m_keyBufSize = MAX_UK_ENGINE; + m_current = -1; + m_keyCurrent = -1; + m_singleMode = false; m_keyCheckFunc = 0; - m_reverted = false; - m_toEscape = false; - m_keyRestored = false; + m_reverted = false; + m_toEscape = false; + m_keyRestored = false; } //---------------------------------------------------- @@ -1932,13 +2644,15 @@ void UkEngine::prepareBuffer() { if (m_current >= 0 && m_current + 10 >= m_bufSize) { // Get rid of at least half of the current entries // don't get rid from the middle of a word. - for (rid = m_current / 2; m_buffer[rid].form != vnw_empty && rid < m_current; rid++) + for (rid = m_current / 2; + m_buffer[rid].form != vnw_empty && rid < m_current; rid++) ; if (rid == m_current) { m_current = -1; } else { rid++; - memmove(m_buffer, m_buffer + rid, (m_current - rid + 1) * sizeof(WordInfo)); + memmove(m_buffer, m_buffer + rid, + (m_current - rid + 1) * sizeof(WordInfo)); m_current -= rid; } } @@ -1947,21 +2661,18 @@ void UkEngine::prepareBuffer() { if (m_keyCurrent > 0 && m_keyCurrent + 1 >= m_keyBufSize) { // Get rid of at least half of the current entries rid = m_keyCurrent / 2; - memmove(m_keyStrokes, m_keyStrokes + rid, (m_keyCurrent - rid + 1) * sizeof(m_keyStrokes[0])); + memmove(m_keyStrokes, m_keyStrokes + rid, + (m_keyCurrent - rid + 1) * sizeof(m_keyStrokes[0])); m_keyCurrent -= rid; } } #define ENTER_CHAR 13 -enum VnCaseType { - VnCaseNoChange, - VnCaseAllCapital, - VnCaseAllSmall -}; +enum VnCaseType { VnCaseNoChange, VnCaseAllCapital, VnCaseAllSmall }; //---------------------------------------------------- -int UkEngine::macroMatch(UkKeyEvent& ev) { - int capsLockOn = 0; +int UkEngine::macroMatch(UkKeyEvent &ev) { + int capsLockOn = 0; int shiftPressed = 0; if (m_keyCheckFunc) m_keyCheckFunc(&shiftPressed, &capsLockOn); @@ -1969,19 +2680,20 @@ int UkEngine::macroMatch(UkKeyEvent& ev) { if (shiftPressed && (ev.keyCode == ' ' || ev.keyCode == ENTER_CHAR)) return 0; - const StdVnChar* pMacText = NULL; - StdVnChar key[MAX_MACRO_KEY_LEN + 1]; - StdVnChar* pKeyStart; + const StdVnChar *pMacText = NULL; + StdVnChar key[MAX_MACRO_KEY_LEN + 1]; + StdVnChar *pKeyStart; // Use static macro text so we can gain a bit of performance // by avoiding memory allocation each time this function is called static StdVnChar macroText[MAX_MACRO_TEXT_LEN + 1]; - int i, j; + int i, j; i = m_current; while (i >= 0 && (m_current - i + 1) < MAX_MACRO_KEY_LEN) { - while (i >= 0 && m_buffer[i].form != vnw_empty && (m_current - i + 1) < MAX_MACRO_KEY_LEN) + while (i >= 0 && m_buffer[i].form != vnw_empty && + (m_current - i + 1) < MAX_MACRO_KEY_LEN) i--; if (i >= 0 && m_buffer[i].form != vnw_empty) return 0; @@ -2061,8 +2773,9 @@ int UkEngine::macroMatch(UkKeyEvent& ev) { // Convert to target output charset int outSize; int maxOutSize = *m_pOutSize; - int inLen = charCount * sizeof(StdVnChar); - VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, (UKBYTE*)macroText, (UKBYTE*)m_pOutBuf, &inLen, &maxOutSize); + int inLen = charCount * sizeof(StdVnChar); + VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, (UKBYTE *)macroText, + (UKBYTE *)m_pOutBuf, &inLen, &maxOutSize); outSize = maxOutSize; // write the last input character @@ -2074,32 +2787,37 @@ int UkEngine::macroMatch(UkKeyEvent& ev) { else vnChar = ev.keyCode; inLen = sizeof(StdVnChar); - VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, (UKBYTE*)&vnChar, ((UKBYTE*)m_pOutBuf) + outSize, &inLen, &maxOutSize); + VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId, + (UKBYTE *)&vnChar, ((UKBYTE *)m_pOutBuf) + outSize, &inLen, + &maxOutSize); outSize += maxOutSize; } int backs = m_backs; // store m_backs before calling reset reset(); m_outputWritten = true; - m_backs = backs; - *m_pOutSize = outSize; + m_backs = backs; + *m_pOutSize = outSize; return 1; } //---------------------------------------------------- -int UkEngine::restoreKeyStrokes(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType) { +int UkEngine::restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType) { outType = UkKeyOutput; if (!lastWordHasVnMark()) { - backs = 0; + backs = 0; outSize = 0; return 0; } - m_backs = 0; + m_backs = 0; m_changePos = m_current + 1; - int keyStart; + int keyStart; bool converted = false; - for (keyStart = m_keyCurrent; keyStart >= 0 && m_keyStrokes[keyStart].ev.chType != ukcWordBreak; keyStart--) { + for (keyStart = m_keyCurrent; + keyStart >= 0 && m_keyStrokes[keyStart].ev.chType != ukcWordBreak; + keyStart--) { if (m_keyStrokes[keyStart].converted) { converted = true; } @@ -2109,7 +2827,7 @@ int UkEngine::restoreKeyStrokes(int& backs, unsigned char* outBuf, int& outSize, if (!converted) { // no key stroke has been converted, so it doesn't make sense to restore // key strokes - backs = 0; + backs = 0; outSize = 0; return 0; } @@ -2120,8 +2838,8 @@ int UkEngine::restoreKeyStrokes(int& backs, unsigned char* outBuf, int& outSize, markChange(m_current + 1); backs = m_backs; - int count; - int i; + int count; + int i; UkKeyEvent ev; m_keyRestoring = true; for (i = keyStart, count = 0; i <= m_keyCurrent; i++) { @@ -2132,21 +2850,19 @@ int UkEngine::restoreKeyStrokes(int& backs, unsigned char* outBuf, int& outSize, m_keyStrokes[i].converted = false; processAppend(ev); } - outSize = count; + outSize = count; m_keyRestoring = false; return 1; } //-------------------------------------------------- -void UkEngine::setSingleMode() { - m_singleMode = true; -} +void UkEngine::setSingleMode() { m_singleMode = true; } //-------------------------------------------------- static void SetupUnikeyEngineOnce() { SetupInputClassifierTable(); - int i; + int i; VnLexiName lexi; // Calculate IsoStdVnCharMap @@ -2155,7 +2871,8 @@ static void SetupUnikeyEngineOnce() { } for (i = 0; SpecialWesternChars[i]; i++) { - IsoStdVnCharMap[SpecialWesternChars[i]] = (vnl_lastChar + i) + VnStdCharOffset; + IsoStdVnCharMap[SpecialWesternChars[i]] = + (vnl_lastChar + i) + VnStdCharOffset; } for (i = 0; i < 256; i++) { @@ -2167,9 +2884,7 @@ static void SetupUnikeyEngineOnce() { std::once_flag setupFlag; -void SetupUnikeyEngine() { - std::call_once(setupFlag, SetupUnikeyEngineOnce); -} +void SetupUnikeyEngine() { std::call_once(setupFlag, SetupUnikeyEngineOnce); } //-------------------------------------------------- bool UkEngine::atWordBeginning() const { @@ -2181,21 +2896,22 @@ bool UkEngine::atWordBeginning() const { // Spell-check, if is valid Vietnamese, return normally, if not: // restore key strokes if auto-restore is enabled //-------------------------------------------------- -int UkEngine::processWordEnd(UkKeyEvent& ev) { +int UkEngine::processWordEnd(UkKeyEvent &ev) { if (m_pCtrl->options.macroEnabled && macroMatch(ev)) return 1; - auto putKeyInBuffer = [this](UkKeyEvent& ev) { + auto putKeyInBuffer = [this](UkKeyEvent &ev) { m_current++; - WordInfo& entry = m_buffer[m_current]; - entry.form = vnw_empty; + WordInfo &entry = m_buffer[m_current]; + entry.form = vnw_empty; entry.c1Offset = entry.c2Offset = entry.vOffset = -1; - entry.keyCode = ev.keyCode; - entry.vnSym = vnToLower(ev.vnSym); - entry.caps = (entry.vnSym != ev.vnSym); + entry.keyCode = ev.keyCode; + entry.vnSym = vnToLower(ev.vnSym); + entry.caps = (entry.vnSym != ev.vnSym); }; - if (!m_pCtrl->options.spellCheckEnabled || m_singleMode || m_current < 0 || m_keyRestoring) { + if (!m_pCtrl->options.spellCheckEnabled || m_singleMode || m_current < 0 || + m_keyRestoring) { putKeyInBuffer(ev); return 0; } @@ -2204,7 +2920,7 @@ int UkEngine::processWordEnd(UkKeyEvent& ev) { if (m_pCtrl->options.autoNonVnRestore && lastWordIsNonVn()) { outSize = *m_pOutSize; if (restoreKeyStrokes(m_backs, m_pOutBuf, outSize, m_outType)) { - m_keyRestored = true; + m_keyRestored = true; m_outputWritten = true; } } @@ -2232,33 +2948,38 @@ bool UkEngine::lastWordIsNonVn() const { return false; switch (m_buffer[m_current].form) { - case vnw_nonVn: return true; - case vnw_empty: - case vnw_c: return false; - case vnw_v: - case vnw_cv: return !VSeqList[m_buffer[m_current].vseq].complete; - case vnw_vc: - case vnw_cvc: { - int vIndex = m_current - m_buffer[m_current].vOffset; - VowelSeq vs = m_buffer[vIndex].vseq; - if (!VSeqList[vs].complete) - return true; - ConSeq cs = m_buffer[m_current].cseq; - ConSeq c1 = cs_nil; - if (m_buffer[m_current].c1Offset != -1) - c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; + case vnw_nonVn: + return true; + case vnw_empty: + case vnw_c: + return false; + case vnw_v: + case vnw_cv: + return !VSeqList[m_buffer[m_current].vseq].complete; + case vnw_vc: + case vnw_cvc: { + int vIndex = m_current - m_buffer[m_current].vOffset; + VowelSeq vs = m_buffer[vIndex].vseq; + if (!VSeqList[vs].complete) + return true; + ConSeq cs = m_buffer[m_current].cseq; + ConSeq c1 = cs_nil; + if (m_buffer[m_current].c1Offset != -1) + c1 = m_buffer[m_current - m_buffer[m_current].c1Offset].cseq; - if (!isValidCVC(c1, vs, cs)) { - return true; - } + if (!isValidCVC(c1, vs, cs)) { + return true; + } - int tonePos = (vIndex - VSeqList[vs].len + 1) + getTonePosition(vs, false); - int tone = m_buffer[tonePos].tone; - if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && (tone == 2 || tone == 3 || tone == 4)) { - return true; - } + int tonePos = + (vIndex - VSeqList[vs].len + 1) + getTonePosition(vs, false); + int tone = m_buffer[tonePos].tone; + if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) && + (tone == 2 || tone == 3 || tone == 4)) { + return true; } } + } return false; } diff --git a/unikey/core/ukengine.h b/unikey/core/ukengine.h index d218e5ce..c79a1304 100644 --- a/unikey/core/ukengine.h +++ b/unikey/core/ukengine.h @@ -16,42 +16,33 @@ // This is a shared object among processes, do not put any pointer in it struct UkSharedMem { // states - bool vietKey; + bool vietKey; - UnikeyOptions options; + UnikeyOptions options; UkInputProcessor input; - bool usrKeyMapLoaded; - int usrKeyMap[256]; - int charsetId; + bool usrKeyMapLoaded; + int usrKeyMap[256]; + int charsetId; - CMacroTable macStore; + CMacroTable macStore; }; #define MAX_UK_ENGINE 128 -enum VnWordForm { - vnw_nonVn, - vnw_empty, - vnw_c, - vnw_v, - vnw_cv, - vnw_vc, - vnw_cvc -}; +enum VnWordForm { vnw_nonVn, vnw_empty, vnw_c, vnw_v, vnw_cv, vnw_vc, vnw_cvc }; -typedef std::function CheckKeyboardCaseCb; +typedef std::function + CheckKeyboardCaseCb; struct KeyBufEntry { UkKeyEvent ev; - bool converted; + bool converted; }; class UkEngine { - public: +public: UkEngine(); - void setCtrlInfo(UkSharedMem* p) { - m_pCtrl = p; - } + void setCtrlInfo(UkSharedMem *p) { m_pCtrl = p; } void setCheckKbCaseFunc(CheckKeyboardCaseCb pFunc) { m_keyCheckFunc = pFunc; @@ -59,65 +50,69 @@ class UkEngine { bool atWordBeginning() const; - int process(unsigned int keyCode, int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType); + int process(unsigned int keyCode, int &backs, unsigned char *outBuf, + int &outSize, UkOutputType &outType); // just pass through without filtering void pass(int keyCode); // rebuild preedit from surrounding char - void rebuildChar(VnLexiName ch, int& backs, unsigned char* outBuf, int& outSize); + void rebuildChar(VnLexiName ch, int &backs, unsigned char *outBuf, + int &outSize); void setSingleMode(); - int processBackspace(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType); + int processBackspace(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType); void reset(); - int restoreKeyStrokes(int& backs, unsigned char* outBuf, int& outSize, UkOutputType& outType); + int restoreKeyStrokes(int &backs, unsigned char *outBuf, int &outSize, + UkOutputType &outType); // following methods must be public just to enable the use of pointers to // them they should not be called from outside. - int processTone(UkKeyEvent& ev); - int processRoof(UkKeyEvent& ev); - int processHook(UkKeyEvent& ev); - int processAppend(UkKeyEvent& ev); - int appendVowel(UkKeyEvent& ev); - int appendConsonnant(UkKeyEvent& ev); - int processDd(UkKeyEvent& ev); - int processMapChar(UkKeyEvent& ev); - int processTelexW(UkKeyEvent& ev); - int processEscChar(UkKeyEvent& ev); - - protected: - static bool m_classInit; + int processTone(UkKeyEvent &ev); + int processRoof(UkKeyEvent &ev); + int processHook(UkKeyEvent &ev); + int processAppend(UkKeyEvent &ev); + int appendVowel(UkKeyEvent &ev); + int appendConsonnant(UkKeyEvent &ev); + int processDd(UkKeyEvent &ev); + int processMapChar(UkKeyEvent &ev); + int processTelexW(UkKeyEvent &ev); + int processEscChar(UkKeyEvent &ev); + +protected: + static bool m_classInit; CheckKeyboardCaseCb m_keyCheckFunc; - UkSharedMem* m_pCtrl; + UkSharedMem *m_pCtrl; - int m_changePos; - int m_backs; - int m_bufSize; - int m_current; - int m_singleMode; + int m_changePos; + int m_backs; + int m_bufSize; + int m_current; + int m_singleMode; - int m_keyBufSize; + int m_keyBufSize; // unsigned int m_keyStrokes[MAX_UK_ENGINE]; KeyBufEntry m_keyStrokes[MAX_UK_ENGINE]; - int m_keyCurrent; - bool m_toEscape; + int m_keyCurrent; + bool m_toEscape; // variables valid in one session - unsigned char* m_pOutBuf; - int* m_pOutSize; - bool m_outputWritten; - bool m_reverted; - bool m_keyRestored; - bool m_keyRestoring; - UkOutputType m_outType; + unsigned char *m_pOutBuf; + int *m_pOutSize; + bool m_outputWritten; + bool m_reverted; + bool m_keyRestored; + bool m_keyRestoring; + UkOutputType m_outType; struct WordInfo { // info for word ending at this position VnWordForm form; - int c1Offset, vOffset, c2Offset; + int c1Offset, vOffset, c2Offset; union { VowelSeq vseq; - ConSeq cseq; + ConSeq cseq; }; // info for current symbol @@ -125,23 +120,23 @@ class UkEngine { // canonical symbol, after caps, tone are removed // for non-Vn, vnSym == -1 VnLexiName vnSym; - int keyCode; + int keyCode; }; WordInfo m_buffer[MAX_UK_ENGINE]; - int processHookWithUO(UkKeyEvent& ev); - int macroMatch(UkKeyEvent& ev); - void markChange(int pos); - void prepareBuffer(); // make sure we have a least 10 entries available - int writeOutput(unsigned char* outBuf, int& outSize); + int processHookWithUO(UkKeyEvent &ev); + int macroMatch(UkKeyEvent &ev); + void markChange(int pos); + void prepareBuffer(); // make sure we have a least 10 entries available + int writeOutput(unsigned char *outBuf, int &outSize); // int getSeqLength(int first, int last); - int getSeqSteps(int first, int last) const; - int getTonePosition(VowelSeq vs, bool terminated) const; + int getSeqSteps(int first, int last) const; + int getTonePosition(VowelSeq vs, bool terminated) const; void resetKeyBuf(); - int checkEscapeVIQR(UkKeyEvent& ev); - int processNoSpellCheck(UkKeyEvent& ev); - int processWordEnd(UkKeyEvent& ev); + int checkEscapeVIQR(UkKeyEvent &ev); + int processNoSpellCheck(UkKeyEvent &ev); + int processWordEnd(UkKeyEvent &ev); void synchKeyStrokeBuffer(); bool lastWordHasVnMark() const; bool lastWordIsNonVn() const; diff --git a/unikey/core/unikeyinputcontext.cpp b/unikey/core/unikeyinputcontext.cpp index 894bee10..2043bf17 100644 --- a/unikey/core/unikeyinputcontext.cpp +++ b/unikey/core/unikeyinputcontext.cpp @@ -15,21 +15,22 @@ using namespace std; //-------------------------------------------- -void CreateDefaultUnikeyOptions(UnikeyOptions* pOpt) { - pOpt->freeMarking = 1; - pOpt->modernStyle = 0; - pOpt->macroEnabled = 0; +void CreateDefaultUnikeyOptions(UnikeyOptions *pOpt) { + pOpt->freeMarking = 1; + pOpt->modernStyle = 0; + pOpt->macroEnabled = 0; pOpt->useUnicodeClipboard = 0; - pOpt->alwaysMacro = 0; - pOpt->spellCheckEnabled = 1; - pOpt->autoNonVnRestore = 0; + pOpt->alwaysMacro = 0; + pOpt->spellCheckEnabled = 1; + pOpt->autoNonVnRestore = 0; } -UnikeyInputMethod::UnikeyInputMethod() : sharedMem_(std::make_unique()) { +UnikeyInputMethod::UnikeyInputMethod() + : sharedMem_(std::make_unique()) { SetupUnikeyEngine(); sharedMem_->input.init(); sharedMem_->macStore.init(); - sharedMem_->vietKey = true; + sharedMem_->vietKey = true; sharedMem_->usrKeyMapLoaded = false; setInputMethod(UkTelex); setOutputCharset(CONV_CHARSET_XUTF8); @@ -38,7 +39,8 @@ UnikeyInputMethod::UnikeyInputMethod() : sharedMem_(std::make_uniqueinput.setIM(im); } else if (im == UkUsrIM && sharedMem_->usrKeyMapLoaded) { // cout << "Switched to user mode\n"; //DEBUG @@ -54,31 +56,32 @@ void UnikeyInputMethod::setOutputCharset(int charset) { } //-------------------------------------------- -void UnikeyInputMethod::setOptions(UnikeyOptions* pOpt) { - sharedMem_->options.freeMarking = pOpt->freeMarking; - sharedMem_->options.modernStyle = pOpt->modernStyle; - sharedMem_->options.macroEnabled = pOpt->macroEnabled; +void UnikeyInputMethod::setOptions(UnikeyOptions *pOpt) { + sharedMem_->options.freeMarking = pOpt->freeMarking; + sharedMem_->options.modernStyle = pOpt->modernStyle; + sharedMem_->options.macroEnabled = pOpt->macroEnabled; sharedMem_->options.useUnicodeClipboard = pOpt->useUnicodeClipboard; - sharedMem_->options.alwaysMacro = pOpt->alwaysMacro; - sharedMem_->options.spellCheckEnabled = pOpt->spellCheckEnabled; - sharedMem_->options.autoNonVnRestore = pOpt->autoNonVnRestore; + sharedMem_->options.alwaysMacro = pOpt->alwaysMacro; + sharedMem_->options.spellCheckEnabled = pOpt->spellCheckEnabled; + sharedMem_->options.autoNonVnRestore = pOpt->autoNonVnRestore; } //-------------------------------------------- void UnikeyInputContext::setCapsState(int shiftPressed, int CapsLockOn) { // UnikeyCapsAll = (shiftPressed && !CapsLockOn) || (!shiftPressed && // CapsLockOn); - capsLockOn_ = CapsLockOn; + capsLockOn_ = CapsLockOn; shiftPressed_ = shiftPressed; } //-------------------------------------------- -UnikeyInputContext::UnikeyInputContext(UnikeyInputMethod* im) { - conn_ = im->connect([this]() { engine_.reset(); }); +UnikeyInputContext::UnikeyInputContext(UnikeyInputMethod *im) { + conn_ = + im->connect([this]() { engine_.reset(); }); engine_.setCtrlInfo(im->sharedMem()); - engine_.setCheckKbCaseFunc([this](int* pShiftPressed, int* pCapsLockOn) { + engine_.setCheckKbCaseFunc([this](int *pShiftPressed, int *pCapsLockOn) { *pShiftPressed = shiftPressed_; - *pCapsLockOn = capsLockOn_; + *pCapsLockOn = capsLockOn_; }); } @@ -94,7 +97,7 @@ void UnikeyInputContext::filter(unsigned int ch) { //-------------------------------------------- void UnikeyInputContext::putChar(unsigned int ch) { engine_.pass(ch); - bufChars_ = 0; + bufChars_ = 0; backspaces_ = 0; } @@ -105,9 +108,7 @@ void UnikeyInputContext::rebuildChar(VnLexiName ch) { } //-------------------------------------------- -void UnikeyInputContext::resetBuf() { - engine_.reset(); -} +void UnikeyInputContext::resetBuf() { engine_.reset(); } //-------------------------------------------- void UnikeyInputContext::backspacePress() { diff --git a/unikey/core/unikeyinputcontext.h b/unikey/core/unikeyinputcontext.h index 1416f31e..bb9f02da 100644 --- a/unikey/core/unikeyinputcontext.h +++ b/unikey/core/unikeyinputcontext.h @@ -13,7 +13,7 @@ #include class UnikeyInputMethod : public fcitx::ConnectableObject { - public: +public: UnikeyInputMethod(); // set input method @@ -23,27 +23,25 @@ class UnikeyInputMethod : public fcitx::ConnectableObject { void setOutputCharset(int charset); // set extra options - void setOptions(UnikeyOptions* pOpt); + void setOptions(UnikeyOptions *pOpt); //-------------------------------------------- - int loadMacroTable(const char* fileName) { + int loadMacroTable(const char *fileName) { return sharedMem_->macStore.loadFromFile(fileName); } - UkSharedMem* sharedMem() { - return sharedMem_.get(); - } + UkSharedMem *sharedMem() { return sharedMem_.get(); } FCITX_DECLARE_SIGNAL(UnikeyInputMethod, Reset, void()); - private: +private: FCITX_DEFINE_SIGNAL(UnikeyInputMethod, Reset); std::unique_ptr sharedMem_; }; class UnikeyInputContext { - public: - UnikeyInputContext(UnikeyInputMethod* im); +public: + UnikeyInputContext(UnikeyInputMethod *im); ~UnikeyInputContext(); // call this to reset Unikey's state when focus, context is changed or @@ -69,27 +67,21 @@ class UnikeyInputContext { bool isAtWordBeginning() const; - int backspaces() const { - return backspaces_; - } - int bufChars() const { - return bufChars_; - } - const unsigned char* buf() const { - return buf_; - } + int backspaces() const { return backspaces_; } + int bufChars() const { return bufChars_; } + const unsigned char *buf() const { return buf_; } - private: +private: fcitx::ScopedConnection conn_; - unsigned char buf_[1024]; - int backspaces_ = 0; - int bufChars_; - UkOutputType output_; - UkEngine engine_; + unsigned char buf_[1024]; + int backspaces_ = 0; + int bufChars_; + UkOutputType output_; + UkEngine engine_; - int capsLockOn_ = 0; - int shiftPressed_ = 0; + int capsLockOn_ = 0; + int shiftPressed_ = 0; }; #endif // _UNIKEY_UNIKEYINPUTCONTEXT_H_ diff --git a/unikey/core/usrkeymap.cpp b/unikey/core/usrkeymap.cpp index 54e2afce..3475e050 100644 --- a/unikey/core/usrkeymap.cpp +++ b/unikey/core/usrkeymap.cpp @@ -18,50 +18,56 @@ namespace { - constexpr char OPT_COMMENT_CHAR = ';'; - - struct UkEventLabelPair { - char label[32]; - int ev; - }; - - const char* UkKeyMapHeader = "; This is UniKey user-defined key mapping file, " - "generated from UniKey (Fcitx 5)\n\n"; - - constexpr UkKeyEvName lexi(VnLexiName v) { - return static_cast(static_cast(vneCount) + static_cast(v)); - } - - static const UkEventLabelPair UkEvLabelList[] = { - {"Tone0", vneTone0}, {"Tone1", vneTone1}, {"Tone2", vneTone2}, {"Tone3", vneTone3}, {"Tone4", vneTone4}, {"Tone5", vneTone5}, {"Roof-All", vneRoofAll}, - {"Roof-A", vneRoof_a}, {"Roof-E", vneRoof_e}, {"Roof-O", vneRoof_o}, {"Hook-Bowl", vneHookAll}, {"Hook-UO", vneHook_uo}, {"Hook-U", vneHook_u}, {"Hook-O", vneHook_o}, - {"Bowl", vneBowl}, {"D-Mark", vneDd}, {"Telex-W", vne_telex_w}, {"Escape", vneEscChar}, - - {"DD", lexi(vnl_DD)}, {"dd", lexi(vnl_dd)}, {"A^", lexi(vnl_Ar)}, {"a^", lexi(vnl_ar)}, {"A(", lexi(vnl_Ab)}, {"a(", lexi(vnl_ab)}, {"E^", lexi(vnl_Er)}, - {"e^", lexi(vnl_er)}, {"O^", lexi(vnl_Or)}, {"o^", lexi(vnl_or)}, {"O+", lexi(vnl_Oh)}, {"o+", lexi(vnl_oh)}, {"U+", lexi(vnl_Uh)}, {"u+", lexi(vnl_uh)}, - }; - - constexpr auto UkEvLabelCount = FCITX_ARRAY_SIZE(UkEvLabelList); - - //------------------------------------------- - void initKeyMap(int keyMap[256]) { - unsigned int c; - for (c = 0; c < 256; c++) - keyMap[c] = vneNormal; - } +constexpr char OPT_COMMENT_CHAR = ';'; + +struct UkEventLabelPair { + char label[32]; + int ev; +}; + +const char *UkKeyMapHeader = "; This is UniKey user-defined key mapping file, " + "generated from UniKey (Fcitx 5)\n\n"; + +constexpr UkEventLabelPair UkEvLabelList[] = { + {"Tone0", vneTone0}, {"Tone1", vneTone1}, + {"Tone2", vneTone2}, {"Tone3", vneTone3}, + {"Tone4", vneTone4}, {"Tone5", vneTone5}, + {"Roof-All", vneRoofAll}, {"Roof-A", vneRoof_a}, + {"Roof-E", vneRoof_e}, {"Roof-O", vneRoof_o}, + {"Hook-Bowl", vneHookAll}, {"Hook-UO", vneHook_uo}, + {"Hook-U", vneHook_u}, {"Hook-O", vneHook_o}, + {"Bowl", vneBowl}, {"D-Mark", vneDd}, + {"Telex-W", vne_telex_w}, {"Escape", vneEscChar}, + {"DD", lexi(vnl_DD)}, {"dd", lexi(vnl_dd)}, + {"A^", lexi(vnl_Ar)}, {"a^", lexi(vnl_ar)}, + {"A(", lexi(vnl_Ab)}, {"a(", lexi(vnl_ab)}, + {"E^", lexi(vnl_Er)}, {"e^", lexi(vnl_er)}, + {"O^", lexi(vnl_Or)}, {"o^", lexi(vnl_or)}, + {"O+", lexi(vnl_Oh)}, {"o+", lexi(vnl_oh)}, + {"U+", lexi(vnl_Uh)}, {"u+", lexi(vnl_uh)}}; + +constexpr auto UkEvLabelCount = FCITX_ARRAY_SIZE(UkEvLabelList); + +//------------------------------------------- +void initKeyMap(int keyMap[256]) { + unsigned int c; + for (c = 0; c < 256; c++) + keyMap[c] = vneNormal; +} - int getLabelIndex(int event) { - for (size_t i = 0; i < UkEvLabelCount; i++) { - if (UkEvLabelList[i].ev == event) - return i; - } - return -1; +int getLabelIndex(int event) { + for (size_t i = 0; i < UkEvLabelCount; i++) { + if (UkEvLabelList[i].ev == event) + return i; } + return -1; +} } // namespace //-------------------------------------------------- -static bool parseNameValue(std::string_view line, std::string_view* name, std::string_view* value) { +static bool parseNameValue(std::string_view line, std::string_view *name, + std::string_view *value) { if (line.empty()) { return false; } @@ -85,7 +91,7 @@ static bool parseNameValue(std::string_view line, std::string_view* name, std::s return false; } - *name = k; + *name = k; *value = v; return true; } @@ -94,7 +100,7 @@ static bool parseNameValue(std::string_view line, std::string_view* name, std::s DllExport void UkLoadKeyMap(int fd, int keyMap[256]) { std::vector orderMap = UkLoadKeyOrderMap(fd); initKeyMap(keyMap); - for (const auto& item : orderMap) { + for (const auto &item : orderMap) { keyMap[item.key] = item.action; if (item.action < vneCount) { keyMap[tolower(item.key)] = item.action; @@ -105,14 +111,14 @@ DllExport void UkLoadKeyMap(int fd, int keyMap[256]) { //------------------------------------------------------------------ DllExport std::vector UkLoadKeyOrderMap(int fd) { size_t lineCount = 0; - int keyMap[256]; + int keyMap[256]; initKeyMap(keyMap); std::vector pMap; - fcitx::IFDStreamBuf buf(fd); - std::istream in(&buf); - std::string line; + fcitx::IFDStreamBuf buf(fd); + std::istream in(&buf); + std::string line; while (std::getline(in, line)) { lineCount++; auto text = fcitx::stringutils::trimView(line); @@ -122,7 +128,8 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { std::string_view name, value; if (parseNameValue(text, &name, &value)) { if (name.size() != 1) { - FCITX_ERROR() << "Error in user key layout, line " << lineCount << ": key name is not a single character"; + FCITX_ERROR() << "Error in user key layout, line " << lineCount + << ": key name is not a single character"; continue; } size_t i = 0; @@ -132,7 +139,8 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { } } if (i == UkEvLabelCount) { - FCITX_ERROR() << "Error in user key layout, line " << lineCount << ": command not found"; + FCITX_ERROR() << "Error in user key layout, line " << lineCount + << ": command not found"; continue; } @@ -147,7 +155,7 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { UkKeyMapping newPair; newPair.action = UkEvLabelList[i].ev; if (keyMap[c] < vneCount) { - newPair.key = toupper(c); + newPair.key = toupper(c); keyMap[toupper(c)] = UkEvLabelList[i].ev; } else { newPair.key = c; @@ -158,11 +166,12 @@ DllExport std::vector UkLoadKeyOrderMap(int fd) { return pMap; } -DllExport void UkStoreKeyOrderMap(FILE* f, const std::vector& pMap) { +DllExport void UkStoreKeyOrderMap(FILE *f, + const std::vector &pMap) { int labelIndex; fputs(UkKeyMapHeader, f); - for (const auto& item : pMap) { + for (const auto &item : pMap) { labelIndex = getLabelIndex(item.action); if (labelIndex != -1) { fprintf(f, "%c = %s\n", item.key, UkEvLabelList[labelIndex].label); diff --git a/unikey/core/usrkeymap.h b/unikey/core/usrkeymap.h index 7073aa67..76098527 100644 --- a/unikey/core/usrkeymap.h +++ b/unikey/core/usrkeymap.h @@ -13,6 +13,7 @@ DllInterface void UkLoadKeyMap(int fd, int keyMap[256]); DllInterface std::vector UkLoadKeyOrderMap(int fd); -DllInterface void UkStoreKeyOrderMap(FILE* f, const std::vector& pMap); +DllInterface void UkStoreKeyOrderMap(FILE *f, + const std::vector &pMap); #endif diff --git a/unikey/core/vnconv.h b/unikey/core/vnconv.h index e3030ab6..f51a98a5 100644 --- a/unikey/core/vnconv.h +++ b/unikey/core/vnconv.h @@ -21,51 +21,57 @@ #define DllImport #endif -#define CONV_CHARSET_UNICODE 0 -#define CONV_CHARSET_UNIUTF8 1 -#define CONV_CHARSET_UNIREF 2 //&#D; -#define CONV_CHARSET_UNIREF_HEX 3 +#define CONV_CHARSET_UNICODE 0 +#define CONV_CHARSET_UNIUTF8 1 +#define CONV_CHARSET_UNIREF 2 //&#D; +#define CONV_CHARSET_UNIREF_HEX 3 #define CONV_CHARSET_UNIDECOMPOSED 4 -#define CONV_CHARSET_WINCP1258 5 -#define CONV_CHARSET_UNI_CSTRING 6 -#define CONV_CHARSET_VNSTANDARD 7 +#define CONV_CHARSET_WINCP1258 5 +#define CONV_CHARSET_UNI_CSTRING 6 +#define CONV_CHARSET_VNSTANDARD 7 -#define CONV_CHARSET_VIQR 10 +#define CONV_CHARSET_VIQR 10 #define CONV_CHARSET_UTF8VIQR 11 -#define CONV_CHARSET_XUTF8 12 +#define CONV_CHARSET_XUTF8 12 -#define CONV_CHARSET_TCVN3 20 -#define CONV_CHARSET_VPS 21 -#define CONV_CHARSET_VISCII 22 -#define CONV_CHARSET_BKHCM1 23 +#define CONV_CHARSET_TCVN3 20 +#define CONV_CHARSET_VPS 21 +#define CONV_CHARSET_VISCII 22 +#define CONV_CHARSET_BKHCM1 23 #define CONV_CHARSET_VIETWAREF 24 -#define CONV_CHARSET_ISC 25 +#define CONV_CHARSET_ISC 25 -#define CONV_CHARSET_VNIWIN 40 -#define CONV_CHARSET_BKHCM2 41 +#define CONV_CHARSET_VNIWIN 40 +#define CONV_CHARSET_BKHCM2 41 #define CONV_CHARSET_VIETWAREX 42 -#define CONV_CHARSET_VNIMAC 43 +#define CONV_CHARSET_VNIMAC 43 #define CONV_TOTAL_SINGLE_CHARSETS 6 #define CONV_TOTAL_DOUBLE_CHARSETS 4 -#define IS_SINGLE_BYTE_CHARSET(x) (x >= CONV_CHARSET_TCVN3 && x < CONV_CHARSET_TCVN3 + CONV_TOTAL_SINGLE_CHARSETS) -#define IS_DOUBLE_BYTE_CHARSET(x) (x >= CONV_CHARSET_VNIWIN && x < CONV_CHARSET_VNIWIN + CONV_TOTAL_DOUBLE_CHARSETS) +#define IS_SINGLE_BYTE_CHARSET(x) \ + (x >= CONV_CHARSET_TCVN3 && \ + x < CONV_CHARSET_TCVN3 + CONV_TOTAL_SINGLE_CHARSETS) +#define IS_DOUBLE_BYTE_CHARSET(x) \ + (x >= CONV_CHARSET_VNIWIN && \ + x < CONV_CHARSET_VNIWIN + CONV_TOTAL_DOUBLE_CHARSETS) typedef unsigned char UKBYTE; #if defined(__cplusplus) extern "C" { #endif -DllInterface int VnConvert(int inCharset, int outCharset, UKBYTE* input, UKBYTE* output, int* pInLen, int* pMaxOutLen); +DllInterface int VnConvert(int inCharset, int outCharset, UKBYTE *input, + UKBYTE *output, int *pInLen, int *pMaxOutLen); -DllInterface int VnFileConvert(int inCharset, int outCharset, const char* inFile, const char* outFile); +DllInterface int VnFileConvert(int inCharset, int outCharset, + const char *inFile, const char *outFile); #if defined(__cplusplus) } #endif -DllInterface const char* VnConvErrMsg(int errCode); +DllInterface const char *VnConvErrMsg(int errCode); enum VnConvError { VNCONV_NO_ERROR, @@ -81,8 +87,8 @@ enum VnConvError { typedef struct _CharsetNameId CharsetNameId; struct _CharsetNameId { - const char* name; - int id; + const char *name; + int id; }; typedef struct _VnConvOptions VnConvOptions; @@ -96,8 +102,8 @@ struct _VnConvOptions { int smartViqr; }; -DllInterface void VnConvSetOptions(VnConvOptions* pOptions); -DllInterface void VnConvGetOptions(VnConvOptions* pOptions); -DllInterface void VnConvResetOptions(VnConvOptions* pOptions); +DllInterface void VnConvSetOptions(VnConvOptions *pOptions); +DllInterface void VnConvGetOptions(VnConvOptions *pOptions); +DllInterface void VnConvResetOptions(VnConvOptions *pOptions); #endif From a0f03c0323226512e2ae65a3598eb9a60499f512 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Wed, 13 May 2026 15:59:06 +0700 Subject: [PATCH 23/42] asm --- src/CMakeLists.txt | 18 +++ src/app_quirks.h | 4 +- src/lotus-engine.cpp | 19 ++- src/lotus-state.cpp | 41 +----- src/lotus-utils-avx512.S | 307 +++++++++++++++++++++++++++++++++++++++ src/lotus-utils.cpp | 11 +- src/lotus-utils.h | 15 ++ 7 files changed, 366 insertions(+), 49 deletions(-) create mode 100644 src/lotus-utils-avx512.S diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ff4d23b..4f04df5f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ +option(LOTUS_ENABLE_AVX512 "Enable AVX-512 acceleration for lotus-utils" ON) set(fcitx_lotus_sources lotus.cpp lotus-engine.cpp @@ -8,6 +9,11 @@ set(fcitx_lotus_sources emoji.cpp ) +if (LOTUS_ENABLE_AVX512) + list(APPEND fcitx_lotus_sources lotus-utils-avx512.S) +endif() +enable_language(ASM) +set_source_files_properties(lotus-utils-avx512.S PROPERTIES LANGUAGE ASM) list(APPEND fcitx_lotus_sources lotus-unikey-backend.cpp) add_library(lotus MODULE ${fcitx_lotus_sources}) @@ -30,6 +36,18 @@ target_include_directories(lotus PRIVATE target_compile_definitions(lotus PRIVATE FCITX5_LOTUS_SETTINGS_PATH=\"${CMAKE_INSTALL_FULL_BINDIR}/fcitx5-lotus-settings\" ) +if (LOTUS_ENABLE_AVX512) + target_compile_definitions(lotus PRIVATE LOTUS_ENABLE_AVX512=1) + + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(lotus PRIVATE + -O3 + -mavx512f + -mavx512bw + -mbmi + ) + endif() +endif() install(TARGETS lotus DESTINATION "${CMAKE_INSTALL_LIBDIR}/fcitx5") fcitx5_translate_desktop_file(lotus.conf.in lotus.conf) diff --git a/src/app_quirks.h b/src/app_quirks.h index 9737c0b7..446f736d 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -20,8 +20,8 @@ * * Chromium-based browsers that need special handling for text replacement. */ -inline constexpr std::array ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", - "cromite", "helium", "thorium", "slimjet", "yandex", "vesktop"}; +inline constexpr std::array ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", + "cromite", "helium", "thorium", "slimjet", "yandex", "vesktop", "obsidian"}; /** * @brief List of application names have goood support surrowding text diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 74ba9227..156a3eee 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -399,14 +399,25 @@ namespace fcitx { state->waitAck_ = false; if (*config_.fixUinputWithAck) { if (targetMode == LotusMode::Uinput || targetMode == LotusMode::UinputWine || targetMode == LotusMode::Smooth) { -#if __cplusplus >= 202002L +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + tolower_avx512(appName.data(), appName.size()); +#elif __cplusplus >= 202002L std::ranges::transform(appName, appName.begin(), [](unsigned char c) { return std::tolower(c); }); #else std::transform(appName.begin(), appName.end(), appName.begin(), ::tolower); +#endif +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + auto contains = [&](std::string_view s) { + return strfind_avx512(appName.data(), appName.size(), s.data(), s.size()) != static_cast(-1); + }; +#else + auto contains = [&](std::string_view s) { + return appName.find(s) != std::string::npos; + }; #endif for (const auto& ackApp : ack_apps) { - if (appName.find(ackApp) != std::string::npos) { + if (contains(ackApp)) { if (is_dbus) { state->waitAck_ = true; LOTUS_INFO(std::string(ackApp) + " detected, waiting for ack"); @@ -416,14 +427,14 @@ namespace fcitx { } } for (const auto& _App : surrtp_apps) { - if (appName.find(_App) != std::string::npos) { + if (contains(_App)) { LOTUS_INFO(std::string(_App) + " support surr"); state->surrtp = true; break; } } for (const auto& _term : terminalm) { - if (appName.find(_term) != std::string::npos) { + if (contains(_term)) { LOTUS_INFO(std::string(_term) + " is terminal"); state->isTerm = true; break; diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index cf5b39ad..b9dd3c3f 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -49,27 +49,6 @@ namespace fcitx { return r; } - // Word-at-a-time high-byte scan (glibc / Linux kernel byte-at-a-time.h technique). - // Reads 8 bytes per iteration; testq checks all 8 in one instruction. - static inline bool hasHighByte(const std::string& s) { - static constexpr uint64_t kHi = 0x8080808080808080ULL; - const uint8_t* p = reinterpret_cast(s.data()); - size_t n = s.size(); - bool r = false; - for (; n >= 8 && !r; p += 8, n -= 8) { - uint64_t w; - __builtin_memcpy(&w, p, 8); - asm("testq %1, %2\n\t" - "setne %0" - : "=r"(r) - : "r"(w), "r"(kHi) - : "cc"); - } - for (; n && !r; --n) - r = (*p++ & 0x80) != 0; - return r; - } - inline void update_max(std::atomic& value, uint32_t target) { uint32_t current = value.load(std::memory_order_acquire); @@ -497,24 +476,6 @@ namespace fcitx { return false; // Allow intermediate backspaces to reach the app to clear autofill/old text. } is_deleting_.store(false); - /* - replacement_start_ms_.store(0, std::memory_order_release); - replacement_thread_id_.store(0, std::memory_order_release); - int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); - int64_t wait_ms = static_cast(sleepTime) - elapsed_ms; - if (wait_ms > 0) - std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); - { - const unsigned int expected_cursor = static_cast(realtextLen.load(std::memory_order_acquire)); - const int max_retries = waitAck_ ? 5 : 1; - for (int retry = 0; retry < max_retries; ++retry) { - const auto& surr = ic_->surroundingText(); - if (surr.isValid() && surr.cursor() == expected_cursor) - break; - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - } - */ replacement_start_ms_.store(0, std::memory_order_release); replacement_thread_id_.store(0, std::memory_order_release); int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); @@ -547,7 +508,7 @@ namespace fcitx { if (realMode == LotusMode::UinputWine) --expected_backspaces_; // Use deleteSurroundingText for apps that support it for smooth typing - bool test_flags = true; // use for testing only :v + bool test_flags = false; // use for testing only :v if (surrtp) LOTUS_INFO("surrtp"); if ((test_flags || surrtp) // Lmfao, only this work :> diff --git a/src/lotus-utils-avx512.S b/src/lotus-utils-avx512.S new file mode 100644 index 00000000..eb953d09 --- /dev/null +++ b/src/lotus-utils-avx512.S @@ -0,0 +1,307 @@ +.intel_syntax noprefix +.text + +.globl compare_split_avx512 +.type compare_split_avx512, @function + +# SysV ABI: +# rdi = A +# rsi = B +# rdx = lenA +# rcx = lenB (unused) +# r8 = dummy (unused) +# +# return: rax = first differing index + +compare_split_avx512: + xor rax, rax + +.Lloop: + # check if we can safely load 64 bytes + mov r10, rax + add r10, 64 + cmp r10, rdx + ja .Ltail + + # load 64 bytes from both strings + vmovdqu64 zmm0, ZMMWORD PTR [rdi + rax] + vmovdqu64 zmm1, ZMMWORD PTR [rsi + rax] + + # compare bytes + vpcmpeqb k1, zmm0, zmm1 + kortestq k1, k1 + jnc .Ldiff + + add rax, 64 + jmp .Lloop + +.Ldiff: + # k1 = equal bytes + # invert to get mismatch mask + knotq k1, k1 + + # move mask to GPR + kmovq r11, k1 + + # find first mismatch byte + tzcnt r11, r11 + + add rax, r11 + ret + +.Ltail: + cmp rax, rdx + jae .Ldone + +.Lbyte_loop: + mov r10b, BYTE PTR [rdi + rax] + cmp r10b, BYTE PTR [rsi + rax] + jne .Ldone + + inc rax + cmp rax, rdx + jb .Lbyte_loop + +.Ldone: + ret + +.globl utf8_length_avx512 +.type utf8_length_avx512, @function + +# rdi = str, rsi = len +# returns rax = codepoint count (non-continuation bytes) +utf8_length_avx512: + xor rax, rax + xor r10, r10 + mov r11d, 0xC0 + vpbroadcastb zmm2, r11d + mov r11d, 0x80 + vpbroadcastb zmm3, r11d + +.Lu8_loop: + lea r11, [r10 + 64] + cmp r11, rsi + ja .Lu8_tail + vmovdqu8 zmm0, [rdi + r10] + vpandq zmm1, zmm0, zmm2 + vpcmpeqb k1, zmm1, zmm3 + kmovq r11, k1 + not r11 + popcnt r11, r11 + add rax, r11 + add r10, 64 + jmp .Lu8_loop + +.Lu8_tail: + cmp r10, rsi + jae .Lu8_done +.Lu8_byte: + movzx r11d, BYTE PTR [rdi + r10] + and r11d, 0xC0 + cmp r11d, 0x80 + je .Lu8_skip + inc rax +.Lu8_skip: + inc r10 + cmp r10, rsi + jb .Lu8_byte +.Lu8_done: + ret + + +.globl find_char_avx512 +.type find_char_avx512, @function + +# rdi = str, rsi = len, rdx = start, rcx = ch +# returns rax = index or (size_t)-1 +find_char_avx512: + mov rax, rdx + vpbroadcastb zmm1, ecx + +.Lfc_loop: + lea r10, [rax + 64] + cmp r10, rsi + ja .Lfc_tail + vmovdqu8 zmm0, [rdi + rax] + vpcmpeqb k1, zmm0, zmm1 + kmovq r10, k1 + test r10, r10 + jnz .Lfc_found + add rax, 64 + jmp .Lfc_loop + +.Lfc_found: + tzcnt r10, r10 + add rax, r10 + cmp rax, rsi + jb .Lfc_done + mov rax, -1 +.Lfc_done: + ret + +.Lfc_tail: + cmp rax, rsi + jae .Lfc_notfound +.Lfc_byte: + movzx r10d, BYTE PTR [rdi + rax] + cmp r10b, cl + je .Lfc_done + inc rax + cmp rax, rsi + jb .Lfc_byte +.Lfc_notfound: + mov rax, -1 + ret + +.globl tolower_avx512 +.type tolower_avx512, @function + +# rdi = str (in-place), rsi = len +tolower_avx512: + test rsi, rsi + jz .Ltl_done + mov r8d, 'A' + vpbroadcastb zmm3, r8d + mov r8d, 'Z' + vpbroadcastb zmm4, r8d + mov r8d, 0x20 + vpbroadcastb zmm5, r8d + xor r10, r10 + +.Ltl_loop: + lea r11, [r10 + 64] + cmp r11, rsi + ja .Ltl_tail + vmovdqu8 zmm0, [rdi + r10] + vpcmpub k1, zmm0, zmm3, 5 + vpcmpub k2, zmm0, zmm4, 2 + kandq k3, k1, k2 + vpaddb zmm1, zmm0, zmm5 + vmovdqu8 zmm0{k3}, zmm1 + vmovdqu8 [rdi + r10], zmm0 + add r10, 64 + jmp .Ltl_loop + +.Ltl_tail: + cmp r10, rsi + jae .Ltl_done +.Ltl_byte: + movzx r11d, BYTE PTR [rdi + r10] + cmp r11d, 'A' + jb .Ltl_skip + cmp r11d, 'Z' + ja .Ltl_skip + add r11d, 0x20 + mov BYTE PTR [rdi + r10], r11b +.Ltl_skip: + inc r10 + cmp r10, rsi + jb .Ltl_byte +.Ltl_done: + ret + + +.globl strfind_avx512 +.type strfind_avx512, @function + +# rdi=haystack, rsi=hlen, rdx=needle, rcx=nlen +# returns rax = index or (size_t)-1 +strfind_avx512: + test rcx, rcx + jz .Lsf_zero + cmp rsi, rcx + jb .Lsf_notfound + push rbx + push r12 + push r13 + push r14 + push r15 + push rbp + mov r12, rdi + mov r13, rsi + mov r14, rdx + mov r15, rcx + mov rbp, r13 + sub rbp, r15 # rbp = max valid start = hlen - nlen + movzx eax, BYTE PTR [r14] + vpbroadcastb zmm2, eax + xor r11, r11 # scan offset + +.Lsf_loop: + cmp r11, rbp + ja .Lsf_miss + lea rax, [r11 + 64] + cmp rax, r13 + ja .Lsf_scalar + vmovdqu8 zmm0, [r12 + r11] + vpcmpeqb k1, zmm0, zmm2 + kmovq rbx, k1 + +.Lsf_mask: + test rbx, rbx + jz .Lsf_advance + tzcnt rax, rbx + add rax, r11 + cmp rax, rbp + ja .Lsf_miss + xor r8, r8 +.Lsf_cmp: + cmp r8, r15 + jae .Lsf_hit + lea r10, [r12 + rax] + movzx r9d, BYTE PTR [r10 + r8] + cmp r9b, BYTE PTR [r14 + r8] + jne .Lsf_cmp_fail + inc r8 + jmp .Lsf_cmp +.Lsf_cmp_fail: + blsr rbx, rbx + jmp .Lsf_mask +.Lsf_advance: + add r11, 64 + jmp .Lsf_loop + +.Lsf_scalar: + cmp r11, rbp + ja .Lsf_miss + movzx r9d, BYTE PTR [r12 + r11] + cmp r9b, BYTE PTR [r14] + jne .Lsf_scalar_next + xor r8, r8 +.Lsf_scalar_cmp: + cmp r8, r15 + jae .Lsf_hit_r11 + lea r10, [r12 + r11] + movzx r9d, BYTE PTR [r10 + r8] + cmp r9b, BYTE PTR [r14 + r8] + jne .Lsf_scalar_next + inc r8 + jmp .Lsf_scalar_cmp +.Lsf_hit_r11: + mov rax, r11 + jmp .Lsf_hit +.Lsf_scalar_next: + inc r11 + jmp .Lsf_scalar + +.Lsf_hit: + pop rbp + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + ret +.Lsf_miss: + pop rbp + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx +.Lsf_notfound: + mov rax, -1 + ret +.Lsf_zero: + xor rax, rax + ret diff --git a/src/lotus-utils.cpp b/src/lotus-utils.cpp index 1c5ccc5f..40a8624e 100644 --- a/src/lotus-utils.cpp +++ b/src/lotus-utils.cpp @@ -58,7 +58,12 @@ bool isBackspace(uint32_t sym) { int compareAndSplitStrings(const std::string& A, const std::string& B, std::string& commonPrefix, std::string& deletedPart, std::string& addedPart) { size_t i = 0; size_t j = 0; - +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + i = compare_split_avx512(A.data(), B.data(), A.size(), B.size(), nullptr); + while (i > 0 && i < A.size() && ((A[i] & 0xC0) == 0x80)) + i--; + j = i; +#else while (i < A.size() && j < B.size()) { unsigned int lenA = fcitx_utf8_char_len(&A[i]); unsigned int lenB = fcitx_utf8_char_len(&B[j]); @@ -75,7 +80,7 @@ int compareAndSplitStrings(const std::string& A, const std::string& B, std::stri break; } } - +#endif commonPrefix.assign(A, 0, i); deletedPart.assign(A, i); addedPart.assign(B, j); @@ -95,4 +100,4 @@ std::string getFrontendName(fcitx::InputContext* ic) { return "unknown"; } return ic->frontend(); -} \ No newline at end of file +} diff --git a/src/lotus-utils.h b/src/lotus-utils.h index 4b2f95a4..753935c1 100644 --- a/src/lotus-utils.h +++ b/src/lotus-utils.h @@ -22,6 +22,21 @@ #include #include "lotus-config.h" +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) +extern "C" size_t compare_split_avx512( + const char* A, const char* B, size_t lenA, size_t lenB, void* dummy); +extern "C" size_t utf8_length_avx512(const char* str, size_t len); +extern "C" size_t find_char_avx512(const char* str, size_t len, size_t start, int ch); +extern "C" size_t compare_split_avx512( + const char* A, + const char* B, + size_t lenA, + size_t lenB, + void* dummy +); +extern "C" size_t tolower_avx512(char* str, size_t len); +extern "C" size_t strfind_avx512(const char* hay, size_t hlen, const char* needle, size_t nlen); +#endif /** * @brief Maximum length of Unix socket paths. From d5b4113b4cc6acfe00815dd50403ab3087dce889 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 11:29:59 +0700 Subject: [PATCH 24/42] rm log --- server/lotus-server.cpp | 34 ++++++++++---------- src/lotus-state.cpp | 70 +++++++++++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 27 deletions(-) diff --git a/server/lotus-server.cpp b/server/lotus-server.cpp index 5de91cf3..39b3625d 100644 --- a/server/lotus-server.cpp +++ b/server/lotus-server.cpp @@ -142,7 +142,7 @@ uid_t get_uid_for_user(const std::string& username) { void boost_process_priority() { if (setpriority(PRIO_PROCESS, 0, -10) != 0) { //NOLINT - LotusLogger::instance().error("Failed to boost process priority"); + ;//LotusLogger::instance().error("Failed to boost process priority"); } } @@ -152,7 +152,7 @@ void pin_to_pcore() { for (int i = 0; i <= 3; ++i) CPU_SET(i, &cpuset); if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { - LotusLogger::instance().error("Failed to pin process to core"); + ;//LotusLogger::instance().error("Failed to pin process to core"); } } @@ -177,11 +177,11 @@ int main(int argc, char* argv[]) { } else { target_user = get_current_username(); } - LotusLogger::instance().info("Target user: " + target_user); + //LotusLogger::instance().info("Target user: " + target_user); uid_t expected_uid = get_uid_for_user(target_user); if (expected_uid == (uid_t)-1) { - LotusLogger::instance().error("Failed to find UID for target user: " + target_user); + //LotusLogger::instance().error("Failed to find UID for target user: " + target_user); return 1; } @@ -207,7 +207,7 @@ int main(int argc, char* argv[]) { // Setup Uinput UinputDevice uinput; if (!uinput.initialize()) { - LotusLogger::instance().error("Failed to initialize uinput device"); + //LotusLogger::instance().error("Failed to initialize uinput device"); return 1; } @@ -230,12 +230,12 @@ int main(int argc, char* argv[]) { socklen_t mouse_len = offsetof(struct sockaddr_un, sun_path) + mouse_flag_socket.length() + 1; if (bind(server_fd.get(), (struct sockaddr*)&addr_kb, kb_len) != 0) { - LotusLogger::instance().error("Failed to bind socket"); + //LotusLogger::instance().error("Failed to bind socket"); return 1; } if (bind(mouse_server_fd.get(), (struct sockaddr*)&addr_mouse, mouse_len) != 0) { - LotusLogger::instance().error("Failed to bind socket"); + //LotusLogger::instance().error("Failed to bind socket"); return 1; } @@ -244,7 +244,7 @@ int main(int argc, char* argv[]) { LibinputContext li_ctx(&interface); if (!li_ctx.is_valid()) { - LotusLogger::instance().error("Failed to create libinput/udev context"); + //LotusLogger::instance().error("Failed to create libinput/udev context"); return 1; } @@ -317,17 +317,17 @@ int main(int argc, char* argv[]) { if (strcmp(exe_path, "/usr/bin/fcitx5") == 0) { authorized = true; } else { - LotusLogger::instance().warn("Unauthorized executable connection attempt to keyboard socket from: " + std::string(exe_path)); + ;//LotusLogger::instance().warn("Unauthorized executable connection attempt to keyboard socket from: " + std::string(exe_path)); } } else { - LotusLogger::instance().warn("Unauthorized UID connection attempt to keyboard socket from UID: " + std::to_string(cred.uid)); + ;//LotusLogger::instance().warn("Unauthorized UID connection attempt to keyboard socket from UID: " + std::to_string(cred.uid)); } } else { - LotusLogger::instance().warn("Failed to get peer credentials for keyboard socket"); + ;//LotusLogger::instance().warn("Failed to get peer credentials for keyboard socket"); } if (authorized) { - LotusLogger::instance().info("Fcitx5 connected to keyboard socket (PID: " + std::to_string(cred.pid) + ")"); + //LotusLogger::instance().info("Fcitx5 connected to keyboard socket (PID: " + std::to_string(cred.pid) + ")"); kb_client_fd.reset(client_fd); fds[KB_CLIENT_INDEX].fd = kb_client_fd.get(); } else { @@ -341,7 +341,7 @@ int main(int argc, char* argv[]) { int count = 0; ssize_t n = recv(fds[KB_CLIENT_INDEX].fd, &count, sizeof(count), 0); if (n <= 0) { - LotusLogger::instance().warn("Keyboard client disconnected or connection error"); + //LotusLogger::instance().warn("Keyboard client disconnected or connection error"); kb_client_fd.reset(-1); fds[KB_CLIENT_INDEX].fd = -1; } else if (count > 0) { @@ -354,7 +354,7 @@ int main(int argc, char* argv[]) { if ((fds[2].revents & POLLIN) != 0) { int new_fd = accept4(mouse_server_fd.get(), nullptr, nullptr, SOCK_NONBLOCK); if (new_fd >= 0) { - LotusLogger::instance().info("New mouse flag client connected"); + //LotusLogger::instance().info("New mouse flag client connected"); addon_fd.reset(new_fd); } } @@ -371,7 +371,7 @@ int main(int argc, char* argv[]) { if (libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED) { if (addon_fd.is_valid()) { if (send(addon_fd.get(), "C", 1, MSG_NOSIGNAL | MSG_DONTWAIT) <= 0) { - LotusLogger::instance().warn("Failed to send to mouse flag client, closing connection"); + //LotusLogger::instance().warn("Failed to send to mouse flag client, closing connection"); addon_fd.reset(-1); } } @@ -379,7 +379,7 @@ int main(int argc, char* argv[]) { } else if (type == LIBINPUT_EVENT_DEVICE_ADDED) { struct libinput_device* dev = libinput_event_get_device(event); const char* name = libinput_device_get_name(dev); - LotusLogger::instance().info("Device added: " + std::string(name)); + //LotusLogger::instance().info("Device added: " + std::string(name)); if (libinput_device_config_tap_get_finger_count(dev) > 0) { libinput_device_config_tap_set_enabled(dev, LIBINPUT_CONFIG_TAP_ENABLED); libinput_device_config_tap_set_button_map(dev, LIBINPUT_CONFIG_TAP_MAP_LRM); @@ -389,6 +389,6 @@ int main(int argc, char* argv[]) { } } } - LotusLogger::instance().info("Terminating server..."); + //LotusLogger::instance().info("Terminating server..."); return 0; } diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index b9dd3c3f..460998cb 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -172,7 +172,12 @@ namespace fcitx { const size_t cursor_sz = static_cast(cursor); // Fix that surrounding text is delay update - const size_t buffLen = utf8::length(oldPreBuffer_); + const size_t buffLen = +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + utf8_length_avx512(oldPreBuffer_.data(), oldPreBuffer_.size()); +#else + utf8::length(oldPreBuffer_); +#endif const size_t pb = text.find(oldPreBuffer_); size_t rangeStart = buffLen >= cursor_sz ? 0 : cursor_sz - buffLen; const bool sameprefix = pb != std::string::npos && pb >= rangeStart && pb <= cursor_sz; @@ -191,12 +196,22 @@ namespace fcitx { return false; // If the selection contains a newline, it's likely a multiline editor (AI ghost text), // not a single-line URL/Search bar. - size_t p = text.find('\n', selectionStart); + size_t p = +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + find_char_avx512(text.data(), text.size(), selectionStart, '\n'); +#else + text.find('\n', selectionStart); +#endif return p == std::string::npos || p >= static_cast(selectionEnd); } } - const size_t textLen = utf8::length(text); + const size_t textLen = +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + utf8_length_avx512(text.data(), text.size()); +#else + utf8::length(text); +#endif if (textLen == cursor_sz) { realtextLen.store(textLen, std::memory_order_release); return false; @@ -207,7 +222,11 @@ namespace fcitx { // Check for wayland app that use dbus as backend if (textLen > cursor_sz) if(cursor == realtextLen.load(std::memory_order_acquire) +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + && find_char_avx512(text.data(), text.size(), cursor, '\n') == static_cast(-1) +#else && text.find('\n', cursor) == std::string::npos +#endif && sameprefix) return true; @@ -504,24 +523,44 @@ namespace fcitx { pending_commit_string_ = addedPart; const auto& surrounding = ic_->surroundingText(); int autofillOffset = isAutofillCertain(surrounding) ? 1 : 0; - expected_backspaces_ = static_cast(utf8::length(deletedPart)) + 1 + autofillOffset; + expected_backspaces_ = static_cast( +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + utf8_length_avx512(deletedPart.data(), deletedPart.size()) +#else + utf8::length(deletedPart) +#endif + ) + 1 + autofillOffset; if (realMode == LotusMode::UinputWine) --expected_backspaces_; // Use deleteSurroundingText for apps that support it for smooth typing bool test_flags = false; // use for testing only :v + LOTUS_INFO("surr: \""+surrounding.text()+"\""); if (surrtp) LOTUS_INFO("surrtp"); if ((test_flags || surrtp) // Lmfao, only this work :> - && (surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText) && - (!surrounding.text().empty() && surrounding.text().back() != '\n') // firefox and discord insert '\n' into surr cause bug + && (surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText)) + && (!surrounding.text().empty() && surrounding.text().back() != '\n' // firefox and discord insert '\n' into surr cause bug && !autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes ) { LOTUS_INFO("deleteSurroundingText branch"); auto cur = static_cast(surrounding.cursor()); - const int bsCount = static_cast(utf8::length(deletedPart)); + const int bsCount = static_cast( +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + utf8_length_avx512(deletedPart.data(), deletedPart.size()) +#else + utf8::length(deletedPart) +#endif + ); if (autofillOffset) { LOTUS_INFO("have suggestions branch"); - int surrLen = static_cast(utf8::length(surrounding.text())); + const auto surr = surrounding.text(); + int surrLen = static_cast( +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + utf8_length_avx512(surr.data(), surr.size()) +#else + utf8::length(surr) +#endif + ); int realLen = static_cast(cur); int suggestionLen = surrLen - realLen; // delete suggestion tail @@ -730,7 +769,13 @@ namespace fcitx { hasMultibyte = true; break; } - if (!hasMultibyte && utf8::length(oldPreBuffer_) > 8) { + if (!hasMultibyte && +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + utf8_length_avx512(oldPreBuffer_.data(), oldPreBuffer_.size()) +#else + utf8::length(oldPreBuffer_) +#endif + > 8) { inputBackend_->resetEngine(); hasHistory_ = false; oldPreBuffer_.clear(); @@ -1071,7 +1116,12 @@ namespace fcitx { void LotusState::reset(bool isFocusOut) { const auto& surrounding = ic_->surroundingText(); const auto& text = surrounding.text(); - size_t textLen = utf8::length(text); + const size_t textLen = +#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) + utf8_length_avx512(text.data(), text.size()); +#else + utf8::length(text); +#endif realtextLen.store(textLen, std::memory_order_release); if (is_deleting_.load(std::memory_order_acquire)) { return; From af0311fc9048b8a6dfc828bf4baa1f337e450bd2 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 13:31:00 +0700 Subject: [PATCH 25/42] rm stuff Signed-off-by: Zebra2711 --- CMakeLists.txt | 5 -- src/lotus-engine.cpp | 103 +---------------------------------- src/lotus-engine.h | 22 -------- src/lotus-unikey-backend.cpp | 6 +- src/lotus.h | 41 -------------- 5 files changed, 6 insertions(+), 171 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01545707..b510fbd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,11 +45,6 @@ add_subdirectory(data) add_subdirectory(server) add_subdirectory(misc) -if(ENABLE_QT) - target_compile_definitions(lotus PRIVATE DISABLE_VERSION_ACTION) - add_subdirectory(settings-gui) -endif(ENABLE_QT) - configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in" "${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in" diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 156a3eee..fe65b558 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -15,9 +15,6 @@ #include "app_quirks.h" #include #include -#ifndef DISABLE_VERSION_ACTION -#include "lotus-version.h" -#endif #include #include @@ -65,21 +62,6 @@ namespace fcitx { return isAppModeMenuReservedKey(hotkeySym) ? FcitxKey_f : hotkeySym; } -#ifndef LOTUS_ENGINE_UNIKEY - static inline uintptr_t newMacroTable(const lotusMacroTable& macroTable) { - const auto& macros = *macroTable.macros; - std::vector charArray; - charArray.reserve((macros.size() * 2) + 1); - for (const auto& keymap : macros) { - // External C API doesn't use const, but doesn't modify data - charArray.push_back(const_cast(keymap.key->data())); //NOLINT - charArray.push_back(const_cast(keymap.value->data())); //NOLINT - } - charArray.push_back(nullptr); - return NewMacroTable(charArray.data()); - } -#endif - static inline std::vector convertToStringList(char** list) { std::vector result; if (list == nullptr) @@ -96,52 +78,23 @@ namespace fcitx { return result; } - uintptr_t LotusEngine::macroTable() const { - if (config_.inputMethod.value().empty()) { - return 0; - } - return macroTableObject_.handle(); - } - LotusEngine::LotusEngine(Instance* instance) : instance_(instance), factory_([this](InputContext& ic) { return new LotusState(this, &ic); }) { //NOLINT const char* desktop = std::getenv("XDG_CURRENT_DESKTOP"); isGnome_ = (desktop != nullptr) && std::string(desktop).find("GNOME") != std::string::npos; // emptyCustomKeymap_.customKeymap is implicitly initialized to empty by fcitx::Option default value macro. startMonitoring(); -#ifndef LOTUS_ENGINE_UNIKEY - Init(); - { - auto imNames = convertToStringList(GetInputMethodNames()); - imNames.push_back("Custom"); - imNames_ = std::move(imNames); - } -#else - imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "Telex + VNI + VIQR", "VIQR", "Microsoft layout", "VNI Bàn phím tiếng Pháp", "Simple","Custom"}; -#endif + imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "VIQR", "Microsoft"}; config_.inputMethod.annotation().setList(imNames_); auto& uiManager = instance_->userInterfaceManager(); -#ifndef DISABLE_VERSION_ACTION - versionAction_ = std::make_unique(); - versionAction_->setShortText("Lotus " LOTUS_VERSION_STRING); - versionAction_->setLongText("Lotus Input Method v" LOTUS_VERSION_STRING); - versionAction_->setIcon("help-about"); - uiManager.registerAction("lotus-version", versionAction_.get()); -#endif - charsetAction_ = std::make_unique(); charsetAction_->setShortText(_("Charset")); charsetAction_->setIcon("character-set"); uiManager.registerAction("lotus-charset", charsetAction_.get()); charsetMenu_ = std::make_unique(); charsetAction_->setMenu(charsetMenu_.get()); - -#ifndef LOTUS_ENGINE_UNIKEY - auto charsets = convertToStringList(GetCharsetNames()); -#else std::vector charsets = {"Unicode", "TCVN3", "VNI Win", "VIQR", "BK HCM 2", "UTF-8 VIQR"}; -#endif for (const auto& charset : charsets) { charsetSubAction_.emplace_back(std::make_unique()); auto* action = charsetSubAction_.back().get(); @@ -168,8 +121,6 @@ namespace fcitx { uiManager); initToggleAction(autoNonVnRestoreAction_, config_.autoNonVnRestore, "lotus-autonvnrestore", "edit-undo", _("Auto Restore Keys With Invalid Words"), _("Auto Non-VN Restore"), uiManager); - initToggleAction(enableDictionaryAction_, config_.enableDictionary, "lotus-dictionary", "accessories-dictionary", _("Enable Custom Dictionary"), _("Custom Dictionary"), - uiManager); settingsAction_ = std::make_unique(); settingsAction_->setShortText(_("Settings")); @@ -196,11 +147,8 @@ namespace fcitx { appRulesPath_ = configDir + "/lotus-app-rules.conf"; loadAppRules(); toggleActions_ = { -#ifndef DISABLE_VERSION_ACTION - versionAction_.get(), -#endif charsetAction_.get(), spellCheckAction_.get(), macroAction_.get(), capitalizeMacroAction_.get(), - autoNonVnRestoreAction_.get(), enableDictionaryAction_.get(), settingsAction_.get()}; + autoNonVnRestoreAction_.get(), settingsAction_.get()}; } void LotusEngine::initToggleAction(std::unique_ptr& action, Option& option, const std::string& actionId, const std::string& iconName, @@ -255,42 +203,6 @@ namespace fcitx { void LotusEngine::reloadConfig() { readAsIni(config_, "conf/lotus.conf"); readAsIni(customKeymap_, CustomKeymapFile); - readAsIni(macroTables_, MacroTableFile); -#ifndef LOTUS_ENGINE_UNIKEY - macroTableObject_.reset(newMacroTable(macroTables_)); - if (config_.enableDictionary.value()) { -#if LOTUS_USE_MODERN_FCITX_API - auto fd = StandardPaths::global().open(StandardPathsType::PkgData, "lotus/vietnamese.cm.dict"); -#else - auto fd = StandardPath::global().open(StandardPath::Type::PkgData, "lotus/vietnamese.cm.dict", O_RDONLY); -#endif - if (fd.isValid()) { - dictionary_.reset(NewDictionary(fd.release())); - } - } else { -#if LOTUS_USE_MODERN_FCITX_API - auto paths = StandardPaths::global().locateAll(StandardPathsType::PkgData, "lotus/vietnamese.cm.dict"); -#else - auto paths = StandardPath::global().locateAll(StandardPath::Type::PkgData, "lotus/vietnamese.cm.dict"); -#endif - for (const auto& p : paths) { -#if LOTUS_USE_MODERN_FCITX_API - if (!isStartsWith(p.string(), "/home/")) { - auto fd = fcitx::UnixFD(::open(p.c_str(), O_RDONLY)); - if (fd.isValid()) { - dictionary_.reset(NewDictionary(fd.release())); -#else - if (!isStartsWith(p, "home/")) { - int fd = ::open(p.c_str(), O_RDONLY); - if (fd != -1) { - dictionary_.reset(NewDictionary(fd)); -#endif - break; - } - } - } - } -#endif loadAppRules(); populateConfig(); } @@ -298,9 +210,6 @@ namespace fcitx { const Configuration* LotusEngine::getSubConfig(const std::string& path) const { if (path == "custom_keymap") return &customKeymap_; - if (path == "lotus-macro") { - return ¯oTables_; - } if (path == "app_rules") { return &appRulesTables_; } @@ -321,7 +230,6 @@ namespace fcitx { updateAction(nullptr, macroAction_, config_.enableMacro, _("Macro")); updateAction(nullptr, capitalizeMacroAction_, config_.capitalizeMacro, _("Capitalize Macro")); updateAction(nullptr, autoNonVnRestoreAction_, config_.autoNonVnRestore, _("Auto Non-VN Restore")); - updateAction(nullptr, enableDictionaryAction_, config_.enableDictionary, _("Custom Dictionary")); } void LotusEngine::setSubConfig(const std::string& path, const RawConfig& config) { @@ -329,13 +237,6 @@ namespace fcitx { customKeymap_.load(config, true); safeSaveAsIni(customKeymap_, CustomKeymapFile); refreshEngine(); - } else if (path == "lotus-macro") { - macroTables_.load(config, true); - safeSaveAsIni(macroTables_, MacroTableFile); -#ifndef LOTUS_ENGINE_UNIKEY - macroTableObject_.reset(newMacroTable(macroTables_)); -#endif - refreshEngine(); } else if (path == "app_rules") { appRulesTables_.load(config, true); { diff --git a/src/lotus-engine.h b/src/lotus-engine.h index f32f1966..a96bfce3 100644 --- a/src/lotus-engine.h +++ b/src/lotus-engine.h @@ -28,7 +28,6 @@ namespace fcitx { - class Object; class LotusState; /** @@ -163,20 +162,6 @@ namespace fcitx { */ const lotusCustomKeymap& customKeymap() const; - /** - * @brief Gets the dictionary handle. - * @return CGo handle for the dictionary. - */ - uintptr_t dictionary() const { - return dictionary_.handle(); - } - - /** - * @brief Gets the macro table handle. - * @return CGo handle for the macro table. - */ - uintptr_t macroTable() const; - /** * @brief Gets the emoji loader. * @return Reference to emoji loader instance. @@ -195,16 +180,11 @@ namespace fcitx { lotusCustomKeymap customKeymap_; lotusCustomKeymap emptyCustomKeymap_; - lotusMacroTable macroTables_; - Object macroTableObject_; lotusAppRules appRulesTables_; FactoryFor factory_; std::vector imNames_; -#ifndef DISABLE_VERSION_ACTION - std::unique_ptr versionAction_; -#endif std::unique_ptr charsetAction_; std::vector> charsetSubAction_; std::unique_ptr charsetMenu_; @@ -213,11 +193,9 @@ namespace fcitx { std::unique_ptr macroAction_; std::unique_ptr capitalizeMacroAction_; std::unique_ptr autoNonVnRestoreAction_; - std::unique_ptr enableDictionaryAction_; std::unique_ptr settingsAction_; std::vector toggleActions_; std::vector connections_; - Object dictionary_; std::unordered_map appRules_; std::string appRulesPath_; bool isSelectingAppMode_ = false; diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index 7998779d..6a74f9f1 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -34,7 +34,7 @@ namespace fcitx { static UkInputMethod mapLotusIm(const std::string& name) { if (name.find("Telex 2") != std::string::npos && name.find("VNI") == std::string::npos) - return UkSimpleTelex; + return UkSimpleTelex2; if (name.find("VNI") != std::string::npos || name == "VNI") return UkVni; if (name.find("VIQR") != std::string::npos) @@ -43,7 +43,9 @@ namespace fcitx { return UkMsVi; if (name.find("Telex") != std::string::npos) return UkSimpleTelex; - return UkTelex; + if (name.find("Telex + VNI") != std::string::npos) + return UkTelex; + return UkSimpleTelex; } static int mapLotusCharset(const std::string& name) { diff --git a/src/lotus.h b/src/lotus.h index 49f6947c..036f92c8 100644 --- a/src/lotus.h +++ b/src/lotus.h @@ -17,47 +17,6 @@ namespace fcitx { class LotusEngine; class LotusState; - - class Object { - public: - Object() noexcept = default; - - explicit Object(uintptr_t value) noexcept : value_(value) {} - - ~Object() = default; - - Object(const Object&) = delete; - Object& operator=(const Object&) = delete; - - Object(Object&& other) noexcept : value_(std::exchange(other.value_, 0)) {} - - Object& operator=(Object&& other) noexcept { - if (this != &other) { - value_ = std::exchange(other.value_, 0); - } - return *this; - } - - void reset(uintptr_t value = 0) noexcept { - value_ = value; - } - - [[nodiscard]] uintptr_t handle() const noexcept { - return value_; - } - - [[nodiscard]] uintptr_t release() noexcept { - return std::exchange(value_, 0); - } - - explicit operator bool() const noexcept { - return value_ != 0; - } - - private: - uintptr_t value_ = 0; - }; - } // namespace fcitx #endif // _FCITX5_LOTUS_H_ From c90b4aa57fa053c46a71f5e84fb0c5d44af0813d Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 13:42:52 +0700 Subject: [PATCH 26/42] rm stuff Signed-off-by: Zebra2711 --- src/lotus-engine.cpp | 27 --------------------------- src/lotus-unikey-backend.cpp | 14 +------------- 2 files changed, 1 insertion(+), 40 deletions(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index fe65b558..9c6310c5 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -88,33 +88,6 @@ namespace fcitx { auto& uiManager = instance_->userInterfaceManager(); - charsetAction_ = std::make_unique(); - charsetAction_->setShortText(_("Charset")); - charsetAction_->setIcon("character-set"); - uiManager.registerAction("lotus-charset", charsetAction_.get()); - charsetMenu_ = std::make_unique(); - charsetAction_->setMenu(charsetMenu_.get()); - std::vector charsets = {"Unicode", "TCVN3", "VNI Win", "VIQR", "BK HCM 2", "UTF-8 VIQR"}; - for (const auto& charset : charsets) { - charsetSubAction_.emplace_back(std::make_unique()); - auto* action = charsetSubAction_.back().get(); - action->setShortText(charset); - action->setCheckable(true); - uiManager.registerAction(stringutils::concat(CharsetActionPrefix, charset), action); - connections_.emplace_back(action->connect([this, charset](InputContext* ic) { - if (config_.outputCharset.value() == charset) - return; - config_.outputCharset.setValue(charset); - saveConfig(); - refreshEngine(); - updateCharsetAction(ic); - if (ic) - ic->updateUserInterface(UserInterfaceComponent::StatusArea); - })); - charsetMenu_->addAction(action); - } - config_.outputCharset.annotation().setList(charsets); - initToggleAction(spellCheckAction_, config_.spellCheck, "lotus-spellcheck", "tools-check-spelling", _("Enable Spell Check"), _("Spell Check"), uiManager); initToggleAction(macroAction_, config_.enableMacro, "lotus-macro", "document-edit", _("Enable Macro"), _("Macro"), uiManager); initToggleAction(capitalizeMacroAction_, config_.capitalizeMacro, "lotus-capitalizemacro", "format-text-uppercase", _("Capitalize Macro"), _("Capitalize Macro"), diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index 6a74f9f1..69159c0e 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -48,18 +48,6 @@ namespace fcitx { return UkSimpleTelex; } - static int mapLotusCharset(const std::string& name) { - if (name == "Unicode" || name.empty()) - return CONV_CHARSET_XUTF8; - if (name.find("TCVN") != std::string::npos) - return CONV_CHARSET_TCVN3; - if (name.find("VNI") != std::string::npos && name != "VNI") - return CONV_CHARSET_VNIWIN; - if (name.find("VIQR") != std::string::npos) - return CONV_CHARSET_VIQR; - return CONV_CHARSET_XUTF8; - } - class LotusUnikeyInputBackend final : public LotusInputBackend { public: void recreateEngine(LotusEngine* engine) override { @@ -147,7 +135,7 @@ namespace fcitx { UkInputMethod currentIM_ = mapLotusIm(engine->config().inputMethod.value()); uk_->setInputMethod(currentIM_); - uk_->setOutputCharset(mapLotusCharset(engine->config().outputCharset.value())); + uk_->setOutputCharset(CONV_CHARSET_XUTF8); UnikeyOptions opt{}; opt.freeMarking = *engine->config().freeMarking ? 1 : 0; From 6f3d3b29e1e1513f32f8005e016e2020ea125031 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 13:43:04 +0700 Subject: [PATCH 27/42] rm stuff ui Signed-off-by: Zebra2711 --- settings-gui/CMakeLists.txt | 47 - settings-gui/core/__init__.py | 0 settings-gui/core/dbus_handler.py | 120 --- settings-gui/i18n.py | 27 - settings-gui/main.py | 41 - ...itx.Fcitx5.Addon.Lotus.Settings.desktop.in | 9 - settings-gui/ui/__init__.py | 0 settings-gui/ui/components.py | 112 --- settings-gui/ui/main_window.py | 303 ------- settings-gui/ui/pages/__init__.py | 0 settings-gui/ui/pages/about.py | 208 ----- settings-gui/ui/pages/backup.py | 315 ------- settings-gui/ui/pages/base_editor.py | 147 ---- settings-gui/ui/pages/dict_editor.py | 418 --------- settings-gui/ui/pages/dynamic_settings.py | 345 -------- settings-gui/ui/pages/keymap_editor.py | 625 -------------- settings-gui/ui/pages/macro_editor.py | 552 ------------ settings-gui/ui/pages/mode_manager.py | 811 ------------------ settings-gui/version.py.in | 8 - 19 files changed, 4088 deletions(-) delete mode 100644 settings-gui/CMakeLists.txt delete mode 100644 settings-gui/core/__init__.py delete mode 100644 settings-gui/core/dbus_handler.py delete mode 100644 settings-gui/i18n.py delete mode 100755 settings-gui/main.py delete mode 100644 settings-gui/org.fcitx.Fcitx5.Addon.Lotus.Settings.desktop.in delete mode 100644 settings-gui/ui/__init__.py delete mode 100644 settings-gui/ui/components.py delete mode 100644 settings-gui/ui/main_window.py delete mode 100644 settings-gui/ui/pages/__init__.py delete mode 100644 settings-gui/ui/pages/about.py delete mode 100644 settings-gui/ui/pages/backup.py delete mode 100644 settings-gui/ui/pages/base_editor.py delete mode 100644 settings-gui/ui/pages/dict_editor.py delete mode 100644 settings-gui/ui/pages/dynamic_settings.py delete mode 100644 settings-gui/ui/pages/keymap_editor.py delete mode 100644 settings-gui/ui/pages/macro_editor.py delete mode 100644 settings-gui/ui/pages/mode_manager.py delete mode 100644 settings-gui/version.py.in diff --git a/settings-gui/CMakeLists.txt b/settings-gui/CMakeLists.txt deleted file mode 100644 index 899cdc62..00000000 --- a/settings-gui/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -find_package(Python3 COMPONENTS Interpreter REQUIRED) - -set(SETTINGS_GUI_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/settings-gui") -set(SETTINGS_GUI_FULL_PATH "${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}/settings-gui") - -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/version.py.in" - "${CMAKE_CURRENT_BINARY_DIR}/version.py" - @ONLY -) - -install(DIRECTORY core ui - DESTINATION "${SETTINGS_GUI_INSTALL_DIR}" - FILES_MATCHING PATTERN "*.py" PATTERN "*.qss" PATTERN "*.svg") - -install(FILES i18n.py "${CMAKE_CURRENT_BINARY_DIR}/version.py" - DESTINATION "${SETTINGS_GUI_INSTALL_DIR}") - -install(FILES main.py - DESTINATION "${SETTINGS_GUI_INSTALL_DIR}" - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - -set(LAUNCHER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/fcitx5-lotus-settings") - -file(WRITE "${LAUNCHER_SCRIPT}" -"#!/bin/sh -exec \"${SETTINGS_GUI_FULL_PATH}/main.py\" \"$@\" -") - -install(PROGRAMS "${LAUNCHER_SCRIPT}" - DESTINATION "${CMAKE_INSTALL_BINDIR}") - -install(CODE " - message(STATUS \"Byte-compiling Python scripts...\") - execute_process( - COMMAND \"${Python3_EXECUTABLE}\" -m compileall -q \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${SETTINGS_GUI_INSTALL_DIR}\" - ) -") - -fcitx5_translate_desktop_file( - "${CMAKE_CURRENT_SOURCE_DIR}/org.fcitx.Fcitx5.Addon.Lotus.Settings.desktop.in" - org.fcitx.Fcitx5.Addon.Lotus.Settings.desktop - DESKTOP -) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.Addon.Lotus.Settings.desktop" - DESTINATION "${CMAKE_INSTALL_DATADIR}/applications") \ No newline at end of file diff --git a/settings-gui/core/__init__.py b/settings-gui/core/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/settings-gui/core/dbus_handler.py b/settings-gui/core/dbus_handler.py deleted file mode 100644 index 52f032f2..00000000 --- a/settings-gui/core/dbus_handler.py +++ /dev/null @@ -1,120 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -D-Bus handler to communicate with Fcitx5 Controller. -""" - -import dbus - - -class LotusDBusHandler: - def __init__(self): - self.addon_name = "fcitx://config/addon/lotus" - try: - self.bus = dbus.SessionBus() - self.proxy = self.bus.get_object("org.fcitx.Fcitx5", "/controller") - self.iface = dbus.Interface(self.proxy, "org.fcitx.Fcitx.Controller1") - except dbus.DBusException as e: - print(f"Fcitx5 D-Bus Error: {e}") - self.iface = None - - def get_config(self) -> dict: - """Get config from Fcitx5 and convert to Python dict/list.""" - if not self.iface: - return {} - try: - values, metadata = self.iface.GetConfig(self.addon_name) - return { - "values": self._clean_dbus(values), - "metadata": self._clean_dbus(metadata), - } - except Exception as e: - print(f"Failed to fetch config: {e}") - return {} - - def set_config(self, values_dict: dict): - """Set config and send to Fcitx5.""" - if not self.iface: - return - try: - dbus_dict = self._prepare_dbus_data(values_dict) - self.iface.SetConfig(self.addon_name, dbus_dict) - except Exception as e: - print(f"Failed to set config: {e}") - - def get_sub_config_list(self, path: str, root_key: str) -> list: - """Get sub config list from Fcitx5 and convert to Python list.""" - if not self.iface: - return [] - try: - full_path = f"{self.addon_name}/{path}" - values, metadata = self.iface.GetConfig(full_path) - clean_values = self._clean_dbus(values) - - array_dict = clean_values.get(root_key, {}) - if isinstance(array_dict, dict): - sorted_keys = sorted( - array_dict.keys(), - key=lambda k: (0, int(k)) if str(k).isdigit() else (1, str(k)), - ) - return [array_dict[k] for k in sorted_keys] - elif isinstance(array_dict, list): - return array_dict - return [] - except Exception as e: - print(f"Failed to fetch sub config ({path}): {e}") - return [] - - def set_sub_config_list(self, path: str, root_key: str, data_list: list): - """Set sub config list and send to Fcitx5.""" - if not self.iface: - return - try: - full_path = f"{self.addon_name}/{path}" - fcitx_array = {str(i): item for i, item in enumerate(data_list)} - dbus_payload = {root_key: fcitx_array} - dbus_dict = self._prepare_dbus_data(dbus_payload) - self.iface.SetConfig(full_path, dbus_dict) - except Exception as e: - print(f"Failed to set sub config ({path}): {e}") - - def _prepare_dbus_data(self, data): - """Prepare data to be sent to Fcitx5 in dbus types with signatures.""" - if isinstance(data, dict): - # Fcitx5 expects dicts to be a{sv} (String to Variant) - formatted = {str(k): self._prepare_dbus_data(v) for k, v in data.items()} - return dbus.Dictionary(formatted, signature="sv") - elif isinstance(data, list): - # Arrays must be Array of Variants (av) - formatted = [self._prepare_dbus_data(v) for v in data] - return dbus.Array(formatted, signature="v") - elif isinstance(data, bool): - return dbus.Boolean(data) - elif isinstance(data, int): - return dbus.Int32(data) - elif isinstance(data, float): - return dbus.Double(data) - elif data is None: - return dbus.String("") - else: - return dbus.String(str(data)) - - def _clean_dbus(self, data): - """Convert dbus types to Python types.""" - if isinstance(data, dbus.Dictionary): - return {str(k): self._clean_dbus(v) for k, v in data.items()} - elif isinstance(data, (dbus.Array, dbus.Struct, list, tuple)): - return [self._clean_dbus(v) for v in data] - elif isinstance(data, dbus.Boolean): - return bool(data) - elif isinstance( - data, - (dbus.Int16, dbus.Int32, dbus.Int64, dbus.UInt16, dbus.UInt32, dbus.UInt64), - ): - return int(data) - elif isinstance(data, dbus.Double): - return float(data) - elif isinstance(data, dbus.String): - return str(data) - return data diff --git a/settings-gui/i18n.py b/settings-gui/i18n.py deleted file mode 100644 index 8301eac9..00000000 --- a/settings-gui/i18n.py +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Internationalization setup for the application. -""" - -import gettext -import locale -import os - - -def setup_i18n(): - """Initialize gettext with system locale.""" - try: - locale.setlocale(locale.LC_ALL, "") - domain = "fcitx5-lotus" - localedir = "/usr/share/locale" - - if os.path.exists(localedir): - gettext.bindtextdomain(domain, localedir) - gettext.textdomain(domain) - except Exception as e: - print(f"Failed to initialize i18n: {e}") - - -_ = gettext.gettext diff --git a/settings-gui/main.py b/settings-gui/main.py deleted file mode 100755 index 76cc09c5..00000000 --- a/settings-gui/main.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Application entry point. -""" - -import sys -import signal -from PySide6.QtWidgets import QApplication -from PySide6.QtCore import QTimer -from PySide6.QtGui import QIcon -from i18n import setup_i18n -from ui.main_window import LotusSettingsWindow - - -def main(): - """Main execution function.""" - setup_i18n() - app = QApplication(sys.argv) - app.setDesktopFileName("org.fcitx.Fcitx5.Addon.Lotus.Settings") - app.setApplicationName("org.fcitx.Fcitx5.Addon.Lotus.Settings") - signal.signal(signal.SIGINT, signal.SIG_DFL) - app.setWindowIcon(QIcon.fromTheme("fcitx-lotus")) - - timer = QTimer() - timer.start(500) - timer.timeout.connect(lambda: None) - - window = LotusSettingsWindow() - window.show() - - try: - sys.exit(app.exec()) - except KeyboardInterrupt: - app.quit() - - -if __name__ == "__main__": - main() diff --git a/settings-gui/org.fcitx.Fcitx5.Addon.Lotus.Settings.desktop.in b/settings-gui/org.fcitx.Fcitx5.Addon.Lotus.Settings.desktop.in deleted file mode 100644 index 957edeeb..00000000 --- a/settings-gui/org.fcitx.Fcitx5.Addon.Lotus.Settings.desktop.in +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Name=Fcitx5 Lotus Settings -Comment=Configure Fcitx5 Lotus Input Method -Exec=fcitx5-lotus-settings -Icon=fcitx-lotus -StartupWMClass=org.fcitx.Fcitx5.Addon.Lotus.Settings -Terminal=false -Type=Application -Categories=Settings;DesktopSettings; diff --git a/settings-gui/ui/__init__.py b/settings-gui/ui/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/settings-gui/ui/components.py b/settings-gui/ui/components.py deleted file mode 100644 index 8920f965..00000000 --- a/settings-gui/ui/components.py +++ /dev/null @@ -1,112 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Reusable UI components. -Uses system's libxkbcommon to natively resolve XKB keysym names, -convert keysyms to Unicode, and mathematically handle Shift modifiers. -""" - -import ctypes -import ctypes.util -from PySide6.QtWidgets import QPushButton -from PySide6.QtCore import Qt, Signal -from i18n import _ - -libxkb = None -libxkb_path = ctypes.util.find_library("xkbcommon") -if libxkb_path: - try: - libxkb = ctypes.CDLL(libxkb_path) - - libxkb.xkb_keysym_get_name.argtypes = [ - ctypes.c_uint32, - ctypes.c_char_p, - ctypes.c_size_t, - ] - libxkb.xkb_keysym_get_name.restype = ctypes.c_int - - libxkb.xkb_keysym_to_lower.argtypes = [ctypes.c_uint32] - libxkb.xkb_keysym_to_lower.restype = ctypes.c_uint32 - - libxkb.xkb_keysym_to_utf32.argtypes = [ctypes.c_uint32] - libxkb.xkb_keysym_to_utf32.restype = ctypes.c_uint32 - - except Exception as e: - print(f"Failed to load libxkbcommon: {e}") - - -class HotkeyCaptureWidget(QPushButton): - """A button that captures keystrokes to set an Fcitx5-compatible hotkey.""" - - textChanged = Signal(str) - - def __init__(self, current_key="", parent=None): - super().__init__(parent) - self.setText(current_key if current_key else _("None")) - self.setCheckable(True) - self.current_key = current_key - self.setObjectName("HotkeyButton") - self.toggled.connect(self._on_toggled) - - def _on_toggled(self, checked): - if checked: - self.setText(_("[ Recording... ]")) - else: - self.setText(self.current_key if self.current_key else _("None")) - - def keyPressEvent(self, event): - """Captures the key press when button is checked.""" - if not self.isChecked(): - super().keyPressEvent(event) - return - - key_code = event.key() - - if key_code in ( - Qt.Key_Control, - Qt.Key_Shift, - Qt.Key_Alt, - Qt.Key_Meta, - Qt.Key_unknown, - ): - return - - keysym = event.nativeVirtualKey() - base_key = "" - is_upper = False - is_symbol = False - - if libxkb and keysym > 0: - buf = ctypes.create_string_buffer(64) - if libxkb.xkb_keysym_get_name(keysym, buf, 64) > 0: - base_key = buf.value.decode("utf-8") - - lower_sym = libxkb.xkb_keysym_to_lower(keysym) - if lower_sym != keysym: - is_upper = True - - utf32 = libxkb.xkb_keysym_to_utf32(keysym) - if utf32 > 0: - char = chr(utf32) - if not char.isalpha() and not char.isspace() and char.isprintable(): - is_symbol = True - - mods = [] - if event.modifiers() & Qt.ControlModifier: - mods.append("Control") - if event.modifiers() & Qt.AltModifier: - mods.append("Alt") - if event.modifiers() & Qt.MetaModifier: - mods.append("Super") - - if event.modifiers() & Qt.ShiftModifier: - if not (is_upper or is_symbol): - mods.append("Shift") - - mods.append(base_key) - self.current_key = "+".join(mods) - - self.setText(self.current_key) - self.setChecked(False) - self.textChanged.emit(self.current_key) diff --git a/settings-gui/ui/main_window.py b/settings-gui/ui/main_window.py deleted file mode 100644 index 8e05e1d0..00000000 --- a/settings-gui/ui/main_window.py +++ /dev/null @@ -1,303 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Main window assembling all configuration tabs with a modern layout. -""" - -from PySide6.QtWidgets import ( - QMainWindow, - QWidget, - QHBoxLayout, - QVBoxLayout, - QListWidget, - QStackedWidget, - QListWidgetItem, - QApplication, - QFrame, - QPushButton, - QSpacerItem, - QSizePolicy, -) -from PySide6.QtGui import QIcon, QPalette -from PySide6.QtCore import Qt, QSize, QFile -from i18n import _ -from core.dbus_handler import LotusDBusHandler - -from ui.pages.dynamic_settings import DynamicSettingsPage, SettingsCategory -from ui.pages.macro_editor import MacroEditorPage -from ui.pages.dict_editor import DictEditorPage -from ui.pages.keymap_editor import KeymapEditorPage -from ui.pages.about import AboutPage -from ui.pages.mode_manager import ModeManagerPage -from ui.pages.backup import BackupPage -import os - - -class LotusSettingsWindow(QMainWindow): - """Main entry window for Lotus Configuration GUI.""" - - def __init__(self): - super().__init__() - self.setWindowTitle(_("Lotus Settings")) - - self.dbus_handler = LotusDBusHandler() - - self._setup_ui() - self._setup_window_size() - self._apply_global_styles() - self.update_reset_button_state() - - def update_reset_button_state(self): - any_modified_from_default = any( - (hasattr(self.content_stack.widget(i), "is_modified_from_default") - and self.content_stack.widget(i).is_modified_from_default()) - or (hasattr(self.content_stack.widget(i), "is_modified") - and self.content_stack.widget(i).is_modified()) - for i in range(self.content_stack.count()) - ) - self.btn_reset.setEnabled(any_modified_from_default) - - def _apply_global_styles(self): - self.setStyleSheet(""" - QLabel#CategoryTitle { - font-size: 22px; - } - QLabel#AboutTitle { - font-size: 26px; - } - """) - - def _setup_ui(self): - central_widget = QWidget() - self.setCentralWidget(central_widget) - - main_v_layout = QVBoxLayout(central_widget) - main_v_layout.setContentsMargins(0, 0, 0, 0) - main_v_layout.setSpacing(0) - - main_h_layout = QHBoxLayout() - main_h_layout.setContentsMargins(0, 0, 0, 0) - main_h_layout.setSpacing(0) - - self.sidebar = QListWidget() - self.sidebar.setFixedWidth(200) - self.sidebar.setStyleSheet( - """ - QListWidget { - border: none; - background: transparent; - outline: none; - } - QListWidget::item { - padding: 10px 15px; - border-radius: 8px; - margin: 2px 10px; - } - QListWidget::item:selected { - background: palette(highlight); - color: palette(highlighted-text); - } - QListWidget::item:hover:!selected { - background: palette(alternate-base); - } - """ - ) - self.sidebar.setObjectName("Sidebar") - self.sidebar.setFrameShape(QFrame.NoFrame) - - self.content_stack = QStackedWidget() - - main_h_layout.addWidget(self.sidebar) - main_h_layout.addWidget(self.content_stack, 1) - - main_v_layout.addLayout(main_h_layout, 1) - - # Bottom Bar - self._setup_bottom_bar(main_v_layout) - - # Pages Mapping - self._setup_pages() - - self.sidebar.currentRowChanged.connect(self._on_sidebar_changed) - self.sidebar.setCurrentRow(0) - - def _setup_bottom_bar(self, layout): - container = QFrame() - container.setObjectName("BottomBar") - bar_layout = QHBoxLayout(container) - bar_layout.setContentsMargins(20, 12, 20, 12) - bar_layout.setSpacing(10) - - bar_layout.addSpacing(180) - - self.btn_reset = QPushButton(QIcon.fromTheme("edit-undo"), _("&Reset")) - self.btn_reset.clicked.connect(self.on_restore_defaults) - bar_layout.addWidget(self.btn_reset) - - bar_layout.addStretch() - - self.btn_cancel = QPushButton(QIcon.fromTheme("dialog-cancel"), _("&Cancel")) - self.btn_cancel.setEnabled(False) - self.btn_cancel.clicked.connect(self.on_cancel) - bar_layout.addWidget(self.btn_cancel) - - self.btn_apply = QPushButton(QIcon.fromTheme("document-save"), _("&Apply")) - self.btn_apply.setEnabled(False) - self.btn_apply.clicked.connect(lambda: self.on_save_all(quiet=True)) - bar_layout.addWidget(self.btn_apply) - - self.btn_ok = QPushButton(QIcon.fromTheme("dialog-ok"), _("&OK")) - self.btn_ok.setObjectName("Primary") - self.btn_ok.clicked.connect(self.on_ok) - bar_layout.addWidget(self.btn_ok) - - layout.addWidget(container) - - def _setup_pages(self): - # Top-level Settings Pages - self._add_page( - _("General"), - "preferences-system", - DynamicSettingsPage(self.dbus_handler, category=SettingsCategory.GENERAL), - ) - self._add_page( - _("Typing"), - "input-keyboard", - DynamicSettingsPage(self.dbus_handler, category=SettingsCategory.TYPING), - ) - self._add_page( - _("Applications"), - "applications-other", - ModeManagerPage(self.dbus_handler), - ) - self._add_page( - _("Macros"), - "accessories-text-editor", - MacroEditorPage(self.dbus_handler), - ) - self._add_page( - _("Dictionary"), - "edit-copy", - DictEditorPage(self.dbus_handler), - ) - self._add_page( - _("Keymap"), - "preferences-desktop-keyboard", - KeymapEditorPage(self.dbus_handler), - ) - self._add_page( - _("Appearance"), - "preferences-desktop-theme", - DynamicSettingsPage( - self.dbus_handler, category=SettingsCategory.APPEARANCE - ), - ) - self._add_page( - _("Backup"), - "document-save-as", - BackupPage(self.dbus_handler), - ) - - # Bottom section - spacer = QListWidgetItem() - spacer.setFlags(Qt.NoItemFlags) - spacer.setSizeHint(QSize(0, 20)) - self.sidebar.addItem(spacer) - self._add_page(_("About"), "help-about", AboutPage()) - - def on_restore_defaults(self): - """Resets all settings to their default values.""" - from PySide6.QtWidgets import QMessageBox - - reply = QMessageBox.question( - self, - _("Confirm Reset"), - _("Are you sure you want to restore all settings to their default values?"), - QMessageBox.Yes | QMessageBox.No, - ) - if reply == QMessageBox.Yes: - for i in range(self.content_stack.count()): - page = self.content_stack.widget(i) - if hasattr(page, "restore_defaults"): - page.restore_defaults() - # After reset, we definitely have "unsaved changes" relative to previous - self.on_changed() - - def on_changed(self): - """Enables/disables the apply and cancel buttons based on pending changes.""" - any_modified = any( - hasattr(self.content_stack.widget(i), "is_modified") - and self.content_stack.widget(i).is_modified() - for i in range(self.content_stack.count()) - ) - self.btn_apply.setEnabled(any_modified) - self.btn_cancel.setEnabled(any_modified) - self.update_reset_button_state() - - def on_save_all(self, quiet=False): - """Triggers save on all pages that support it.""" - for i in range(self.content_stack.count()): - page = self.content_stack.widget(i) - if hasattr(page, "save_data"): - page.save_data() - - self.btn_apply.setEnabled(False) - self.btn_cancel.setEnabled(False) - self.update_reset_button_state() - if not quiet: - from PySide6.QtWidgets import QMessageBox - - QMessageBox.information( - self, _("Success"), _("All settings applied successfully.") - ) - - def on_ok(self): - self.on_save_all(quiet=True) - self.close() - - def on_cancel(self): - """Discards all unsaved changes by reloading data on all pages.""" - for i in range(self.content_stack.count()): - page = self.content_stack.widget(i) - if hasattr(page, "load_data"): - page.load_data() - elif hasattr(page, "load_config"): - page.load_config() - - self.btn_apply.setEnabled(False) - self.btn_cancel.setEnabled(False) - self.update_reset_button_state() - - def _on_sidebar_changed(self, index): - item = self.sidebar.item(index) - if not item: - return - - role = item.data(Qt.UserRole) - if role == "page": - widget = item.data(Qt.UserRole + 1) - if widget: - self.content_stack.setCurrentWidget(widget) - self.update_reset_button_state() - elif role == "header": - # Don't allow selecting headers, move to next item - if index + 1 < self.sidebar.count(): - self.sidebar.setCurrentRow(index + 1) - - def _setup_window_size(self): - screen = QApplication.primaryScreen().availableGeometry() - w = int(screen.width() * 0.45) - h = int(screen.height() * 0.55) - self.setMinimumSize(750, 500) - self.resize(w, h) - self.move((screen.width() - w) // 2, (screen.height() - h) // 2) - - def _add_page(self, title: str, icon_name: str, widget: QWidget): - item = QListWidgetItem(QIcon.fromTheme(icon_name), title) - item.setData(Qt.UserRole, "page") - - self.content_stack.addWidget(widget) - item.setData(Qt.UserRole + 1, widget) - - self.sidebar.addItem(item) diff --git a/settings-gui/ui/pages/__init__.py b/settings-gui/ui/pages/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/settings-gui/ui/pages/about.py b/settings-gui/ui/pages/about.py deleted file mode 100644 index 1a82f095..00000000 --- a/settings-gui/ui/pages/about.py +++ /dev/null @@ -1,208 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -import os -import subprocess -import getpass -import tempfile -from PySide6.QtWidgets import (QWidget, QVBoxLayout, QLabel, QHBoxLayout, QFrame, - QPushButton, QFileDialog, QMessageBox, QGridLayout, QScrollArea) -from PySide6.QtCore import Qt, QUrl -from PySide6.QtGui import QIcon, QDesktopServices -from i18n import _ - -try: - from version import __version__ -except ImportError: - __version__ = "dev version" # Fallback for local development - -class AboutPage(QWidget): - def __init__(self, parent=None): - super().__init__(parent) - self._setup_ui() - - def _setup_ui(self): - # Root layout for this widget - root_layout = QVBoxLayout(self) - root_layout.setContentsMargins(0, 0, 0, 0) - - # Scroll Area to handle overcrowding - scroll = QScrollArea() - scroll.setWidgetResizable(True) - scroll.setFrameShape(QFrame.NoFrame) - scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - scroll.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) - - content_widget = QWidget() - content_widget.setObjectName("AboutContent") - - layout = QVBoxLayout(content_widget) - layout.setContentsMargins(40, 30, 40, 40) - layout.setSpacing(20) - layout.setAlignment(Qt.AlignTop | Qt.AlignHCenter) - - # Logo/Icon - try: - pixmap = QIcon.fromTheme("fcitx-lotus").pixmap(80, 80) - if pixmap.isNull(): - logo = QLabel("🪷") - logo.setStyleSheet("font-size: 64px; margin-bottom: 5px;") - else: - logo = QLabel() - logo.setPixmap(pixmap) - logo.setStyleSheet("margin-bottom: 5px;") - except Exception: - logo = QLabel("🪷") - logo.setStyleSheet("font-size: 64px; margin-bottom: 5px;") - - layout.addWidget(logo, alignment=Qt.AlignCenter) - - title = QLabel("Fcitx5 Lotus") - title.setObjectName("AboutTitle") - layout.addWidget(title, alignment=Qt.AlignCenter) - - version = QLabel(_("Version {}").format(__version__)) - version.setObjectName("VersionTag") - version.setAlignment(Qt.AlignCenter) - version.setStyleSheet(""" - QLabel#VersionTag { - background-color: palette(highlight); - color: palette(highlighted-text); - border-radius: 10px; - padding: 2px 10px; - font-size: 11px; - font-weight: bold; - } - """) - layout.addWidget(version, alignment=Qt.AlignCenter) - - desc = QLabel(_("A state-of-the-art Vietnamese input method engine for Linux, designed for speed, stability, and a premium user experience.")) - desc.setWordWrap(True) - desc.setAlignment(Qt.AlignCenter) - desc.setObjectName("AboutDescription") - desc.setMinimumHeight(60) - layout.addWidget(desc, alignment=Qt.AlignCenter) - - # GitHub Project Link - github_link = QLabel('https://github.com/LotusInputMethod/fcitx5-lotus') - github_link.setOpenExternalLinks(True) - layout.addWidget(github_link, alignment=Qt.AlignCenter) - - # Support Buttons Row - support_layout = QHBoxLayout() - support_layout.setSpacing(15) - support_layout.setAlignment(Qt.AlignCenter) - - btn_bug = QPushButton(_("Report Bug")) - btn_bug.setObjectName("BugReport") - btn_bug.setFixedWidth(200) - btn_bug.clicked.connect(lambda: QDesktopServices.openUrl(QUrl("https://github.com/LotusInputMethod/fcitx5-lotus/issues/new?template=bug_report.yml"))) - - btn_feature = QPushButton(_("Request Feature")) - btn_feature.setObjectName("FeatureRequest") - btn_feature.setFixedWidth(200) - btn_feature.clicked.connect(lambda: QDesktopServices.openUrl(QUrl("https://github.com/LotusInputMethod/fcitx5-lotus/issues/new?template=feature_request.yml"))) - - support_layout.addWidget(btn_bug) - support_layout.addWidget(btn_feature) - layout.addLayout(support_layout) - - # Export Log Button - self.btn_export_log = QPushButton(_("Export Debug Logs")) - self.btn_export_log.setObjectName("ExportLogs") - self.btn_export_log.setFixedWidth(415) # 200 + 200 + 15 spacing - self.btn_export_log.clicked.connect(self._on_export_logs) - layout.addWidget(self.btn_export_log, alignment=Qt.AlignCenter) - - line = QFrame() - line.setFrameShape(QFrame.HLine) - line.setObjectName("AboutLine") - layout.addWidget(line) - - # Credits Section - credits_title = QLabel(_("DEVELOPED BY")) - credits_title.setObjectName("CreditsTitle") - layout.addWidget(credits_title, alignment=Qt.AlignCenter) - - # Authors List - Single Column with wrap-round support - authors_layout = QVBoxLayout() - authors_layout.setSpacing(12) - - authors_data = [ - ("Nguyễn Hoàng Kỳ", "https://github.com/nhktmdzhg"), - ("Nguyễn Hồng Hiệp", "https://github.com/justanoobcoder"), - ("Đặng Quang Hiển", "https://github.com/Miho1254"), - ("Zebra2711", "https://github.com/Zebra2711"), - ("Huỳnh Thiện Lộc", "https://github.com/hthienloc"), - ] - - for name, profile_url in authors_data: - author_link = QLabel(f'{name}') - author_link.setOpenExternalLinks(True) - author_link.setCursor(Qt.PointingHandCursor) - author_link.setObjectName("AuthorLink") - author_link.setAlignment(Qt.AlignCenter) - author_link.setMinimumHeight(24) - authors_layout.addWidget(author_link) - - layout.addLayout(authors_layout) - layout.addStretch() - - # Footer - footer_line = QFrame() - footer_line.setFrameShape(QFrame.HLine) - footer_line.setObjectName("AboutLine") - layout.addWidget(footer_line) - - license_info = QLabel(_("Licensed under the GNU General Public License v3.0")) - license_info.setObjectName("LicenseInfo") - layout.addWidget(license_info, alignment=Qt.AlignCenter) - - scroll.setWidget(content_widget) - root_layout.addWidget(scroll) - - def _on_export_logs(self): - # Using names that don't conflict with _ - save_dialog_result = QFileDialog.getSaveFileName( - self, _("Save Debug Log"), - os.path.expanduser("~/fcitx5-lotus-debug.log"), - "Log Files (*.log);;All Files (*)" - ) - - if not save_dialog_result or not save_dialog_result[0]: - return - - export_filename = save_dialog_result[0] - - try: - with open(export_filename, 'w') as log_output_file: - log_output_file.write("=== Fcitx5 Lotus Debug Log Export ===\n") - log_output_file.write(f"Version: {__version__}\n") - log_output_file.write(f"User: {getpass.getuser()}\n") - log_output_file.write("--------------------------------------\n\n") - - system_log_path = os.path.join(tempfile.gettempdir(), "fcitx5-lotus-server.log") - log_output_file.write(f"--- Server Log ({system_log_path}) ---\n") - if os.path.exists(system_log_path): - with open(system_log_path, 'r') as src_log: - log_output_file.write(src_log.read()) - else: - log_output_file.write("Log file not found.\n") - log_output_file.write("\n\n") - - log_output_file.write("--- Systemd Journal (fcitx5-lotus-server) ---\n") - try: - current_sys_user = getpass.getuser() - process_capture = subprocess.run( - ['journalctl', f'-u', f'fcitx5-lotus-server@{current_sys_user}.service', '--no-pager', '-n', '200'], - capture_output=True, text=True, timeout=10 - ) - log_output_file.write(process_capture.stdout if process_capture.stdout else "No journal entries found.\n") - except Exception as journal_ex: - log_output_file.write(f"Error collecting journal: {str(journal_ex)}\n") - - log_output_file.write("\n\n--- End of Log ---\n") - - QMessageBox.information(self, _("Success"), _("Debug logs exported successfully to:\n") + export_filename) - except Exception as export_ex: - QMessageBox.critical(self, _("Error"), _("Failed to export logs:\n") + str(export_ex)) diff --git a/settings-gui/ui/pages/backup.py b/settings-gui/ui/pages/backup.py deleted file mode 100644 index dd269158..00000000 --- a/settings-gui/ui/pages/backup.py +++ /dev/null @@ -1,315 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Backup and Restore page for Lotus settings. -Supports JSON-based backups and selective export/import. -""" - -import os -import json -from datetime import datetime -from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QLabel, - QPushButton, - QFileDialog, - QMessageBox, - QFrame, - QScrollArea, - QCheckBox, - QGroupBox, -) -from PySide6.QtCore import Qt -from PySide6.QtGui import QIcon -from i18n import _ -from core.dbus_handler import LotusDBusHandler -from ui.pages.dynamic_settings import CardWidget - - -class BackupPage(QWidget): - def __init__(self, dbus_handler: LotusDBusHandler, parent=None): - super().__init__(parent) - self.dbus = dbus_handler - self.restore_data = None # Stores data from opened backup for selective restore - self._setup_ui() - - def _setup_ui(self): - root_layout = QVBoxLayout(self) - root_layout.setContentsMargins(0, 0, 0, 0) - - scroll = QScrollArea() - scroll.setWidgetResizable(True) - scroll.setFrameShape(QFrame.NoFrame) - - content_widget = QWidget() - layout = QVBoxLayout(content_widget) - layout.setContentsMargins(30, 20, 30, 20) - layout.setSpacing(20) - - title = QLabel(_("Backup & Restore")) - title.setObjectName("CategoryTitle") - layout.addWidget(title) - - # Single Card for Export / Import - self.main_card = CardWidget(_("Export/Import Settings")) - - self.import_desc = QLabel( - _( - "Save or restore your configurations via JSON files. Select the components you wish to include:" - ) - ) - self.import_desc.setWordWrap(True) - self.import_desc.setStyleSheet("color: gray; font-size: 13px;") - self.main_card.content_layout.addWidget(self.import_desc) - - # Shared Checkboxes - self.checkboxes = { - "config": QCheckBox(_("Main Settings")), - "macros": QCheckBox(_("Macros")), - "keymaps": QCheckBox(_("Custom Keymaps")), - "rules": QCheckBox(_("Application Rules")), - "dictionary": QCheckBox(_("Custom Dictionary")), - } - for cb in self.checkboxes.values(): - cb.setChecked(True) - self.main_card.content_layout.addWidget(cb) - - # Buttons (Horizontal layout) - btn_layout = QHBoxLayout() - btn_layout.setSpacing(10) - - self.btn_export = QPushButton( - QIcon.fromTheme("document-save-as"), _("Export Backup...") - ) - self.btn_export.clicked.connect(self.do_export) - self.btn_export.setMinimumHeight(40) - - self.btn_import = QPushButton( - QIcon.fromTheme("document-open"), _("Import Backup...") - ) - self.btn_import.clicked.connect(self.on_select_import_file) - self.btn_import.setMinimumHeight(40) - - btn_layout.addWidget(self.btn_export) - btn_layout.addWidget(self.btn_import) - self.main_card.content_layout.addLayout(btn_layout) - - # Restore Confirmation (hidden initially) - self.restore_group = QGroupBox(_("Items found in backup:")) - self.restore_group_layout = QVBoxLayout(self.restore_group) - self.restore_group.setVisible(False) - self.main_card.content_layout.addWidget(self.restore_group) - - self.btn_restore = QPushButton( - QIcon.fromTheme("system-reboot"), _("Restore Selected Now") - ) - self.btn_restore.clicked.connect(self.on_restore_selected) - self.btn_restore.setMinimumHeight(40) - self.btn_restore.setVisible(False) - self.btn_restore.setStyleSheet("font-weight: bold;") - self.main_card.content_layout.addWidget(self.btn_restore) - - layout.addWidget(self.main_card) - - layout.addStretch() - scroll.setWidget(content_widget) - root_layout.addWidget(scroll) - - def _get_local_dict_path(self) -> str: - xdg_data_home = os.environ.get( - "XDG_DATA_HOME", os.path.expanduser("~/.local/share") - ) - return os.path.join(xdg_data_home, "fcitx5/lotus/vietnamese.cm.dict") - - def do_export(self): - """Creates a JSON backup of selected components.""" - selected = {k: cb.isChecked() for k, cb in self.checkboxes.items()} - if not any(selected.values()): - QMessageBox.warning( - self, _("Warning"), _("Please select at least one item to export.") - ) - return - - default_filename = ( - f"lotus-backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}.json" - ) - path, _filter = QFileDialog.getSaveFileName( - self, - _("Export Backup"), - os.path.join(os.path.expanduser("~"), default_filename), - _("JSON Backup (*.json);;All Files (*)"), - ) - if not path: - return - - try: - backup = { - "meta": { - "version": 1, - "timestamp": datetime.now().isoformat(), - "components": [k for k, v in selected.items() if v], - } - } - - if selected["config"]: - config_data = self.dbus.get_config().get("values", {}) - backup["config"] = config_data - - if selected["macros"]: - macros = self.dbus.get_sub_config_list("lotus-macro", "Macro") - backup["macros"] = macros - - if selected["keymaps"]: - keymaps = self.dbus.get_sub_config_list("custom_keymap", "CustomKeymap") - backup["keymaps"] = keymaps - - if selected["rules"]: - rules = self.dbus.get_sub_config_list("app_rules", "Rules") - backup["rules"] = rules - - if selected["dictionary"]: - dict_path = self._get_local_dict_path() - if os.path.exists(dict_path): - with open(dict_path, "r", encoding="utf-8") as f: - backup["dictionary"] = f.read() - - with open(path, "w", encoding="utf-8") as f: - json.dump(backup, f, indent=2, ensure_ascii=False) - - QMessageBox.information( - self, _("Success"), _("Backup exported successfully to:\n") + path - ) - - except Exception as e: - QMessageBox.critical( - self, _("Error"), _("Failed to export backup:\n") + str(e) - ) - - def on_select_import_file(self): - """Opens a JSON backup and shows available components for restore.""" - path, _filter = QFileDialog.getOpenFileName( - self, - _("Select Backup File"), - os.path.expanduser("~"), - _("JSON Backup (*.json);;All Files (*)"), - ) - if not path: - return - - try: - # Re-init UI for restore - for i in reversed(range(self.restore_group_layout.count())): - widget = self.restore_group_layout.itemAt(i).widget() - if widget: - widget.deleteLater() - - self.restore_checkboxes = {} - self.restore_data = {"json_path": path} - - with open(path, "r", encoding="utf-8") as f: - backup = json.load(f) - - options = [ - ("config", _("Main Settings")), - ("macros", _("Macros")), - ("keymaps", _("Custom Keymaps")), - ("rules", _("Application Rules")), - ("dictionary", _("Custom Dictionary")), - ] - - found_any = False - for key, label in options: - if key in backup: - cb = QCheckBox(label) - cb.setChecked(True) - self.restore_checkboxes[key] = cb - self.restore_group_layout.addWidget(cb) - found_any = True - - if not found_any: - raise ValueError( - _("Invalid backup file: No recognizable components found.") - ) - - self.restore_group.setVisible(True) - self.btn_restore.setVisible(True) - - except Exception as e: - QMessageBox.critical( - self, _("Error"), _("Failed to open backup file:\n") + str(e) - ) - - def on_restore_selected(self): - """Applies selected components from the JSON backup.""" - if not self.restore_data or "json_path" not in self.restore_data: - return - - selected_keys = [ - k for k, cb in self.restore_checkboxes.items() if cb.isChecked() - ] - if not selected_keys: - QMessageBox.warning( - self, _("Warning"), _("Please select at least one item to restore.") - ) - return - - reply = QMessageBox.warning( - self, - _("Confirm Restore"), - _( - "Are you sure you want to restore the selected components? This will overwrite your current configuration." - ), - QMessageBox.Yes | QMessageBox.No, - ) - if reply != QMessageBox.Yes: - return - - try: - with open(self.restore_data["json_path"], "r", encoding="utf-8") as f: - backup = json.load(f) - - if "config" in selected_keys and "config" in backup: - self.dbus.set_config(backup["config"]) - - if "macros" in selected_keys and "macros" in backup: - self.dbus.set_sub_config_list("lotus-macro", "Macro", backup["macros"]) - - if "keymaps" in selected_keys and "keymaps" in backup: - self.dbus.set_sub_config_list( - "custom_keymap", "CustomKeymap", backup["keymaps"] - ) - - if "rules" in selected_keys and "rules" in backup: - self.dbus.set_sub_config_list("app_rules", "Rules", backup["rules"]) - - if "dictionary" in selected_keys and "dictionary" in backup: - dict_path = self._get_local_dict_path() - os.makedirs(os.path.dirname(dict_path), exist_ok=True) - with open(dict_path, "w", encoding="utf-8") as f: - f.write(backup["dictionary"]) - - QMessageBox.information( - self, - _("Success"), - _( - "Selected components restored successfully. Some changes may require restarting Fcitx5." - ), - ) - - # Trigger UI reload - main_win = self.window() - if hasattr(main_win, "on_cancel"): - main_win.on_cancel() - - # Reset Restore UI - self.restore_group.setVisible(False) - self.btn_restore.setVisible(False) - self.restore_data = None - - except Exception as e: - QMessageBox.critical( - self, _("Error"), _("Failed to restore backup:\n") + str(e) - ) diff --git a/settings-gui/ui/pages/base_editor.py b/settings-gui/ui/pages/base_editor.py deleted file mode 100644 index bf278fc9..00000000 --- a/settings-gui/ui/pages/base_editor.py +++ /dev/null @@ -1,147 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Base class for Table-based editors (Macros, Keymap). -""" - -from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QPushButton, - QTableWidget, - QTableWidgetItem, - QHeaderView, - QMessageBox, - QLabel, - QSizePolicy, - QAbstractItemView, - QFileDialog, -) -from PySide6.QtCore import Qt -from PySide6.QtGui import QIcon -from i18n import _ - - -class BaseEditorPage(QWidget): - """Base UI for table-based editors.""" - - def __init__(self, parent=None): - super().__init__(parent) - self.table = None - - def apply_table_style(self): - """Applies modern styling to the table and connects signals.""" - if not self.table: - return - - self.table.setFocusPolicy(Qt.NoFocus) - self.table.verticalHeader().setVisible(False) - self.table.setShowGrid(False) - self.table.itemSelectionChanged.connect(self.update_button_states) - - self.table.setStyleSheet( - """ - QTableWidget { - border: 1px solid palette(midlight); - border-radius: 6px; - background-color: transparent; - } - QTableWidget::item { - padding: 4px; - border-bottom: 1px solid palette(midlight); - } - QTableWidget::item:selected { - background-color: palette(highlight); - color: palette(highlighted-text); - } - QHeaderView::section { - background-color: transparent; - border: none; - border-bottom: 2px solid palette(mid); - padding: 4px; - font-weight: bold; - } - """ - ) - - def _on_item_changed(self): - """Notifies parent window of change.""" - main_win = self.window() - if hasattr(main_win, "on_changed"): - main_win.on_changed() - self.update_button_states() - - def update_button_states(self): - """Standard button state update logic.""" - if not self.table: - return - - row = self.table.currentRow() - count = self.table.rowCount() - selected = len(self.table.selectedRanges()) > 0 - - if hasattr(self, "btn_up"): - self.btn_up.setEnabled(row > 0) - if hasattr(self, "btn_down"): - self.btn_down.setEnabled(0 <= row < count - 1) - if hasattr(self, "btn_remove"): - self.btn_remove.setEnabled(selected) - - def _swap_rows(self, row1, row2): - """Swaps two rows in the table, preserving widgets if any.""" - for col in range(self.table.columnCount()): - # Swap Items - item1 = self.table.takeItem(row1, col) - item2 = self.table.takeItem(row2, col) - self.table.setItem(row1, col, item2) - self.table.setItem(row2, col, item1) - - # Swap Widgets - w1 = self.table.cellWidget(row1, col) - w2 = self.table.cellWidget(row2, col) - - if w1: - self.table.removeCellWidget(row1, col) - if w2: - self.table.removeCellWidget(row2, col) - - if w1: - self.table.setCellWidget(row2, col, w1) - if w2: - self.table.setCellWidget(row1, col, w2) - - def on_move_up(self): - row = self.table.currentRow() - if row <= 0: - return - self._swap_rows(row, row - 1) - self.table.selectRow(row - 1) - self.update_button_states() - self._on_item_changed() - - def on_move_down(self): - row = self.table.currentRow() - if row < 0 or row >= self.table.rowCount() - 1: - return - self._swap_rows(row, row + 1) - self.table.selectRow(row + 1) - self.update_button_states() - self._on_item_changed() - - def on_remove(self): - selected_ranges = self.table.selectedRanges() - if not selected_ranges: - return - - rows_to_delete = set() - for r in selected_ranges: - for i in range(r.topRow(), r.bottomRow() + 1): - rows_to_delete.add(i) - - for row in sorted(list(rows_to_delete), reverse=True): - self.table.removeRow(row) - - self.update_button_states() - self._on_item_changed() diff --git a/settings-gui/ui/pages/dict_editor.py b/settings-gui/ui/pages/dict_editor.py deleted file mode 100644 index c0aeae69..00000000 --- a/settings-gui/ui/pages/dict_editor.py +++ /dev/null @@ -1,418 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Dictionary Editor Page. Edits lotus-dict-table.conf. -Implements UI with row reordering and TSV import/export. -""" - -import os -from pathlib import Path -from PySide6.QtWidgets import ( - QVBoxLayout, - QHBoxLayout, - QPushButton, - QTableWidget, - QTableWidgetItem, - QHeaderView, - QLineEdit, - QMessageBox, - QLabel, - QAbstractItemView, - QFileDialog, - QCheckBox, -) -from PySide6.QtGui import QIcon, QColor -from PySide6.QtCore import Qt -from i18n import _ -from core.dbus_handler import LotusDBusHandler -from ui.pages.base_editor import BaseEditorPage -from ui.pages.dynamic_settings import CardWidget - - -class DictEditorPage(BaseEditorPage): - """UI for editing Lotus dictionary.""" - - def __init__( - self, - dbus_handler: LotusDBusHandler, - parent=None, - ): - super().__init__(parent) - self.dbus = dbus_handler - self.words = [] # List of all words - self.initial_state = {} - self._setup_ui() - self.load_data() - - def _get_local_dict_path(self) -> str: - xdg_data_home = os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")) - return os.path.join(xdg_data_home, "fcitx5/lotus/vietnamese.cm.dict") - - def _get_global_dict_path(self) -> str: - # Common locations for fcitx5 pkgdata - paths = [ - "/usr/share/fcitx5/lotus/vietnamese.cm.dict", - "/usr/local/share/fcitx5/lotus/vietnamese.cm.dict", - ] - for p in paths: - if os.path.exists(p): - return p - return paths[0] - - def _setup_ui(self): - main_layout = QVBoxLayout(self) - main_layout.setContentsMargins(30, 20, 30, 20) - main_layout.setSpacing(15) - - title = QLabel(_("Custom Dictionary")) - title.setObjectName("CategoryTitle") - main_layout.addWidget(title) - - explanation = QLabel(_("Words in the custom dictionary will be protected from 'Auto Restore'. Use this for names, technical terms, or words not yet in the standard dictionary.")) - explanation.setWordWrap(True) - explanation.setStyleSheet("color: gray; font-size: 13px;") - main_layout.addWidget(explanation) - - # Dictionary behavior toggles - toggles_card = CardWidget("") - toggles_layout = QHBoxLayout() - self.cb_enable = QCheckBox(_("Enable Custom Dictionary")) - self.cb_enable.toggled.connect(self._on_item_changed) - toggles_layout.addWidget(self.cb_enable) - toggles_layout.addStretch() - - self.search_input = QLineEdit() - self.search_input.setPlaceholderText(_("Search words...")) - self.search_input.setClearButtonEnabled(True) - self.search_input.setFixedWidth(200) - self.search_input.textChanged.connect(self.on_search_changed) - toggles_layout.addWidget(QLabel(_("Search:"))) - toggles_layout.addWidget(self.search_input) - - toggles_card.content_layout.addLayout(toggles_layout) - main_layout.addWidget(toggles_card) - - # Main content area - editor_card = CardWidget("") - content_layout = QVBoxLayout() - editor_card.content_layout.addLayout(content_layout) - main_layout.addWidget(editor_card) - - # 1. Input Row (Top) - input_layout = QHBoxLayout() - self.input_word = QLineEdit() - self.input_word.setPlaceholderText(_("Word (e.g. khongdau)")) - self.input_word.setClearButtonEnabled(True) - self.input_word.returnPressed.connect(self.on_add) - - self.btn_add = QPushButton(QIcon.fromTheme("list-add"), _("Add")) - self.btn_add.clicked.connect(self.on_add) - self.input_word.textChanged.connect(self._update_add_button_icon) - - input_layout.addWidget(QLabel(_("Word:"))) - input_layout.addWidget(self.input_word, 1) - input_layout.addWidget(self.btn_add) - content_layout.addLayout(input_layout) - - # 2. Table Area - self.table = QTableWidget(0, 3) - self.table.horizontalHeader().setVisible(False) - self.table.verticalHeader().setVisible(False) - for i in range(3): - self.table.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch) - self.table.setSelectionBehavior(QAbstractItemView.SelectItems) - self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) - self.table.setAlternatingRowColors(True) - self.apply_table_style() # Apply custom table styling - self.table.cellClicked.connect(self.on_cell_clicked) - content_layout.addWidget(self.table) - - # 3. Bottom Toolbar - toolbar_layout = QHBoxLayout() - toolbar_layout.setContentsMargins(0, 5, 0, 0) - - self.btn_remove = QPushButton(QIcon.fromTheme("list-remove"), _("Remove")) - self.btn_remove.clicked.connect(self.on_remove) - - toolbar_layout.addWidget(self.btn_remove) - toolbar_layout.addStretch() - - content_layout.addLayout(toolbar_layout) - self.update_button_states() - - def load_data(self): - self.blockSignals(True) - try: - # Load global dictionary settings via DBus - config_data = self.dbus.get_config() - if config_data: - values = config_data.get("values", {}) - self.cb_enable.setChecked( - str(values.get("EnableDictionary", "True")).lower() == "true" - ) - - self.words = [] - local_path = self._get_local_dict_path() - global_path = self._get_global_dict_path() - path_to_read = local_path if os.path.exists(local_path) else global_path - - if os.path.exists(path_to_read): - try: - with open(path_to_read, "r", encoding="utf-8") as f: - for line in f: - word = line.strip() - if word and not word.startswith("#"): - self.words.append(word) - except Exception as e: - print(f"Failed to read dictionary {path_to_read}: {e}") - - self._rebuild_table() - self.initial_state = self._get_current_state() - finally: - self.blockSignals(False) - self.on_search_changed() - self.update_button_states() - - def _rebuild_table(self, filtered_words: list = None): - """Rebuilds the table based on self.words or filtered_words.""" - display_words = filtered_words if filtered_words is not None else self.words - - num_cols = 3 - num_rows = (len(display_words) + num_cols - 1) // num_cols - self.table.setRowCount(num_rows) - - for i, word in enumerate(display_words): - row = i // num_cols - col = i % num_cols - item = QTableWidgetItem(word) - self.table.setItem(row, col, item) - self._apply_cell_highlight(item, word) - - # Clear remaining cells in the last row - for i in range(len(display_words), num_rows * num_cols): - row = i // num_cols - col = i % num_cols - self.table.setItem(row, col, QTableWidgetItem("")) - - def restore_defaults(self): - """Resets dictionary to default.""" - self.blockSignals(True) - try: - self.cb_enable.setChecked(True) - self.words = [] - self.load_data() - self._on_item_changed() - finally: - self.blockSignals(False) - - def is_modified_from_default(self): - """Returns True if the dictionary has entries or checkboxes are changed from default.""" - return len(self.words) > 0 or not self.cb_enable.isChecked() - - def is_modified(self): - """Returns True if the current state differs from the initial loaded state.""" - return self._get_current_state() != self.initial_state - - def _get_current_state(self): - """Captures the current UI state for comparison.""" - return { - "words": sorted(self.words), - "EnableDictionary": self.cb_enable.isChecked(), - } - - def save_data(self): - # Save global dictionary settings via DBus - config_data = self.dbus.get_config() - if config_data: - values = config_data.get("values", {}) - values["EnableDictionary"] = "True" if self.cb_enable.isChecked() else "False" - self.dbus.set_config(values) - - local_path = self._get_local_dict_path() - try: - os.makedirs(os.path.dirname(local_path), exist_ok=True) - with open(local_path, "w", encoding="utf-8") as f: - for word in self.words: - f.write(f"{word}\n") - - # Trigger engine reload by setting global config (unchanged) - if self.dbus.iface: - current_config = self.dbus.get_config() - if current_config: - self.dbus.set_config(current_config.get("values", {})) - - self.initial_state = self._get_current_state() - except Exception as e: - QMessageBox.warning(self, _("Error"), _("Failed to save dictionary: {}").format(e)) - - def upsert_row(self, word: str, sort: bool = True): - if word in self.words: - return - self.words.append(word) - if sort: - self.words.sort() - self.on_search_changed() - self._on_item_changed() - - def _is_invalid_word(self, word: str) -> bool: - """Checks if word contains spaces.""" - if not word: - return False - return " " in word - - def _apply_cell_highlight(self, item: QTableWidgetItem, word: str): - """Applies red background and warning icon to items with invalid words.""" - is_invalid = self._is_invalid_word(word) - bg_color = Qt.transparent - tooltip = "" - icon = QIcon() - if is_invalid: - bg_color = QColor(Qt.red) - bg_color.setAlpha(60) - icon = QIcon.fromTheme("dialog-warning") - tooltip = _("Warning: Dictionary words should not contain spaces.") - - item.setBackground(bg_color) - item.setToolTip(tooltip) - item.setIcon(icon) - - def on_search_changed(self): - """Filters the words and rebuilds the table.""" - search_text = self.search_input.text().lower().strip() - if not search_text: - self._rebuild_table() - return - - filtered = [w for w in self.words if search_text in w.lower()] - self._rebuild_table(filtered) - - def on_add(self): - word = self.input_word.text().strip() - if not word: - return - - self.upsert_row(word) - self.input_word.clear() - self.input_word.setFocus() - - def _update_add_button_icon(self): - """Handles validation and Add button state.""" - word = self.input_word.text().strip() - is_invalid = self._is_invalid_word(word) - - if is_invalid: - self.input_word.setStyleSheet("color: red;") - self.input_word.setToolTip(_("Warning: Dictionary words should not contain spaces.")) - else: - self.input_word.setStyleSheet("") - self.input_word.setToolTip("") - - self.btn_add.setEnabled(not is_invalid and bool(word)) - - if word in self.words: - self.btn_add.setIcon(QIcon.fromTheme("document-save")) - self.btn_add.setText(_("Exists")) - self.btn_add.setEnabled(False) - else: - self.btn_add.setIcon(QIcon.fromTheme("list-add")) - self.btn_add.setText(_("Add")) - - def on_cell_clicked(self, row, column): - item = self.table.item(row, column) - if item and item.text(): - self.input_word.setText(item.text()) - self.update_button_states() - - def on_remove(self): - selected_items = self.table.selectedItems() - if not selected_items: - return - - for item in selected_items: - word = item.text() - if word in self.words: - self.words.remove(word) - - self.on_search_changed() - self.update_button_states() - self._on_item_changed() - self._update_add_button_icon() - - def do_import(self): - path, _filter = QFileDialog.getOpenFileName( - self, - _("Import Custom Dictionary"), - "", - _("Dictionary files (*.tsv *.txt);;All files (*)"), - ) - if not path: - return - try: - with open(path, "r", encoding="utf-8") as f: - lines = f.readlines() - except (IOError, OSError, UnicodeDecodeError) as e: - QMessageBox.warning(self, _("Error"), _("Cannot open file for reading: {}").format(e)) - return - - imported = 0 - confirmed = False - for line in lines: - word = line.strip() - if not word or word.startswith("#"): - continue - - if not confirmed and len(self.words) > 0: - reply = QMessageBox.question( - self, - _("Confirm Import"), - _( - "The current dictionary is not empty. Imported entries will be merged. Continue?" - ), - QMessageBox.Yes | QMessageBox.No, - ) - if reply == QMessageBox.No: - return - confirmed = True - else: - confirmed = True - - if word not in self.words: - self.words.append(word) - imported += 1 - - self.words.sort() - self.on_search_changed() - - QMessageBox.information( - self, - _("Import Complete"), - _("Imported {} words.").format(imported), - ) - - def do_export(self): - if not self.words: - QMessageBox.information( - self, _("Export"), _("The custom dictionary is empty, nothing to export.") - ) - return - path, _filter = QFileDialog.getSaveFileName( - self, - _("Export Custom Dictionary"), - "lotus-dict.tsv", - _("Tab-separated (*.tsv);;Text files (*.txt);;All files (*)"), - ) - if not path: - return - try: - with open(path, "w", encoding="utf-8") as f: - f.write("# Lotus Dictionary\n") - for word in self.words: - f.write(f"{word}\n") - QMessageBox.information( - self, - _("Export Complete"), - _("Exported {} words to:\n{}").format(len(self.words), path), - ) - except (IOError, OSError, UnicodeDecodeError) as e: - QMessageBox.warning(self, _("Error"), _("Cannot open file for writing: {}").format(e)) diff --git a/settings-gui/ui/pages/dynamic_settings.py b/settings-gui/ui/pages/dynamic_settings.py deleted file mode 100644 index 7ea80281..00000000 --- a/settings-gui/ui/pages/dynamic_settings.py +++ /dev/null @@ -1,345 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Dynamic Settings Page with Card-based Layout matching modern guidelines. -""" - -from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QCheckBox, - QLabel, - QScrollArea, - QFrame, - QRadioButton, - QComboBox, - QButtonGroup, - QGridLayout, - QSizePolicy, -) -from ui.components import HotkeyCaptureWidget -from core.dbus_handler import LotusDBusHandler -from enum import Enum -from i18n import _ - - -class SettingsCategory(Enum): - GENERAL = "general" - APPEARANCE = "appearance" - TYPING = "typing" - SHORTCUTS = "shortcuts" - INTERFACE = "interface" - - -# Mapping of settings keys to categories and groups -SETTINGS_MAP = { - SettingsCategory.GENERAL: { - "HOTKEYS": ["ModeMenuKey"], - "INPUT METHOD": ["InputMethod", "Mode", "OutputCharset"], - }, - SettingsCategory.APPEARANCE: { - "THEME & ICONS": ["UseLotusIcons", "UseBlackDefaultIcons"], - }, - SettingsCategory.TYPING: { - "SPELLING & CORRECTIONS": ["SpellCheck", "AutoNonVnRestore", "DdFreeStyle"], - "TYPING OPTIONS": ["ModernStyle", "FreeMarking", "W2U", "FixUinputWithAck", "DoubleSpaceToPeriod", "AutoCapitalizeAfterPunctuation"], - }, - SettingsCategory.SHORTCUTS: { - "SHORTCUTS": ["ModeMenuKey"], - } -} - - -class CardWidget(QFrame): - """A visual container (Card) for grouping related settings.""" - - def __init__(self, title: str, parent=None): - super().__init__(parent) - self.setObjectName("SettingCard") - - self.main_layout = QVBoxLayout(self) - self.main_layout.setContentsMargins(16, 16, 16, 16) - self.main_layout.setSpacing(12) - - if title: - title_label = QLabel(title) - title_label.setObjectName("CardTitle") - self.main_layout.addWidget(title_label) - - self.content_layout = QVBoxLayout() - self.content_layout.setSpacing(10) - self.main_layout.addLayout(self.content_layout) - - -class DynamicSettingsPage(QWidget): - def __init__(self, dbus_handler: LotusDBusHandler, category: SettingsCategory = SettingsCategory.GENERAL, parent=None): - super().__init__(parent) - self.dbus = dbus_handler - self.category = category - self.current_values = {} - self.initial_values = {} - self.modified_values = {} - self.button_groups = [] - - self._setup_ui() - self.load_config() - - def _setup_ui(self): - self.layout = QVBoxLayout(self) - self.layout.setContentsMargins(0, 0, 0, 0) - - self.scroll = QScrollArea() - self.scroll.setWidgetResizable(True) - self.scroll.setFrameShape(QFrame.NoFrame) - - self.container = QWidget() - self.container_layout = QVBoxLayout(self.container) - self.container_layout.setContentsMargins(30, 20, 30, 20) - self.container_layout.setSpacing(20) - - self.scroll.setWidget(self.container) - self.layout.addWidget(self.scroll) - - def load_config(self): - self.blockSignals(True) - try: - while self.container_layout.count(): - item = self.container_layout.takeAt(0) - if item.widget(): - item.widget().deleteLater() - self.button_groups.clear() - self.modified_values.clear() - - config_data = self.dbus.get_config() - if not config_data: - self.container_layout.addWidget(QLabel(_("Failed to load configuration."))) - return - - self.current_values = config_data.get("values", {}) - metadata_list = config_data.get("metadata", []) - if not metadata_list: - return - - # Flat map all items for easy lookup - self.all_metadata = {} - for group in metadata_list: - for item in group[1]: - self.all_metadata[item[0]] = item - - # Render based on SETTINGS_MAP - title_text = self.category.name.capitalize() - title = QLabel(_(title_text)) - title.setObjectName("CategoryTitle") - self.container_layout.addWidget(title) - - category_groups = SETTINGS_MAP.get(self.category, {}) - for group_name, keys in category_groups.items(): - # Convert ALL CAPS to Title Case - header_text = group_name.title() if group_name.isupper() else group_name - header = QLabel(_(header_text)) - header.setObjectName("GroupHeader") - self.container_layout.addWidget(header) - - card = CardWidget("") - found_any = False - for k in keys: - item = self.all_metadata.get(k) - if not item: - continue - - found_any = True - type_str = item[1] - if k == "ModeMenuKey" or type_str == "Hotkey": - self._render_hotkey(item, card.content_layout) - elif "Enum" in item[4]: - self._render_combobox(item, card.content_layout) - elif type_str == "Boolean": - self._render_checkbox(item, card.content_layout) - - if found_any: - self.container_layout.addWidget(card) - - if self.category == SettingsCategory.INTERFACE and not category_groups: - self.container_layout.addWidget(QLabel(_("No interface settings available yet."))) - - self.initial_values = self.current_values.copy() - self.container_layout.addStretch() - finally: - self.blockSignals(False) - - def is_modified_from_default(self): - if not hasattr(self, "all_metadata"): - return False - for key, val in self.current_values.items(): - meta = self.all_metadata.get(key) - if meta: - default_val = meta[3] - # Handle cases where default_val might be a dict (like hotkeys) - if isinstance(default_val, dict) and isinstance(val, dict): - if str(val.get("0")) != str(default_val.get("0")): - return True - elif str(val) != str(default_val): - return True - return False - - def is_modified(self): - """Returns True if the current values differ from the initial loaded values.""" - return self.current_values != self.initial_values - - def _render_hotkey(self, item, layout): - key, type_str, label, default, annotations = item - val = self.current_values.get(key, default) - - hotkey_str = val.get("0", "") if isinstance(val, dict) else "" - - row_layout = QHBoxLayout() - row_layout.addWidget(QLabel(_(label))) - row_layout.addStretch() - - hk_btn = HotkeyCaptureWidget(hotkey_str) - hk_btn.setFixedWidth(200) - hk_btn.textChanged.connect( - lambda text, k=key: self.update_config(k, {"0": text}) - ) - - row_layout.addWidget(hk_btn) - layout.addLayout(row_layout) - - def _render_combobox(self, item, layout): - key, type_str, label, default, annotations = item - val = str(self.current_values.get(key, default)) - - if "Enum" not in annotations: - return - - row_layout = QHBoxLayout() - row_layout.addWidget(QLabel(_(label))) - row_layout.addStretch() - - combo = QComboBox() - combo.setFixedWidth(200) - enum_dict = annotations.get("Enum", {}) - sorted_keys = sorted( - enum_dict.keys(), key=lambda x: int(x) if str(x).isdigit() else x - ) - - for k in sorted_keys: - rb_text = str(enum_dict[k]) - combo.addItem(_(rb_text), rb_text) - - idx = combo.findData(val) - if idx >= 0: - combo.setCurrentIndex(idx) - - combo.currentTextChanged.connect( - lambda text, k=key: self.update_config(k, combo.currentData()) - ) - row_layout.addWidget(combo) - layout.addLayout(row_layout) - - def _render_radio_group(self, item, layout, columns=1): - key, type_str, label, default, annotations = item - val = str(self.current_values.get(key, default)) - - if "Enum" not in annotations: - return - - subtitle = QLabel(f"{_(label)}") - if label != "Output Charset": - layout.addWidget(subtitle) - - enum_dict = annotations.get("Enum", {}) - sorted_keys = sorted( - enum_dict.keys(), key=lambda x: int(x) if str(x).isdigit() else x - ) - - btn_group = QButtonGroup(self) - self.button_groups.append(btn_group) - - grid = QGridLayout() - grid.setHorizontalSpacing(40) - grid.setVerticalSpacing(8) - - row, col = 0, 0 - for k in sorted_keys: - rb_text = str(enum_dict[k]) - rb = QRadioButton(_(rb_text)) - - rb.setProperty("val_str", rb_text) - - if rb_text == val: - rb.setChecked(True) - - btn_group.addButton(rb) - grid.addWidget(rb, row, col) - - col += 1 - if col >= columns: - col = 0 - row += 1 - - btn_group.buttonClicked.connect( - lambda btn, k=key: self.update_config(k, btn.property("val_str")) - ) - layout.addLayout(grid) - - def _render_checkbox(self, item, layout): - key, type_str, label, default, annotations = item - val = self.current_values.get(key, default) - - cb = QCheckBox(_(label)) - is_checked = str(val).lower() == "true" - cb.setChecked(is_checked) - - cb.toggled.connect( - lambda checked, k=key: self.update_config(k, "True" if checked else "False") - ) - layout.addWidget(cb) - - def load_data(self): - """Standardized reload method (alias for load_config).""" - self.load_config() - - def restore_defaults(self): - """Resets current values to engine defaults.""" - self.blockSignals(True) - try: - config_data = self.dbus.get_config() - if not config_data: - return - - metadata_list = config_data.get("metadata", []) - new_values = {} - for group in metadata_list: - for item in group[1]: - key, type_str, label, default, annotations = item - new_values[key] = default - self.modified_values = new_values.copy() - self.current_values = new_values - self.load_config() - finally: - self.blockSignals(False) - - def save_data(self): - """Commits all staged changes to DBus.""" - if not self.modified_values: - return - - config_data = self.dbus.get_config() - if config_data: - latest_values = config_data.get("values", {}) - latest_values.update(self.modified_values) - self.dbus.set_config(latest_values) - self.modified_values.clear() - self.initial_values = self.current_values.copy() - - def update_config(self, key: str, new_value): - """Updates internal state and notifies parent window of change.""" - self.modified_values[key] = new_value - self.current_values[key] = new_value - # Notify the parent window (LotusSettingsWindow) if it exists - main_win = self.window() - if hasattr(main_win, "on_changed"): - main_win.on_changed() diff --git a/settings-gui/ui/pages/keymap_editor.py b/settings-gui/ui/pages/keymap_editor.py deleted file mode 100644 index 512fda27..00000000 --- a/settings-gui/ui/pages/keymap_editor.py +++ /dev/null @@ -1,625 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Keymap Editor Page. Edits lotus-custom-keymap.conf. -Implements custom keymap presets and TSV import/export. -""" - -import os -from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QPushButton, - QTableWidget, - QTableWidgetItem, - QHeaderView, - QLineEdit, - QMessageBox, - QComboBox, - QLabel, - QFrame, - QFileDialog, - QAbstractItemView, - QCheckBox, -) -from PySide6.QtCore import Qt -from PySide6.QtGui import QIcon -from i18n import _ -from core.dbus_handler import LotusDBusHandler -from ui.pages.base_editor import BaseEditorPage -from ui.pages.dynamic_settings import CardWidget - -BAMBOO_ACTIONS = [ - ("XoaDauThanh", "Xóa dấu thanh"), - ("DauSac", "Dấu sắc"), - ("DauHuyen", "Dấu huyền"), - ("DauHoi", "Dấu hỏi"), - ("DauNga", "Dấu ngã"), - ("DauNang", "Dấu nặng"), - ("A_Â", "a -> â"), - ("E_Ê", "e -> ê"), - ("O_Ô", "o -> ô"), - ("AEO_ÂÊÔ", "a/e/o -> â/ê/ô"), - ("UOA_ƯƠĂ", "u/o/a -> ư/ơ/ă"), - ("D_Đ", "d -> đ"), - ("UO_ƯƠ", "u/o -> ư/ơ"), - ("A_Ă", "a -> ă"), - ("__ă", "ă"), - ("_Ă", "Ă"), - ("__â", "â"), - ("_Â", "Â"), - ("__ê", "ê"), - ("_Ê", "Ê"), - ("__ô", "ô"), - ("_Ô", "Ô"), - ("__ư", "ư"), - ("_Ư", "Ư"), - ("__ơ", "ơ"), - ("_Ơ", "Ơ"), - ("__đ", "đ"), - ("_Đ", "Đ"), - ("UOA_ƯƠĂ__Ư", "u/o/a -> ư/ơ/ă, ư"), -] - -PRESETS = { - "Telex": [ - ("z", "XoaDauThanh"), - ("s", "DauSac"), - ("f", "DauHuyen"), - ("r", "DauHoi"), - ("x", "DauNga"), - ("j", "DauNang"), - ("a", "A_Â"), - ("e", "E_Ê"), - ("o", "O_Ô"), - ("w", "UOA_ƯƠĂ"), - ("d", "D_Đ"), - ], - "VNI": [ - ("0", "XoaDauThanh"), - ("1", "DauSac"), - ("2", "DauHuyen"), - ("3", "DauHoi"), - ("4", "DauNga"), - ("5", "DauNang"), - ("6", "AEO_ÂÊÔ"), - ("7", "UO_ƯƠ"), - ("8", "A_Ă"), - ("9", "D_Đ"), - ], - "VIQR": [ - ("0", "XoaDauThanh"), - ("'", "DauSac"), - ("`", "DauHuyen"), - ("?", "DauHoi"), - ("~", "DauNga"), - (".", "DauNang"), - ("^", "AEO_ÂÊÔ"), - ("+", "UO_ƯƠ"), - ("*", "UO_ƯƠ"), - ("(", "A_Ă"), - ("d", "D_Đ"), - ], - "Microsoft layout": [ - ("8", "DauSac"), - ("5", "DauHuyen"), - ("6", "DauHoi"), - ("7", "DauNga"), - ("9", "DauNang"), - ("1", "__ă"), - ("!", "_Ă"), - ("2", "__â"), - ("@", "_Â"), - ("3", "__ê"), - ("#", "_Ê"), - ("4", "__ô"), - ("$", "_Ô"), - ("0", "__đ"), - (")", "_Đ"), - ("[", "__ư"), - ("{", "_Ư"), - ("]", "__ơ"), - ("}", "_Ơ"), - ], - "Telex 2": [ - ("z", "XoaDauThanh"), - ("s", "DauSac"), - ("f", "DauHuyen"), - ("r", "DauHoi"), - ("x", "DauNga"), - ("j", "DauNang"), - ("a", "A_Â"), - ("e", "E_Ê"), - ("o", "O_Ô"), - ("w", "UOA_ƯƠĂ__Ư"), - ("d", "D_Đ"), - ("]", "__ư"), - ("[", "__ơ"), - ("}", "_Ư"), - ("{", "_Ơ"), - ], - "Telex + VNI": [ - ("z", "XoaDauThanh"), - ("s", "DauSac"), - ("f", "DauHuyen"), - ("r", "DauHoi"), - ("x", "DauNga"), - ("j", "DauNang"), - ("a", "A_Â"), - ("e", "E_Ê"), - ("o", "O_Ô"), - ("w", "UOA_ƯƠĂ"), - ("d", "D_Đ"), - ("0", "XoaDauThanh"), - ("1", "DauSac"), - ("2", "DauHuyen"), - ("3", "DauHoi"), - ("4", "DauNga"), - ("5", "DauNang"), - ("6", "AEO_ÂÊÔ"), - ("7", "UO_ƯƠ"), - ("8", "A_Ă"), - ("9", "D_Đ"), - ], - "Telex + VNI + VIQR": [ - ("z", "XoaDauThanh"), - ("s", "DauSac"), - ("f", "DauHuyen"), - ("r", "DauHoi"), - ("x", "DauNga"), - ("j", "DauNang"), - ("a", "A_Â"), - ("e", "E_Ê"), - ("o", "O_Ô"), - ("w", "UOA_ƯƠĂ"), - ("d", "D_Đ"), - ("0", "XoaDauThanh"), - ("1", "DauSac"), - ("2", "DauHuyen"), - ("3", "DauHoi"), - ("4", "DauNga"), - ("5", "DauNang"), - ("6", "AEO_ÂÊÔ"), - ("7", "UO_ƯƠ"), - ("8", "A_Ă"), - ("9", "D_Đ"), - ("'", "DauSac"), - ("`", "DauHuyen"), - ("?", "DauHoi"), - ("~", "DauNga"), - (".", "DauNang"), - ("^", "AEO_ÂÊÔ"), - ("+", "UO_ƯƠ"), - ("*", "UO_ƯƠ"), - ("(", "A_Ă"), - ("\\\\", "D_Đ"), - ], - "VNI Bàn phím tiếng Pháp": [ - ("&", "XoaDauThanh"), - ("é", "DauSac"), - ('"', "DauHuyen"), - ("'", "DauHoi"), - ("(", "DauNga"), - ("-", "DauNang"), - ("è", "AEO_ÂÊÔ"), - ("_", "UO_ƯƠ"), - ("ç", "A_Ă"), - ("à", "D_Đ"), - ], - "Telex W": [ - ("z", "XoaDauThanh"), - ("s", "DauSac"), - ("f", "DauHuyen"), - ("r", "DauHoi"), - ("x", "DauNga"), - ("j", "DauNang"), - ("a", "A_Â"), - ("e", "E_Ê"), - ("o", "O_Ô"), - ("w", "UOA_ƯƠĂ__Ư"), - ("d", "D_Đ"), - ], -} - - -class KeymapEditorPage(BaseEditorPage): - """UI for editing Lotus custom keymap.""" - - def __init__(self, dbus_handler: LotusDBusHandler, parent=None): - super().__init__(parent) - self.dbus = dbus_handler - self.initial_state = {} - self._setup_ui() - self.load_data() - - def _setup_ui(self): - main_layout = QVBoxLayout(self) - main_layout.setContentsMargins(30, 20, 30, 20) - main_layout.setSpacing(15) - - title = QLabel(_("Keymap")) - title.setObjectName("CategoryTitle") - main_layout.addWidget(title) - - # Configuration card - config_card = CardWidget("") - config_layout = QVBoxLayout() - - # Row 1: Enable checkbox and Search - top_row = QHBoxLayout() - self.cb_enable = QCheckBox(_("Enable Custom Keymap")) - self.cb_enable.toggled.connect(self._on_item_changed) - top_row.addWidget(self.cb_enable) - top_row.addStretch() - - self.search_input = QLineEdit() - self.search_input.setPlaceholderText(_("Search keys...")) - self.search_input.setClearButtonEnabled(True) - self.search_input.setFixedWidth(200) - self.search_input.textChanged.connect(self.on_search_changed) - top_row.addWidget(QLabel(_("Search:"))) - top_row.addWidget(self.search_input) - config_layout.addLayout(top_row) - - # Row 2: Original Input Method (Preset) - bottom_row = QHBoxLayout() - bottom_row.addWidget(QLabel(_("Original Input Method:"))) - self.combo_preset = QComboBox() - self.combo_preset.addItems(PRESETS.keys()) - bottom_row.addWidget(self.combo_preset) - - btn_load_preset = QPushButton( - QIcon.fromTheme("document-import"), _("Apply Preset") - ) - btn_load_preset.clicked.connect(self.on_load_preset) - bottom_row.addWidget(btn_load_preset) - bottom_row.addStretch() - config_layout.addLayout(bottom_row) - - config_card.content_layout.addLayout(config_layout) - main_layout.addWidget(config_card) - - # Editor card - editor_card = CardWidget("") - editor_layout = QVBoxLayout() - editor_card.content_layout.addLayout(editor_layout) - main_layout.addWidget(editor_card) - - # Input Area - input_layout = QHBoxLayout() - self.input_key = QLineEdit() - self.input_key.setPlaceholderText(_("Key (Example: s)")) - self.input_key.setMaxLength(1) - self.input_key.setClearButtonEnabled(True) - - self.combo_action = QComboBox() - for action_code, action_name in BAMBOO_ACTIONS: - self.combo_action.addItem(action_name, action_code) - - self.btn_add = QPushButton(QIcon.fromTheme("list-add"), _("Add")) - self.btn_add.setToolTip(_("Add Keymap")) - self.btn_add.clicked.connect(self.on_add) - self.input_key.textChanged.connect(self._update_add_button_icon) - - input_layout.addWidget(self.input_key) - input_layout.addWidget(self.combo_action) - input_layout.addWidget(self.btn_add) - editor_layout.addLayout(input_layout) - - # Table - self.table = QTableWidget(0, 2) - self.table.setHorizontalHeaderLabels([_("Key"), _("Action")]) - self.table.horizontalHeader().setSectionResizeMode( - 0, QHeaderView.ResizeToContents - ) - self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) - self.table.setSelectionBehavior(QAbstractItemView.SelectRows) - self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) - self.table.setAlternatingRowColors(True) - self.apply_table_style() - self.table.cellClicked.connect(self.on_row_selected) - editor_layout.addWidget(self.table) - - # 3. Bottom Toolbar Layout - toolbar_layout = QHBoxLayout() - toolbar_layout.setContentsMargins(0, 5, 0, 0) - - self.btn_remove = QPushButton(QIcon.fromTheme("list-remove"), _("Remove")) - self.btn_remove.setToolTip(_("Remove selected row")) - self.btn_remove.clicked.connect(self.on_remove) - - - - toolbar_layout.addWidget(self.btn_remove) - toolbar_layout.addStretch() - - editor_layout.addLayout(toolbar_layout) - self.update_button_states() - - def load_data(self): - """Loads keymap data strictly via D-Bus.""" - self.blockSignals(True) - try: - config_data = self.dbus.get_config() - if config_data: - values = config_data.get("values", {}) - self.cb_enable.setChecked( - str(values.get("EnableCustomKeymap", "False")).lower() == "true" - ) - - self.table.setRowCount(0) - data = self.dbus.get_sub_config_list("custom_keymap", "CustomKeymap") - for item in data: - self._add_row(item.get("Key", ""), item.get("Value", "")) - self.initial_state = self._get_current_state() - finally: - self.blockSignals(False) - self.on_search_changed() - - def restore_defaults(self): - """Clears all custom keymap entries, restoring to default.""" - self.table.setRowCount(0) - self._on_item_changed() - - def is_modified_from_default(self): - """Returns True if the keymap table has any entries.""" - return self.table.rowCount() > 0 - - def is_modified(self): - """Returns True if the current state differs from the initial loaded state.""" - return self._get_current_state() != self.initial_state - - def _get_current_state(self): - """Captures the current UI state for comparison.""" - data = [] - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - combo_widget = self.table.cellWidget(row, 1) - if key_item and combo_widget: - data.append({"Key": key_item.text(), "Value": combo_widget.currentData()}) - return { - "data": data, - "EnableCustomKeymap": self.cb_enable.isChecked(), - } - - def save_data(self): - """Saves current table via DBus to C++ Engine.""" - # Save toggle - config_data = self.dbus.get_config() - if config_data: - values = config_data.get("values", {}) - values["EnableCustomKeymap"] = "True" if self.cb_enable.isChecked() else "False" - self.dbus.set_config(values) - - data = [] - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - combo_widget = self.table.cellWidget(row, 1) - if not key_item or not combo_widget: - continue - data.append({"Key": key_item.text(), "Value": combo_widget.currentData()}) - - self.dbus.set_sub_config_list("custom_keymap", "CustomKeymap", data) - self.initial_state = self._get_current_state() - - def on_search_changed(self): - """Filters the table rows based on the search input.""" - search_text = self.search_input.text().lower() - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - action_item = self.table.cellWidget(row, 1) if isinstance(self.table.cellWidget(row, 1), QComboBox) else self.table.item(row, 1) - - key = key_item.text().lower() if key_item else "" - action = "" - if isinstance(action_item, QComboBox): - action = action_item.currentText().lower() - elif action_item: - action = action_item.text().lower() - - self.table.setRowHidden(row, search_text not in key and search_text not in action) - - def on_add(self): - """Adds or updates a keymap entry.""" - key = self.input_key.text().strip() - if not key: - return - - self.upsert_row(key, self.combo_action.currentData()) - self.input_key.clear() - self.input_key.setFocus() - - def upsert_row(self, key: str, action_code: str): - """Adds or updates a row in the keymap table.""" - row = self._find_row_by_key(key) - if row is not None: - # Update existing - cell_combo = self.table.cellWidget(row, 1) - if cell_combo: - idx = cell_combo.findData(action_code) - if idx >= 0: - cell_combo.setCurrentIndex(idx) - self._on_item_changed() - return - - # Insert new - self._add_row(key, action_code) - self.on_search_changed() - self.update_button_states() - self._on_item_changed() - - def _find_row_by_key(self, key: str) -> int | None: - """Finds row index for a given key. Returns None if not found.""" - for r in range(self.table.rowCount()): - item = self.table.item(r, 0) - if item and item.text() == key: - return r - return None - - def _update_add_button_icon(self, *_args): - """Changes the Add button icon to Update if key exists.""" - key = self.input_key.text().strip() - - # Disable button if key is empty - self.btn_add.setEnabled(bool(key)) - - found = self._find_row_by_key(key) is not None - if found: - self.btn_add.setIcon(QIcon.fromTheme("document-save")) - self.btn_add.setText(_("Update")) - self.btn_add.setToolTip(_("Update Keymap")) - else: - self.btn_add.setIcon(QIcon.fromTheme("list-add")) - self.btn_add.setText(_("Add")) - self.btn_add.setToolTip(_("Add Keymap")) - - def on_load_preset(self): - """Loads a predefined set of keymaps.""" - preset_name = self.combo_preset.currentText() - reply = QMessageBox.question( - self, - _("Confirm"), - _("This operation will replace all existing keys with ") - + preset_name - + _(". Are you sure?"), - QMessageBox.Yes | QMessageBox.No, - ) - if reply == QMessageBox.No: - return - - self.table.setRowCount(0) - for key, action_code in PRESETS.get(preset_name, []): - self._add_row(key, action_code) - self._on_item_changed() - - def _add_row(self, key: str, action_code: str): - """Helper to insert a row and properly set the combobox.""" - row = self.table.rowCount() - self.table.insertRow(row) - self.table.setItem(row, 0, QTableWidgetItem(key)) - cell_combo = QComboBox() - for code, name in BAMBOO_ACTIONS: - cell_combo.addItem(name, code) - - idx = cell_combo.findData(action_code) - if idx >= 0: - cell_combo.setCurrentIndex(idx) - - cell_combo.currentIndexChanged.connect(self._on_item_changed) - self.table.setCellWidget(row, 1, cell_combo) - - def on_row_selected(self, row, column): - """Syncs the selected row data to the input fields.""" - key_item = self.table.item(row, 0) - if key_item: - self.input_key.setText(key_item.text()) - - cell_combo = self.table.cellWidget(row, 1) - if cell_combo: - self.combo_action.setCurrentIndex(cell_combo.currentIndex()) - - def do_import(self): - """Imports keymap from a TSV file.""" - path, _filter = QFileDialog.getOpenFileName( - self, - _("Import Keymap"), - "", - _("Tab-separated (*.tsv *.txt);;All files (*)"), - ) - if not path: - return - - try: - with open(path, "r", encoding="utf-8") as f: - lines = f.readlines() - except (IOError, OSError, UnicodeDecodeError) as e: - QMessageBox.warning(self, _("Error"), _("Cannot open file for reading: {}").format(e)) - return - imported = skipped = 0 - confirmed = False - for line in lines: - line = line.strip() - if not line or line.startswith("#"): - continue - parts = line.split("\t") if "\t" in line else line.split(",") - if len(parts) < 2: - skipped += 1 - continue - key, action_code = parts[0].strip(), parts[1].strip() - if not key or not action_code: - skipped += 1 - continue - if not confirmed and self.table.rowCount() > 0: - if ( - QMessageBox.question( - self, - _("Confirm Import"), - _("Merge imported entries?"), - QMessageBox.Yes | QMessageBox.No, - ) - == QMessageBox.No - ): - return - confirmed = True - else: - confirmed = True - - # Upsert - found = False - for row in range(self.table.rowCount()): - item = self.table.item(row, 0) - if item and item.text() == key: - combo = self.table.cellWidget(row, 1) - if combo: - idx = combo.findData(action_code) - if idx >= 0: - combo.setCurrentIndex(idx) - found = True - break - - if not found: - self._add_row(key, action_code) - - imported += 1 - - QMessageBox.information( - self, - _("Import Complete"), - _("Imported {} entries, skipped {} invalid lines.").format(imported, skipped), - ) - - def do_export(self): - """Exports the current table to a TSV file.""" - if self.table.rowCount() == 0: - QMessageBox.information( - self, _("Export"), _("The keymap list is empty, nothing to export.") - ) - return - - path, _filter = QFileDialog.getSaveFileName( - self, - _("Export Keymap"), - "lotus-keymap.tsv", - _("Tab-separated (*.tsv);;Text files (*.txt);;All files (*)"), - ) - if not path: - return - - try: - with open(path, "w", encoding="utf-8") as f: - f.write("# Lotus Keymap Table\n") - f.write("# Format: keyaction_code\n") - - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - combo = self.table.cellWidget(row, 1) - if key_item and combo: - f.write(f"{key_item.text()}\t{combo.currentData()}\n") - QMessageBox.information( - self, - _("Export Complete"), - _("Exported {} entries to:\n{}").format(self.table.rowCount(), path), - ) - except (IOError, OSError, UnicodeDecodeError) as e: - QMessageBox.warning(self, _("Error"), _("Cannot open file for writing: {}").format(e)) diff --git a/settings-gui/ui/pages/macro_editor.py b/settings-gui/ui/pages/macro_editor.py deleted file mode 100644 index dedceb4b..00000000 --- a/settings-gui/ui/pages/macro_editor.py +++ /dev/null @@ -1,552 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Macro Editor Page. Edits lotus-macro-table.conf. -Implements UI with row reordering and TSV import/export. -""" - -from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QPushButton, - QTableWidget, - QTableWidgetItem, - QHeaderView, - QLineEdit, - QMessageBox, - QLabel, - QSizePolicy, - QAbstractItemView, - QFileDialog, - QCheckBox, - QComboBox, -) -from PySide6.QtGui import QIcon, QColor -from PySide6.QtCore import Qt -from i18n import _ -from core.dbus_handler import LotusDBusHandler -from ui.pages.base_editor import BaseEditorPage -from ui.pages.dynamic_settings import CardWidget - - -class MacroEditorPage(BaseEditorPage): - """UI for editing Lotus macros.""" - - def __init__( - self, - dbus_handler: LotusDBusHandler, - parent=None, - ): - super().__init__(parent) - self.dbus = dbus_handler - self.initial_state = {} - self._setup_ui() - self.load_data() - - def _setup_ui(self): - main_layout = QVBoxLayout(self) - main_layout.setContentsMargins(30, 20, 30, 20) - main_layout.setSpacing(15) - - title = QLabel(_("Macros")) - title.setObjectName("CategoryTitle") - main_layout.addWidget(title) - - # Macro behavior toggles - toggles_card = CardWidget("") - toggles_layout = QHBoxLayout() - self.cb_enable = QCheckBox(_("Enable Macro")) - self.cb_capitalize = QCheckBox(_("Capitalize Macro")) - self.cb_enable.toggled.connect(self._on_item_changed) - self.cb_capitalize.toggled.connect(self._on_item_changed) - - cap_layout = QHBoxLayout() - cap_layout.setSpacing(5) - cap_layout.addWidget(self.cb_capitalize) - - help_icon = QLabel() - help_icon.setPixmap(QIcon.fromTheme("help-about").pixmap(16, 16)) - help_icon.setToolTip(_("Automatically match expansion case to trigger key case:
- 'kg' → 'khô gà' (all lowercase)
- 'KG' → 'KHÔ GÀ' (all uppercase)
- 'Kg' → 'khô gà' (original macro case)")) - cap_layout.addWidget(help_icon) - - toggles_layout.addWidget(self.cb_enable) - toggles_layout.addLayout(cap_layout) - toggles_layout.addStretch() - - self.search_input = QLineEdit() - self.search_input.setPlaceholderText(_("Search macros...")) - self.search_input.setClearButtonEnabled(True) - self.search_input.setFixedWidth(200) - self.search_input.textChanged.connect(self.on_search_changed) - toggles_layout.addWidget(QLabel(_("Search:"))) - toggles_layout.addWidget(self.search_input) - - toggles_card.content_layout.addLayout(toggles_layout) - main_layout.addWidget(toggles_card) - - # Main content area - editor_card = CardWidget("") - content_layout = QVBoxLayout() - editor_card.content_layout.addLayout(content_layout) - main_layout.addWidget(editor_card) - - # Dynamic Macro Settings (Moved below the macro table) - dynamic_card = CardWidget("") - dynamic_layout = QVBoxLayout() - - # Hint text - hint_label = QLabel(_("Macros can use dynamic placeholders: $TIME (current time) and $DATE (current date).")) - hint_label.setWordWrap(True) - hint_label.setStyleSheet("color: gray; font-size: 13px;") - dynamic_layout.addWidget(hint_label) - - # Format Inputs - fmt_container = QWidget() - fmt_hbox = QHBoxLayout(fmt_container) - fmt_hbox.setContentsMargins(0, 5, 0, 0) - fmt_hbox.setSpacing(20) - - # Time Format - time_layout = QHBoxLayout() - self.input_time_format = QComboBox() - self.input_time_format.setEditable(False) - self.input_time_format.currentIndexChanged.connect(self._on_item_changed) - - time_presets = [ - ("%H:%M", "15:04 (24h)"), - ("%H:%M:%S", "15:04:05 (24h)"), - ("%I:%M %p", "03:04 PM"), - ("%I:%M:%S %p", "03:04:05 PM"), - ("", _("None (Do not replace $TIME)")), - ] - for fmt, desc in time_presets: - self.input_time_format.addItem(fmt, fmt) - self.input_time_format.setItemData(self.input_time_format.count() - 1, desc, Qt.ToolTipRole) - - time_layout.addWidget(QLabel(_("Time Format:"))) - time_layout.addWidget(self.input_time_format, 1) - - # Date Format - date_layout = QHBoxLayout() - self.input_date_format = QComboBox() - self.input_date_format.setEditable(False) - self.input_date_format.currentIndexChanged.connect(self._on_item_changed) - - date_presets = [ - ("%d/%m/%Y", "dd/MM/yyyy"), - ("%d/%m/%y", "dd/MM/yy"), - ("%m/%d/%Y", "MM/dd/yyyy"), - ("%Y-%m-%d", "yyyy-MM-dd"), - ("%y-%m-%d", "yy-MM-dd"), - ("", _("None (Do not replace $DATE)")), - ] - for fmt, desc in date_presets: - self.input_date_format.addItem(fmt, fmt) - self.input_date_format.setItemData(self.input_date_format.count() - 1, desc, Qt.ToolTipRole) - - date_layout.addWidget(QLabel(_("Date Format:"))) - date_layout.addWidget(self.input_date_format, 1) - - fmt_hbox.addLayout(time_layout) - fmt_hbox.addLayout(date_layout) - - dynamic_layout.addWidget(fmt_container) - - dynamic_card.content_layout.addLayout(dynamic_layout) - main_layout.addWidget(dynamic_card) - - # 1. Input Row (Top) - input_layout = QHBoxLayout() - self.input_key = QLineEdit() - self.input_key.setPlaceholderText(_("Abbreviation (e.g. kg)")) - self.input_key.setClearButtonEnabled(True) - - self.input_val = QLineEdit() - self.input_val.setPlaceholderText(_("Full text (e.g. khô gà)")) - self.input_val.setClearButtonEnabled(True) - self.input_val.returnPressed.connect(self.on_add) - - self.btn_add = QPushButton(QIcon.fromTheme("list-add"), _("Add")) - self.btn_add.clicked.connect(self.on_add) - self.input_key.textChanged.connect(self._update_add_button_icon) - self.input_val.textChanged.connect(self._update_add_button_icon) - - input_layout.addWidget(QLabel(_("Key:"))) - input_layout.addWidget(self.input_key, 1) - input_layout.addWidget(QLabel(_("Value:"))) - input_layout.addWidget(self.input_val, 2) - input_layout.addWidget(self.btn_add) - content_layout.addLayout(input_layout) - - # 2. Table Area - self.table = QTableWidget(0, 2) - self.table.setHorizontalHeaderLabels([_("Abbreviation"), _("Expanded Text")]) - self.table.horizontalHeader().setSectionResizeMode( - 0, QHeaderView.ResizeToContents - ) - self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) - self.table.setSelectionBehavior(QAbstractItemView.SelectRows) - self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) - self.table.setAlternatingRowColors(True) - self.apply_table_style() # Apply custom table styling - self.table.cellClicked.connect(self.on_row_selected) - content_layout.addWidget(self.table) - - # 3. Bottom Toolbar - toolbar_layout = QHBoxLayout() - toolbar_layout.setContentsMargins(0, 5, 0, 0) - - self.btn_remove = QPushButton(QIcon.fromTheme("list-remove"), _("Remove")) - self.btn_up = QPushButton(QIcon.fromTheme("go-up"), "") - self.btn_up.setToolTip(_("Move Up")) - self.btn_down = QPushButton(QIcon.fromTheme("go-down"), "") - self.btn_down.setToolTip(_("Move Down")) - - self.btn_remove.clicked.connect(self.on_remove) - self.btn_up.clicked.connect(self.on_move_up) - self.btn_down.clicked.connect(self.on_move_down) - - toolbar_layout.addWidget(self.btn_remove) - toolbar_layout.addWidget(self.btn_up) - toolbar_layout.addWidget(self.btn_down) - toolbar_layout.addStretch() - - content_layout.addLayout(toolbar_layout) - self.update_button_states() - - def load_data(self): - self.blockSignals(True) - try: - # Load global macro settings via DBus - config_data = self.dbus.get_config() - if config_data: - values = config_data.get("values", {}) - self.cb_enable.setChecked( - str(values.get("EnableMacro", "True")).lower() == "true" - ) - self.cb_capitalize.setChecked( - str(values.get("CapitalizeMacro", "True")).lower() == "true" - ) - # Set time format (default %H:%M) - time_fmt = values.get("TimeFormat", "%H:%M") - index = self.input_time_format.findData(time_fmt) - if index >= 0: - self.input_time_format.setCurrentIndex(index) - else: - # Fallback to default if not in list (since it's not editable anymore) - self.input_time_format.setCurrentIndex(self.input_time_format.findData("%H:%M")) - - # Set date format - date_fmt = values.get("DateFormat", "%d/%m/%Y") - index = self.input_date_format.findData(date_fmt) - if index >= 0: - self.input_date_format.setCurrentIndex(index) - else: - self.input_date_format.setCurrentIndex(self.input_date_format.findData("%d/%m/%Y")) - - self.table.setRowCount(0) - data = self.dbus.get_sub_config_list("lotus-macro", "Macro") - for item in data: - self.upsert_row(item.get("Key", ""), item.get("Value", ""), sort=False) - self.on_search_changed() - self.initial_state = self._get_current_state() - finally: - self.blockSignals(False) - - def restore_defaults(self): - """Resets macros to default (empty table, enabled checkboxes).""" - self.blockSignals(True) - try: - self.cb_enable.setChecked(True) - self.cb_capitalize.setChecked(True) - self.table.setRowCount(0) - self._on_item_changed() - finally: - self.blockSignals(False) - - def is_modified_from_default(self): - """Returns True if the macro table has entries or checkboxes are changed from default.""" - # Default state: table is empty, both checkboxes are True. - return ( - self.table.rowCount() > 0 - or not self.cb_enable.isChecked() - or not self.cb_capitalize.isChecked() - ) - - def is_modified(self): - """Returns True if the current state differs from the initial loaded state.""" - return self._get_current_state() != self.initial_state - - def _get_current_state(self): - """Captures the current UI state for comparison.""" - data = [] - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - val_item = self.table.item(row, 1) - if key_item and key_item.text(): - data.append( - {"Key": key_item.text(), "Value": val_item.text() if val_item else ""} - ) - return { - "data": data, - "EnableMacro": self.cb_enable.isChecked(), - "CapitalizeMacro": self.cb_capitalize.isChecked(), - "TimeFormat": self.input_time_format.currentText(), - "DateFormat": self.input_date_format.currentText(), - } - - def save_data(self): - # Save global macro settings via DBus - config_data = self.dbus.get_config() - if config_data: - values = config_data.get("values", {}) - values["EnableMacro"] = "True" if self.cb_enable.isChecked() else "False" - values["CapitalizeMacro"] = ( - "True" if self.cb_capitalize.isChecked() else "False" - ) - values["TimeFormat"] = self.input_time_format.currentText() - values["DateFormat"] = self.input_date_format.currentText() - self.dbus.set_config(values) - - data = [] - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - val_item = self.table.item(row, 1) - if not key_item or not key_item.text(): - continue - data.append( - {"Key": key_item.text(), "Value": val_item.text() if val_item else ""} - ) - - self.dbus.set_sub_config_list("lotus-macro", "Macro", data) - self.initial_state = self._get_current_state() - - def _find_row_by_key(self, key: str) -> int | None: - """Finds row index for a given key. Returns None if not found.""" - for r in range(self.table.rowCount()): - item = self.table.item(r, 0) - if item and item.text() == key: - return r - return None - - def upsert_row(self, key: str, value: str, sort: bool = True): - # Update existing - row = self._find_row_by_key(key) - if row is not None: - self.table.item(row, 1).setText(value) - self._apply_row_highlight(row, key) - if sort: - self.on_search_changed() # Re-apply filter - self.update_button_states() - self._on_item_changed() - return - - # Insert new - row = self.table.rowCount() - self.table.insertRow(row) - self.table.setItem(row, 0, QTableWidgetItem(key)) - self.table.setItem(row, 1, QTableWidgetItem(value)) - self._apply_row_highlight(row, key) - self.on_search_changed() - if sort: - self.on_search_changed() # Re-apply filter - self.update_button_states() - self._on_item_changed() - - def _is_invalid_macro(self, key: str) -> bool: - """Checks if macro key contains spaces or non-letter characters.""" - if not key: - return False - # Allow alphanumeric characters (including Unicode letters) - return not key.isalnum() - - def _apply_row_highlight(self, row: int, key: str): - """Applies red background and warning icon to rows with invalid keys.""" - is_invalid = self._is_invalid_macro(key) - bg_color = Qt.transparent - tooltip = "" - icon = QIcon() - if is_invalid: - # Use a soft red for warning background - bg_color = QColor(Qt.red) - bg_color.setAlpha(60) - icon = QIcon.fromTheme("dialog-warning") - tooltip = _("Warning: Macro key should not contain spaces or special characters.") - - for col in range(self.table.columnCount()): - item = self.table.item(row, col) - if item: - item.setBackground(bg_color) - item.setData(Qt.ForegroundRole, None) - item.setToolTip(tooltip) - # Show icon in the first column - if col == 0: - item.setIcon(icon) - else: - item.setIcon(QIcon()) - - def sort_invalid_to_top(self): - """Moves all invalid entries to the top, then sorts by key.""" - # We'll extract all items, sort them, and put them back. - rows = [] - for row in range(self.table.rowCount()): - key = self.table.item(row, 0).text() if self.table.item(row, 0) else "" - val = self.table.item(row, 1).text() if self.table.item(row, 1) else "" - rows.append((key, val)) - - rows.sort(key=lambda x: (not self._is_invalid_macro(x[0]), x[0].lower())) - - self.blockSignals(True) - self.table.setRowCount(0) - for key, val in rows: - row_idx = self.table.rowCount() - self.table.insertRow(row_idx) - self.table.setItem(row_idx, 0, QTableWidgetItem(key)) - self.table.setItem(row_idx, 1, QTableWidgetItem(val)) - self._apply_row_highlight(row_idx, key) - self.on_search_changed() # Re-apply filter - self.blockSignals(False) - - def on_search_changed(self): - """Filters the table rows based on the search input.""" - search_text = self.search_input.text().lower() - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - val_item = self.table.item(row, 1) - key = key_item.text().lower() if key_item else "" - val = val_item.text().lower() if val_item else "" - - # Show row if either key or value matches search text - self.table.setRowHidden(row, search_text not in key and search_text not in val) - - def on_add(self): - key = self.input_key.text().strip() - val = self.input_val.text().strip() - if not key or not val: - return - - self.upsert_row(key, val) - self.input_key.clear() - self.input_val.clear() - self.input_key.setFocus() - - def _update_add_button_icon(self): - """Changes the Add button icon to Update if key exists and handles validation.""" - key = self.input_key.text().strip() - val = self.input_val.text().strip() - is_invalid = self._is_invalid_macro(key) - - # Validation feedback for input field - if is_invalid: - self.input_key.setStyleSheet("color: red;") - self.input_key.setToolTip(_("Warning: Macro key should not contain spaces or special characters.")) - else: - self.input_key.setStyleSheet("") - self.input_key.setToolTip("") - - # Disable button if key is invalid, empty, or value is empty - self.btn_add.setEnabled(not is_invalid and bool(key) and bool(val)) - - found = self._find_row_by_key(key) is not None - if found: - self.btn_add.setIcon(QIcon.fromTheme("document-save")) - self.btn_add.setText(_("Update")) - else: - self.btn_add.setIcon(QIcon.fromTheme("list-add")) - self.btn_add.setText(_("Add")) - - def on_row_selected(self, row, column): - key_item = self.table.item(row, 0) - if key_item: - self.input_key.setText(key_item.text()) - val_item = self.table.item(row, 1) - if val_item: - self.input_val.setText(val_item.text()) - self.update_button_states() - - def do_import(self): - path, _filter = QFileDialog.getOpenFileName( - self, - _("Import Macros"), - "", - _("Tab-separated (*.tsv *.txt);;All files (*)"), - ) - if not path: - return - try: - with open(path, "r", encoding="utf-8") as f: - lines = f.readlines() - except (IOError, OSError, UnicodeDecodeError) as e: - QMessageBox.warning(self, _("Error"), _("Cannot open file for reading: {}").format(e)) - return - - imported = skipped = 0 - confirmed = False - for line in lines: - line = line.strip() - if not line or line.startswith("#"): - continue - parts = line.split("\t") if "\t" in line else line.split(",") - if len(parts) < 2: - skipped += 1 - continue - key, val = parts[0].strip(), parts[1].strip() - if not key or not val: - skipped += 1 - continue - - if not confirmed and self.table.rowCount() > 0: - reply = QMessageBox.question( - self, - _("Confirm Import"), - _( - "The current macro list is not empty. Imported entries will be merged. Continue?" - ), - QMessageBox.Yes | QMessageBox.No, - ) - if reply == QMessageBox.No: - return - confirmed = True - else: - confirmed = True - - self.upsert_row(key, val) - imported += 1 - - QMessageBox.information( - self, - _("Import Complete"), - _("Imported {} entries, skipped {} invalid lines.").format(imported, skipped), - ) - - def do_export(self): - if self.table.rowCount() == 0: - QMessageBox.information( - self, _("Export"), _("The macro list is empty, nothing to export.") - ) - return - path, _filter = QFileDialog.getSaveFileName( - self, - _("Export Macros"), - "lotus-macro.tsv", - _("Tab-separated (*.tsv);;Text files (*.txt);;All files (*)"), - ) - if not path: - return - try: - with open(path, "w", encoding="utf-8") as f: - f.write("# Lotus Macro Table\n# Format: shorthandexpanded text\n") - for row in range(self.table.rowCount()): - key_item = self.table.item(row, 0) - val_item = self.table.item(row, 1) - if key_item and val_item and key_item.text(): - f.write(f"{key_item.text()}\t{val_item.text()}\n") - QMessageBox.information( - self, - _("Export Complete"), - _("Exported {} entries to:\n{}").format(self.table.rowCount(), path), - ) - except (IOError, OSError, UnicodeDecodeError) as e: - QMessageBox.warning(self, _("Error"), _("Cannot open file for writing: {}").format(e)) diff --git a/settings-gui/ui/pages/mode_manager.py b/settings-gui/ui/pages/mode_manager.py deleted file mode 100644 index 21423e15..00000000 --- a/settings-gui/ui/pages/mode_manager.py +++ /dev/null @@ -1,811 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Mode Manager Page for per-application input mode configuration. -""" - -import os -import re -from PySide6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QListWidget, - QListWidgetItem, - QLabel, - QFrame, - QPushButton, - QLineEdit, - QScrollArea, - QDialog, - QTabWidget, - QFileDialog, - QComboBox, - QGridLayout, - QMessageBox, -) -from PySide6.QtCore import Qt, QSize, Signal -from PySide6.QtGui import QIcon -from i18n import _ -from ui.pages.dynamic_settings import CardWidget -from core.dbus_handler import LotusDBusHandler - -# Mode constants as defined in C++ LotusEngine -MODE_OFF = 0 -MODE_SMOOTH = 1 -MODE_SLOW = 2 -MODE_HARDCORE = 3 -MODE_SURROUNDING = 4 -MODE_PREEDIT = 5 -MODE_EMOJI = 6 -MODE_DEFAULT = -1 # UI special value for "Use Global Default" - -MODE_INFO = { - MODE_DEFAULT: {"title": "Default", "icon": "preferences-system"}, - MODE_OFF: {"title": "Off", "icon": "input-keyboard"}, - MODE_SMOOTH: {"title": "Uinput (Smooth)", "icon": "input-keyboard"}, - MODE_SLOW: {"title": "Uinput (Slow)", "icon": "input-keyboard"}, - MODE_HARDCORE: {"title": "Uinput (Hardcore)", "icon": "input-keyboard"}, - MODE_SURROUNDING: {"title": "Surrounding Text", "icon": "text-field"}, - MODE_PREEDIT: {"title": "Preedit", "icon": "text-field"}, - MODE_EMOJI: {"title": "Emoji Picker", "icon": "face-smile"}, -} - - - -class ModeCard(QFrame): - """A card for selecting an input mode.""" - - clicked = Signal(int) - - def __init__(self, mode: int, selected: bool = False, parent=None): - super().__init__(parent) - self.mode = mode - self.selected = selected - self.setObjectName("ModeCard") - self.setCursor(Qt.PointingHandCursor) - self._setup_ui() - self.update_style() - - def _setup_ui(self): - layout = QVBoxLayout(self) - layout.setContentsMargins(10, 15, 10, 15) - layout.setSpacing(5) - - info = MODE_INFO[self.mode] - title_label = QLabel(_(info["title"])) - title_label.setObjectName("ModeCardTitle") - title_label.setAlignment(Qt.AlignCenter) - title_label.setWordWrap(True) - title_label.setStyleSheet("font-weight: bold; font-size: 13px;") - - layout.addWidget(title_label) - - def update_style(self): - if self.selected: - self.setStyleSheet( - """ - QFrame#ModeCard { - border: 1.5px solid palette(highlight); - background: palette(highlight); - border-radius: 8px; - } - QLabel { color: palette(highlighted-text); } - """ - ) - else: - self.setStyleSheet( - """ - QFrame#ModeCard { - border: 1.5px solid palette(mid); - background: palette(alternate-base); - border-radius: 8px; - } - """ - ) - - def mousePressEvent(self, event): - if event.button() == Qt.LeftButton: - self.clicked.emit(self.mode) - - -class AddAppDialog(QDialog): - """Dialog to add a new application to the rules list.""" - - def __init__(self, icon_cache=None, existing_apps=None, parent=None): - super().__init__(parent) - self.setWindowTitle(_("Add Application")) - self.setMinimumSize(500, 450) - self.selected_app = None - self._icon_cache = icon_cache or {} - self.existing_apps = set(existing_apps or []) - self._setup_ui() - self._load_running_apps() - - def _setup_ui(self): - layout = QVBoxLayout(self) - layout.setContentsMargins(20, 20, 20, 20) - layout.setSpacing(15) - - header_title = QLabel(_("Add Application")) - header_title.setStyleSheet("font-size: 18px; font-weight: bold;") - header_subtitle = QLabel(_("Assign a specific input mode to an application")) - header_subtitle.setStyleSheet("opacity: 0.7;") - - layout.addWidget(header_title) - layout.addWidget(header_subtitle) - - self.tabs = QTabWidget() - layout.addWidget(self.tabs) - - # Tab 1: Running Apps - self.running_tab = QWidget() - running_layout = QVBoxLayout(self.running_tab) - - search_layout = QHBoxLayout() - self.search_input = QLineEdit() - self.search_input.setPlaceholderText(_("Search process name...")) - self.search_input.textChanged.connect(self._filter_running_apps) - - self.btn_refresh = QPushButton(QIcon.fromTheme("view-refresh"), "") - self.btn_refresh.setToolTip(_("Refresh Process List")) - self.btn_refresh.setFlat(True) - self.btn_refresh.clicked.connect(self._load_running_apps) - - search_layout.addWidget(self.search_input, 1) - search_layout.addWidget(self.btn_refresh) - running_layout.addLayout(search_layout) - - self.running_list = QListWidget() - self.running_list.setIconSize(QSize(32, 32)) - self.running_list.itemClicked.connect(self._on_app_selected) - self.running_list.itemDoubleClicked.connect(self._on_item_double_clicked) - running_layout.addWidget(self.running_list) - - self.tabs.addTab(self.running_tab, _("Running")) - - # Tab 2: Manual Input - self.manual_tab = QWidget() - manual_layout = QVBoxLayout(self.manual_tab) - self.manual_input = QLineEdit() - self.manual_input.setPlaceholderText(_("Enter application name or path...")) - self.manual_input.returnPressed.connect(self._on_add_clicked) - manual_layout.addWidget(self.manual_input) - manual_layout.addStretch() - self.tabs.addTab(self.manual_tab, _("Manual input")) - - # Bottom Buttons - bottom_layout = QHBoxLayout() - self.selection_label = QLabel(_("No application selected")) - self.selection_label.setStyleSheet("opacity: 0.7;") - - self.btn_cancel = QPushButton(QIcon.fromTheme("dialog-cancel"), _("&Cancel")) - self.btn_cancel.clicked.connect(self.reject) - - self.btn_add = QPushButton(QIcon.fromTheme("dialog-ok"), _("&Add")) - self.btn_add.setObjectName("Primary") - self.btn_add.setEnabled(False) - self.btn_add.clicked.connect(self._on_add_clicked) - - bottom_layout.addWidget(self.selection_label) - bottom_layout.addStretch() - bottom_layout.addWidget(self.btn_cancel) - bottom_layout.addWidget(self.btn_add) - layout.addLayout(bottom_layout) - - def _load_running_apps(self): - """Loads running processes from /proc, filtered for user applications.""" - apps = [] - try: - current_uid = os.getuid() - for pid_dir in os.listdir("/proc"): - if not pid_dir.isdigit(): - continue - pid = int(pid_dir) - try: - # Filter by UID (only show current user's processes) - try: - stat_info = os.stat(f"/proc/{pid}") - if stat_info.st_uid != current_uid: - continue - except (PermissionError, FileNotFoundError): - continue - - with open(f"/proc/{pid}/comm", "r") as f: - name = f.read().strip() - with open(f"/proc/{pid}/cmdline", "r") as f: - cmdline = f.read().replace("\x00", " ").strip() - - if not cmdline: - continue - - exe = "" - try: - exe = os.readlink(f"/proc/{pid}/exe") - except (PermissionError, FileNotFoundError): - continue # Probably a kernel thread - - # Clean process names for NixOS - if name.startswith('.'): - exe_base = os.path.basename(exe) - if exe_base.startswith('.') and exe_base.endswith('-wrapped'): - name = exe_base[1:-8] - elif name.startswith('.' + exe_base) or name == ('.' + exe_base)[:15]: - name = exe_base - else: - clean = name[1:] - # On NixOS, wrapped application names from /proc//comm can be truncated. - # We check for partial suffixes of "-wrapped", from longest to shortest. - base_suffix = "-wrapped" - for i in range(len(base_suffix), 1, -1): - if clean.endswith(base_suffix[:i]): - clean = clean[:-i] - break - if clean: - name = clean - - # Exclude common system/background paths - exclude_paths = ["/usr/lib", "/usr/libexec", "/lib", "/systemd", "/usr/sbin"] - if any(exe.startswith(p) for p in exclude_paths): - continue - - # Heuristic: Exclude common background process patterns - # These processes run as user but are typically not "apps" for rules - bg_patterns = [ - "_agent", "_helper", "_daemon", "_resource", "_server", - "-agent", "-helper", "-daemon", "-sandbox", "-proxy", - "akonadi", "kactivitymanagerd", "kaccess", "krunner", - "ksmserver", "kwin_", "kglobalaccel", "org.kde.", - "gnome-shell", "dbus-", "at-spi", "pipewire", "pulseaudio", - "xdg-", "gvfs", "tracker-", "evolution-", "mission-control", - "telepathy", "dconf", "applet", "notify-osd", "indicator-", - "plasmashell", "xwayland", "wireplumber", "xsettingsd", - "xembedsniproxy", "gmenudbusmenuproxy", "kalendarac", - "ksystemstats", "ksecretd", "kwalletd", "kded", "startplasma", "bwrap" - ] - basename = os.path.basename(exe).lower() - if any(p in name.lower() or p in basename for p in bg_patterns): - continue - - # Exclude the settings-gui itself and python interpreters with no script - if ("main.py" in cmdline or "settings-gui" in cmdline) and "python" in exe: - continue - - if name in self.existing_apps: - continue - - # If it's a python command but unknown script, ignore it - if basename.startswith("python") and len(cmdline.split()) < 2: - continue - - if name and exe: - apps.append({"name": name, "exe": exe, "pid": pid}) - except (PermissionError, FileNotFoundError, ProcessLookupError): - continue - except Exception: - continue - except Exception as e: - print(f"Error listing /proc: {e}") - - unique_apps = {} - for app in apps: - key = app["exe"] - if key not in unique_apps: - unique_apps[key] = app - - sorted_apps = sorted(unique_apps.values(), key=lambda x: x["name"].lower()) - self.full_app_list = sorted_apps - self._populate_list(sorted_apps) - - def _populate_list(self, apps): - self.running_list.clear() - for app in apps: - item = QListWidgetItem() - item.setText(f"{app['name']}\n{app['exe']}") - item.setData(Qt.UserRole, app) - - icon_name = self._icon_cache.get(app["name"].lower()) - if not icon_name: - icon_name = self._icon_cache.get(os.path.basename(app["exe"]).lower(), app["name"].lower()) - - item.setIcon(QIcon.fromTheme(icon_name, QIcon.fromTheme("application-x-executable"))) - self.running_list.addItem(item) - - def _filter_running_apps(self, text): - filtered = [a for a in self.full_app_list if text.lower() in a["name"].lower() or text.lower() in a["exe"].lower()] - self._populate_list(filtered) - - def _on_app_selected(self, item): - app = item.data(Qt.UserRole) - self.selected_app = app["name"] - self.selection_label.setText(f"{_('Selected:')} {self.selected_app}") - self.btn_add.setEnabled(True) - - def _on_item_double_clicked(self, item): - self._on_app_selected(item) - self._on_add_clicked() - - def _on_add_clicked(self): - if self.tabs.currentIndex() == 1: # Manual - self.selected_app = self.manual_input.text().strip() - if not self.selected_app: - return - self.accept() - - -class ModeManagerPage(QWidget): - """Main Mode Manager page.""" - - def __init__(self, dbus_handler: LotusDBusHandler, parent=None): - super().__init__(parent) - self.dbus = dbus_handler - self.app_rules = {} - self.original_app_rules = {} - self.original_global_mode = "" - self.selected_app = None - self.current_app_mode = MODE_DEFAULT - self._icon_cache = {} - self._setup_ui() - self.load_data() - - def _setup_ui(self): - self.layout = QHBoxLayout(self) - self.layout.setContentsMargins(0, 0, 0, 0) - self.layout.setSpacing(0) - - # Left Sidebar (Expanded) - self.sidebar_widget = QWidget() - self.sidebar_widget.setFixedWidth(240) - self.sidebar_layout = QVBoxLayout(self.sidebar_widget) - self.sidebar_layout.setContentsMargins(15, 20, 15, 20) - self.app_search = QLineEdit() - self.app_search.setPlaceholderText(_("Search applications...")) - self.app_search.textChanged.connect(self._filter_apps) - self.sidebar_layout.addWidget(self.app_search) - - self.sidebar_layout.setSpacing(10) - - self.app_list = QListWidget() - self.app_list.setIconSize(QSize(24, 24)) - self.app_list.itemClicked.connect(self._on_app_selected) - self.sidebar_layout.addWidget(self.app_list) - - self.btn_add_app = QPushButton(QIcon.fromTheme("list-add"), _("Add Application")) - self.btn_remove_app = QPushButton(QIcon.fromTheme("list-remove"), _("Remove")) - self.btn_remove_app.setEnabled(False) - - self.btn_add_app.clicked.connect(self._on_add_app) - self.btn_remove_app.clicked.connect(self._on_remove_app) - - self.sidebar_layout.addWidget(self.btn_add_app) - self.sidebar_layout.addWidget(self.btn_remove_app) - - self.layout.addWidget(self.sidebar_widget) - - # Right Content Area - self.content_widget = QScrollArea() - self.content_widget.setWidgetResizable(True) - self.content_widget.setFrameShape(QFrame.NoFrame) - - self.main_container = QWidget() - self.main_layout = QVBoxLayout(self.main_container) - self.main_layout.setContentsMargins(30, 20, 30, 30) - self.main_layout.setSpacing(20) - - title = QLabel(_("Applications")) - title.setObjectName("CategoryTitle") - self.main_layout.addWidget(title) - - # 1. Global Mode Section (Simplified Card) - self.global_card = CardWidget("") - global_layout = QHBoxLayout() - global_layout.addWidget(QLabel(_("Global Default Mode:"))) - self.combo_global_mode = QComboBox() - global_modes = [ - MODE_OFF, MODE_SMOOTH, MODE_SLOW, MODE_HARDCORE, - MODE_SURROUNDING, MODE_PREEDIT, MODE_EMOJI - ] - for m in global_modes: - self.combo_global_mode.addItem(_(MODE_INFO[m]["title"]), MODE_INFO[m]["title"]) - - self.combo_global_mode.currentIndexChanged.connect(self._on_global_mode_changed) - global_layout.addWidget(self.combo_global_mode) - self.global_card.content_layout.addLayout(global_layout) - self.main_layout.addWidget(self.global_card) - - # 2. Selected App Card (Empty Title) - self.app_settings_card = CardWidget("") - self.app_settings_layout = QVBoxLayout() - - # App Info Header - self.app_header_layout = QHBoxLayout() - self.app_icon_label = QLabel() - self.app_icon_label.setFixedSize(48, 48) - self.app_name_label = QLabel(_("Select an application")) - self.app_name_label.setStyleSheet("font-size: 16px; font-weight: bold;") - self.app_header_layout.addWidget(self.app_icon_label) - self.app_header_layout.addWidget(self.app_name_label) - self.app_header_layout.addStretch() - self.app_settings_layout.addLayout(self.app_header_layout) - self.app_settings_layout.addSpacing(10) - - # Grid for App Modes - self.mode_grid = QGridLayout() - self.mode_grid.setSpacing(10) - self.mode_cards = {} - - grid_modes = [ - MODE_DEFAULT, MODE_OFF, - MODE_SMOOTH, MODE_SLOW, - MODE_HARDCORE, MODE_SURROUNDING, - MODE_PREEDIT, MODE_EMOJI - ] - for i, m in enumerate(grid_modes): - card = ModeCard(m) - card.clicked.connect(self._on_app_mode_changed) - self.mode_cards[m] = card - self.mode_grid.addWidget(card, i // 2, i % 2) - - self.app_settings_layout.addLayout(self.mode_grid) - self.app_settings_card.content_layout.addLayout(self.app_settings_layout) - self.main_layout.addWidget(self.app_settings_card) - self.main_layout.addStretch() - - # Initial visibility - self.app_settings_card.setVisible(False) - - self.content_widget.setWidget(self.main_container) - self.layout.addWidget(self.content_widget) - - def load_data(self): - """Loads rules from config and global mode from DBus.""" - self.app_rules = {} - try: - data = self.dbus.get_sub_config_list("app_rules", "Rules") - for item in data: - app = item.get("App", "") - mode = int(item.get("Mode", 0)) - if app: - self.app_rules[app] = mode - except Exception as e: - print(f"Error loading app rules via DBus: {e}") - - # Sync Global Mode - config = self.dbus.get_config() - mode_str = config.get("values", {}).get("Mode", "Uinput (Smooth)") - self.combo_global_mode.blockSignals(True) - idx = self.combo_global_mode.findData(mode_str) - if idx >= 0: - self.combo_global_mode.setCurrentIndex(idx) - self.combo_global_mode.blockSignals(False) - self.original_global_mode = mode_str - - self._populate_app_list() - self.original_app_rules = self.app_rules.copy() - - def _populate_app_list(self): - self.app_list.clear() - self._scan_desktop_files() - apps_to_show = set(self.app_rules.keys()) - if self.selected_app: - apps_to_show.add(self.selected_app) - - for app in sorted(apps_to_show): - mode = self.app_rules.get(app, MODE_DEFAULT) - mode_text = _(MODE_INFO.get(mode, MODE_INFO[MODE_SMOOTH])["title"]) - - item = QListWidgetItem() - item.setText(f"{app}\n{mode_text}") - item.setData(Qt.UserRole, (app, mode)) - item.setIcon(self._resolve_icon(app)) - self.app_list.addItem(item) - - if app == self.selected_app: - self.app_list.setCurrentItem(item) - - def _scan_desktop_files(self): - """Builds a robust map of app identifiers to icon names.""" - search_paths = [ - "/usr/share/applications", - os.path.expanduser("~/.local/share/applications"), - "/var/lib/flatpak/exports/share/applications", - os.path.expanduser("~/.local/share/flatpak/exports/share/applications"), - "/var/lib/snapd/desktop/applications", - ] - - xdg_data_dirs = os.environ.get("XDG_DATA_DIRS", "").split(":") - for directory in xdg_data_dirs: - if directory: - xdg_applications_path = os.path.join(directory, "applications") - if xdg_applications_path not in search_paths: - search_paths.append(xdg_applications_path) - - # Priority 1: Direct binary names from Exec line - # Priority 2: Desktop filenames (e.g. com.discordapp.Discord -> Discord) - # Priority 3: Application Names - - for p in search_paths: - if not os.path.isdir(p): - continue - for f in os.listdir(p): - if not f.endswith(".desktop"): - continue - try: - desktop_id = f[:-8] # remove .desktop - with open(os.path.join(p, f), "r", encoding="utf-8") as df: - content = df.read() - - icon_match = re.search(r"^Icon=([^\n]+)", content, re.MULTILINE) - if not icon_match: continue - icon = icon_match.group(1).strip() - - # Map by desktop ID (e.g. discord) - self._icon_cache[desktop_id.lower()] = icon - if "." in desktop_id: # handle com.discordapp.Discord - self._icon_cache[desktop_id.split(".")[-1].lower()] = icon - - name_match = re.search(r"^Name=([^\n]+)", content, re.MULTILINE) - if name_match: - self._icon_cache[name_match.group(1).strip().lower()] = icon - - exec_match = re.search(r"^Exec=([^\n]+)", content, re.MULTILINE) - if exec_match: - exec_line = exec_match.group(1).strip() - # Robustly extract binary name (handle quotes and arguments) - if exec_line.startswith('"'): - binary_path = exec_line[1:].split('"')[0] - else: - binary_path = exec_line.split(" ")[0] - - binary_name = os.path.basename(binary_path).lower() - self._icon_cache[binary_name] = icon - - if binary_name == "flatpak" and " --command=" in exec_line: - cmd_match = re.search(r"--command=([^ ]+)", exec_line) - if cmd_match: - self._icon_cache[cmd_match.group(1).lower()] = icon - except (PermissionError, FileNotFoundError): - continue - except Exception as e: - # Only log truly unexpected errors - if not isinstance(e, (UnicodeDecodeError, re.error)): - print(f"Error parsing desktop file {f}: {e}") - continue - - # Manual Overrides for stubborn apps - manual_icons = { - "discord": "discord", - "upnote": "upnote", - "antigravity": "preferences-desktop-accessibility", - } - for k, v in manual_icons.items(): - if k not in self._icon_cache: - self._icon_cache[k] = v - - def _resolve_icon(self, app_name): - """Resolves icon name for a given app handle.""" - app_lower = app_name.lower() - icon_name = self._icon_cache.get(app_lower) - - if not icon_name: - # Try removing extension or path if it's a full path - basename = os.path.basename(app_name).lower() - if "." in basename: - basename = basename.split(".")[0] - icon_name = self._icon_cache.get(basename, basename) - - return QIcon.fromTheme(icon_name, QIcon.fromTheme("application-x-executable")) - - def _filter_apps(self, text): - for i in range(self.app_list.count()): - item = self.app_list.item(i) - item.setHidden(text.lower() not in item.text().split("\n")[0].lower()) - - def _on_app_selected(self, item): - app_name, mode = item.data(Qt.UserRole) - self.selected_app = app_name - self.current_app_mode = mode - - self.app_name_label.setText(app_name) - self.app_icon_label.setPixmap(self._resolve_icon(app_name).pixmap(48, 48)) - self.app_settings_card.setVisible(True) - self.btn_remove_app.setEnabled(True) # Enable Remove - self._update_mode_cards() - - def _on_global_mode_changed(self, index): - if not self.isVisible(): - return - - self._notify_changed() - - - def _on_app_mode_changed(self, mode): - self.current_app_mode = mode - if mode == MODE_DEFAULT: - if self.selected_app in self.app_rules: - del self.app_rules[self.selected_app] - else: - self.app_rules[self.selected_app] = mode - - self._update_mode_cards() - self._populate_app_list() - self._notify_changed() - - def _update_mode_cards(self): - for m, card in self.mode_cards.items(): - card.selected = (m == self.current_app_mode) - card.update_style() - - def _on_add_app(self): - dialog = AddAppDialog(self._icon_cache, list(self.app_rules.keys()), self) - if dialog.exec(): - new_app = dialog.selected_app - if new_app not in self.app_rules: - self.app_rules[new_app] = MODE_SMOOTH - self.selected_app = new_app - self._populate_app_list() - self._notify_changed() - - def _on_remove_app(self): - if not self.selected_app: - return - - reply = QMessageBox.question( - self, _("Confirm Remove"), - _("Are you sure you want to remove rules for this application?"), - QMessageBox.Yes | QMessageBox.No - ) - if reply == QMessageBox.No: - return - - if self.selected_app in self.app_rules: - del self.app_rules[self.selected_app] - - self.selected_app = None - self.app_settings_card.setVisible(False) - self.btn_remove_app.setEnabled(False) - self._populate_app_list() - self._notify_changed() - - def _notify_changed(self): - main_win = self.window() - if hasattr(main_win, "on_changed"): - main_win.on_changed() - - def is_modified(self): - """Returns True if the current state differs from the initial loaded state.""" - return ( - self.app_rules != self.original_app_rules - or self.combo_global_mode.currentData() != self.original_global_mode - ) - - def is_modified_from_default(self): - """Returns True if the current state differs from the default state.""" - return ( - len(self.app_rules) > 0 - or self.combo_global_mode.currentData() != "Uinput (Smooth)" - ) - - def do_import(self): - """Imports app rules from a TSV file.""" - path, _filter = QFileDialog.getOpenFileName( - self, - _("Import Application Rules"), - "", - _("Tab-separated (*.tsv);;Text files (*.txt);;All files (*)"), - ) - if not path: - return - - try: - with open(path, "r", encoding="utf-8") as f: - lines = f.readlines() - except Exception as e: - QMessageBox.warning(self, _("Error"), f"{_('Cannot open file for reading:')} {e}") - return - - imported = skipped = 0 - confirmed = False - for line in lines: - line = line.strip() - if not line or line.startswith("#"): - continue - parts = line.split("\t") if "\t" in line else line.split(",") - if len(parts) < 2: - skipped += 1 - continue - app, mode_str = parts[0].strip(), parts[1].strip() - if not app or not mode_str: - skipped += 1 - continue - - try: - mode = int(mode_str) - if mode not in MODE_INFO and mode != MODE_DEFAULT: - skipped += 1 - continue - except ValueError: - skipped += 1 - continue - - if not confirmed and self.app_rules: - reply = QMessageBox.question( - self, - _("Confirm Import"), - _("Merge imported application rules?"), - QMessageBox.Yes | QMessageBox.No, - ) - if reply == QMessageBox.No: - return - confirmed = True - else: - confirmed = True - - self.app_rules[app] = mode - imported += 1 - - if imported > 0: - self._populate_app_list() - self._notify_changed() - - QMessageBox.information( - self, - _("Import Complete"), - _("Imported {} rules, skipped {} invalid lines.").format(imported, skipped), - ) - - def do_export(self): - """Exports current app rules to a TSV file.""" - if not self.app_rules: - QMessageBox.information( - self, _("Export"), _("The application rules list is empty, nothing to export.") - ) - return - - path, _filter = QFileDialog.getSaveFileName( - self, - _("Export Application Rules"), - "lotus-app-rules.tsv", - _("Tab-separated (*.tsv);;Text files (*.txt);;All files (*)"), - ) - if not path: - return - - try: - with open(path, "w", encoding="utf-8") as f: - f.write("# Lotus Application Rules Table\n") - f.write("# Format: application_namemode_id\n") - f.write("# Modes: 0=Off, 1=Uinput(Smooth), 2=Uinput(Slow), 3=Uinput(Hardcore), 4=Surrounding, 5=Preedit, 6=Emoji\n") - for app, mode in sorted(self.app_rules.items()): - f.write(f"{app}\t{mode}\n") - QMessageBox.information( - self, - _("Export Complete"), - _("Exported {} rules to:\n{}").format(len(self.app_rules), path), - ) - except Exception as e: - QMessageBox.warning(self, _("Error"), f"{_('Cannot open file for writing:')} {e}") - - def save_data(self): - try: - if self.combo_global_mode.currentData() != self.original_global_mode: - config_data = self.dbus.get_config() - if config_data: - latest_values = config_data.get("values", {}) - latest_values["Mode"] = self.combo_global_mode.currentData() - self.dbus.set_config(latest_values) - - data = [] - for app, mode in sorted(self.app_rules.items()): - data.append({"App": app, "Mode": str(mode)}) - - self.dbus.set_sub_config_list("app_rules", "Rules", data) - self.original_app_rules = self.app_rules.copy() - self.original_global_mode = self.combo_global_mode.currentData() - - except Exception as e: - print(f"Error saving app rules via DBus: {e}") - - def restore_defaults(self): - self.load_data() \ No newline at end of file diff --git a/settings-gui/version.py.in b/settings-gui/version.py.in deleted file mode 100644 index d9a9a9ac..00000000 --- a/settings-gui/version.py.in +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nguyen Hoang Ky -# -# SPDX-License-Identifier: GPL-3.0-or-later -""" -Auto-generated version file by CMake. -""" - -__version__ = "@PROJECT_VERSION@ (Stable)" From 2000c3e727ad5e33dcd11c40afa0f1eaefdcc613 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 14:00:08 +0700 Subject: [PATCH 28/42] fix SIGSEGV Signed-off-by: Zebra2711 --- src/lotus-engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 9c6310c5..210d2cbe 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -120,7 +120,7 @@ namespace fcitx { appRulesPath_ = configDir + "/lotus-app-rules.conf"; loadAppRules(); toggleActions_ = { - charsetAction_.get(), spellCheckAction_.get(), macroAction_.get(), capitalizeMacroAction_.get(), + spellCheckAction_.get(), macroAction_.get(), capitalizeMacroAction_.get(), autoNonVnRestoreAction_.get(), settingsAction_.get()}; } From 54278e17a2610922ead61acecb60eca199176c5f Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 14:14:19 +0700 Subject: [PATCH 29/42] rm stuff Signed-off-by: Zebra2711 --- src/lotus-config.h | 2 +- src/lotus-engine.cpp | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/lotus-config.h b/src/lotus-config.h index 1799e8e9..f91cc691 100644 --- a/src/lotus-config.h +++ b/src/lotus-config.h @@ -218,7 +218,7 @@ namespace fcitx { this, "InputMethod", _("Input Method"), "Telex", InputMethodConstrain(&inputMethod), {}, InputMethodAnnotation()}; OptionWithAnnotation outputCharset{this, "OutputCharset", _("Output Charset"), "Unicode", {}, {}, StringListAnnotation()}; Option spellCheck{this, "SpellCheck", _("Enable Spell Check"), true}; Option enableMacro{this, "EnableMacro", _("Enable Macro"), true}; - Option autoSaveNewAppRules{this, "autoSaveNewAppRules", _("Auto Save"), false}; Option capitalizeMacro{this, "CapitalizeMacro", _("Capitalize Macro"), true}; + Option capitalizeMacro{this, "CapitalizeMacro", _("Capitalize Macro"), true}; Option autoCapitalizeAfterPunctuation{this, "AutoCapitalizeAfterPunctuation", _("Auto capitalize after sentence-ending punctuation (. ! ? Enter) (experimental)"), false}; Option doubleSpaceToPeriod{this, "DoubleSpaceToPeriod", _("Double Space to Period (experimental)"), false}; diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 210d2cbe..6e08125a 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -689,23 +689,6 @@ namespace fcitx { } const auto globalMode = modeStringToEnum(config_.mode.value()); - - // auto save new app rule from global mode - if (config_.autoSaveNewAppRules.value() && false) { - auto rules = *appRulesTables_.rules; - - lotusAppRule newRule; - newRule.app.setValue(appName); - newRule.mode.setValue(static_cast(globalMode)); - - rules.push_back(std::move(newRule)); - - appRules_[appName] = globalMode; - appRulesTables_.rules.setValue(std::move(rules)); - - saveAppRules(); - } - return globalMode; } From 7d89d99464cfe6d2144b5a74063872fcdac0acb8 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 16:02:42 +0700 Subject: [PATCH 30/42] ee Signed-off-by: Zebra2711 --- src/lotus-engine.cpp | 36 ++++------ src/lotus-state.cpp | 164 +++++++++---------------------------------- src/lotus-utils.cpp | 3 +- src/lotus-utils.h | 2 +- 4 files changed, 47 insertions(+), 158 deletions(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 6e08125a..f344be0d 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -64,17 +64,13 @@ namespace fcitx { static inline std::vector convertToStringList(char** list) { std::vector result; - if (list == nullptr) - return result; - size_t count = 0; - while (list[count] != nullptr) - ++count; //NOLINT - result.reserve(count); - for (size_t i = 0; i < count; ++i) - result.emplace_back(list[i]); //NOLINT - for (size_t i = 0; i < count; ++i) - free(list[i]); //NOLINT - free(list); //NOLINT + if (list != nullptr) { + for (size_t i = 0; list[i] != nullptr; ++i) { //NOLINT + result.emplace_back(list[i]); //NOLINT + free(list[i]); //NOLINT + } + free(list); //NOLINT + } return result; } @@ -335,9 +331,11 @@ namespace fcitx { } else { LOTUS_INFO("inputPanel reset"); ic->inputPanel().reset(); - ic->updateUserInterface(UserInterfaceComponent::InputPanel); - if (realMode == LotusMode::Preedit) + if (realMode == LotusMode::Preedit + || realMode == LotusMode::SurroundingText) { + ic->updateUserInterface(UserInterfaceComponent::InputPanel); ic->updatePreedit(); + } } for (const auto& action : toggleActions_) { statusArea.addAction(StatusGroup::InputMethod, action); @@ -723,19 +721,15 @@ namespace fcitx { void LotusEngine::showAppModeMenu(InputContext* ic) { isSelectingAppMode_ = true; - auto candidateList = std::make_unique(); - candidateList->setLayoutHint(CandidateLayoutHint::Vertical); candidateList->setPageSize(10); - auto getLabel = [&](const LotusMode& modeName, const std::string& modeLabel) { if (modeName == realMode) { return Text(">> " + modeLabel); } return Text(" " + modeLabel); }; - auto cleanup = [this](InputContext* ic) { isSelectingAppMode_ = false; ic->inputPanel().reset(); @@ -744,16 +738,13 @@ namespace fcitx { state->commitBuffer(); state->reset(); }; - auto applyMode = [this, cleanup](LotusMode mode) { return [this, mode, cleanup](InputContext* ic) { if (mode != LotusMode::Emoji) { setAppRule(currentConfigureApp_, mode); - if (!isStartsWith(currentConfigureApp_, "ctx_")) { + if (!isStartsWith(currentConfigureApp_, "ctx_")) saveAppRules(); - } } - cleanup(ic); setMode(mode, ic); if (mode == LotusMode::Emoji) { @@ -843,9 +834,8 @@ namespace fcitx { } std::string LotusEngine::getProgramName(InputContext* ic) { - if (ic == nullptr) { + if (ic == nullptr) return "unknown-app"; - } std::string programName = ic->program(); if (programName.empty() || programName == "wayland" || programName == "x11") { // Fallback: InputContext address-based resolution diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index 460998cb..c12af415 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -51,7 +51,6 @@ namespace fcitx { inline void update_max(std::atomic& value, uint32_t target) { uint32_t current = value.load(std::memory_order_acquire); - asm volatile("1:\n\t" "cmpl %[target], %[current]\n\t" "jae 2f\n\t" @@ -71,7 +70,6 @@ namespace fcitx { inputBackend_.reset(); inputBackend_ = makeLotusInputBackend(); realMode = modeStringToEnum(engine_->config().mode.value()); - inputBackend_->recreateEngine(engine_); setOption(); } @@ -91,10 +89,8 @@ namespace fcitx { LOTUS_ERROR("Failed to create socket: " + std::string(strerror(errno))); return false; } - struct sockaddr_un addr{}; addr.sun_family = AF_UNIX; - addr.sun_path[0] = '\0'; memcpy(&addr.sun_path[1], current_path.c_str(), current_path.length()); socklen_t len = offsetof(struct sockaddr_un, sun_path) + current_path.length() + 1; @@ -105,9 +101,8 @@ namespace fcitx { } LOTUS_ERROR("Failed to connect to socket: " + std::string(strerror(errno))); int old_fd = uinput_client_fd_.exchange(-1); - if (old_fd != -1) { + if (old_fd != -1) close(old_fd); - } return false; } @@ -120,9 +115,7 @@ namespace fcitx { LOTUS_ERROR("Cannot send backspace since cannot connect to uinput server"); return; } - ssize_t n = send(uinput_client_fd_, &count, sizeof(count), MSG_NOSIGNAL); - if (n < 0) { LOTUS_WARN("Failed to send backspace: " + std::string(strerror(errno))); int old_fd = uinput_client_fd_.exchange(-1); @@ -158,19 +151,15 @@ namespace fcitx { ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), false); ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), true); } - //send_backspace_uinput(0); // trigger 1bs to make all bs prev release } bool LotusState::isAutofillCertain(const SurroundingText& s) { - if (!s.isValid() || oldPreBuffer_.empty()) { + if (!s.isValid() || oldPreBuffer_.empty()) return false; - } - const unsigned int cursor = s.cursor(); const unsigned int anchor = s.anchor(); const auto& text = s.text(); const size_t cursor_sz = static_cast(cursor); - // Fix that surrounding text is delay update const size_t buffLen = #if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) @@ -188,7 +177,6 @@ namespace fcitx { LOTUS_INFO("check suggest wayland"); unsigned int selectionStart = std::min(anchor, cursor); unsigned int selectionEnd = std::max(anchor, cursor); - // Only consider it browser autofill if the selection starts at the cursor // and extends to the end of the line (common address bar behavior). if (cursor <= selectionEnd) { @@ -205,7 +193,6 @@ namespace fcitx { return p == std::string::npos || p >= static_cast(selectionEnd); } } - const size_t textLen = #if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) utf8_length_avx512(text.data(), text.size()); @@ -216,7 +203,6 @@ namespace fcitx { realtextLen.store(textLen, std::memory_order_release); return false; } - // Heuristic: rapid text growth in a single-line context. // Applied only when no newline is present after the cursor to distinguish from AI text in editors. // Check for wayland app that use dbus as backend @@ -262,15 +248,11 @@ namespace fcitx { } void LotusState::updateEmojiPageStatus(CommonCandidateList* commonList) { - if ((commonList == nullptr) || commonList->empty()) { + if ((commonList == nullptr) || commonList->empty()) return; - } - int pageSize = commonList->pageSize(); - if (pageSize <= 0) { + if (pageSize <= 0) pageSize = 9; - } - int totalItems = commonList->totalSize(); int currentPage = commonList->currentPage() + 1; int totalPages = (totalItems + pageSize - 1) / pageSize; @@ -613,38 +595,14 @@ namespace fcitx { } switch (currentSym) { - case FcitxKey_KP_Add: { - currentSym = FcitxKey_plus; - break; - } - case FcitxKey_KP_Subtract: { - currentSym = FcitxKey_minus; - break; - } - case FcitxKey_KP_Divide: { - currentSym = FcitxKey_slash; - break; - } - case FcitxKey_KP_Multiply: { - currentSym = FcitxKey_asterisk; - break; - } - case FcitxKey_KP_Decimal: { - currentSym = FcitxKey_period; - break; - } - case FcitxKey_KP_Enter: { - currentSym = FcitxKey_Return; - break; - } - case FcitxKey_KP_Equal: { - currentSym = FcitxKey_equal; - break; - } - case FcitxKey_KP_Space: { - currentSym = FcitxKey_space; - break; - } + case FcitxKey_KP_Add: currentSym = FcitxKey_plus; break; + case FcitxKey_KP_Subtract: currentSym = FcitxKey_minus; break; + case FcitxKey_KP_Divide: currentSym = FcitxKey_slash; break; + case FcitxKey_KP_Multiply: currentSym = FcitxKey_asterisk; break; + case FcitxKey_KP_Decimal: currentSym = FcitxKey_period; break; + case FcitxKey_KP_Enter: currentSym = FcitxKey_Return; break; + case FcitxKey_KP_Equal: currentSym = FcitxKey_equal; break; + case FcitxKey_KP_Space: currentSym = FcitxKey_space; break; default: break; } return false; @@ -686,10 +644,9 @@ namespace fcitx { bool processed = inputBackend_->processKeyEventAndPull(currentSym, keyEvent.rawKey().states(), &commitStr, &preeditStrBuf); if (!commitStr.empty()) { - std::string commonPrefix; std::string deletedPart; std::string addedPart; - compareAndSplitStrings(oldPreBuffer_, commitStr, commonPrefix, deletedPart, addedPart); + compareAndSplitStrings(oldPreBuffer_, commitStr, deletedPart, addedPart); if (!deletedPart.empty()) { keyEvent.filterAndAccept(); @@ -737,14 +694,13 @@ namespace fcitx { std::string preeditStr = preeditStrBuf; - std::string commonPrefix; std::string deletedPart; std::string addedPart; if (wa_flag) keyEvent.filterAndAccept(); - if (compareAndSplitStrings(oldPreBuffer_, preeditStr, commonPrefix, deletedPart, addedPart) != 0) { + if (compareAndSplitStrings(oldPreBuffer_, preeditStr, deletedPart, addedPart) != 0) { if (deletedPart.empty()) { bool isCommit = false; bool wasAutoCapitalized = (currentSym != keyEvent.rawKey().sym()); @@ -880,44 +836,31 @@ namespace fcitx { inputBackend_->resetEngine(); return; } - std::string commitPart; std::string preeditPart; inputBackend_->pullCommitAndPreedit(&commitPart, &preeditPart); - std::string newWord; if (!commitPart.empty()) newWord += commitPart; if (!preeditPart.empty()) newWord += preeditPart; - - std::string commonPrefix; std::string deletedPart; std::string addedPart; - compareAndSplitStrings(oldWord, newWord, commonPrefix, deletedPart, addedPart); + compareAndSplitStrings(oldWord, newWord, deletedPart, addedPart); if (deletedPart.empty() && addedPart == keyEvent.key().toString()) { inputBackend_->resetEngine(); keyEvent.forward(); return; } - if (!deletedPart.empty() || !addedPart.empty()) { size_t charsToDelete = utf8::length(deletedPart); - - if (charsToDelete > 0) { + if (charsToDelete > 0) ic->deleteSurroundingText(-static_cast(charsToDelete), static_cast(charsToDelete)); - } - if (!addedPart.empty()) { ic->commitString(addedPart); LOTUS_INFO("Commit: " + addedPart); } - - inputBackend_->resetEngine(); - keyEvent.filterAndAccept(); - return; } - inputBackend_->resetEngine(); keyEvent.filterAndAccept(); return; @@ -936,12 +879,10 @@ namespace fcitx { out += commitPart; if (!preeditPart.empty()) out += preeditPart; - if (!out.empty()) { LOTUS_INFO("Commit: " + out); ic->commitString(out); } - inputBackend_->resetEngine(); keyEvent.filterAndAccept(); } else { @@ -1045,9 +986,8 @@ namespace fcitx { } break; default: - if (currentSym != FcitxKey_space) { + if (currentSym != FcitxKey_space) isPrevPunctuation_ = false; - } break; } } @@ -1086,30 +1026,11 @@ namespace fcitx { } switch (realMode) { - case LotusMode::Uinput: - case LotusMode::Smooth: { - handleUinputMode(keyEvent, currentSym); - break; - } - case LotusMode::UinputWine: { - handleUinputMode(keyEvent, currentSym); - break; - } - case LotusMode::SurroundingText: { - handleSurroundingText(keyEvent, currentSym); - break; - } - case LotusMode::Preedit: { - handlePreeditMode(keyEvent, currentSym); - break; - } - case LotusMode::Emoji: { - handleEmojiMode(keyEvent); - break; - } - default: { - break; - } + case LotusMode::Uinput: case LotusMode::Smooth: case LotusMode::UinputWine: handleUinputMode(keyEvent, currentSym); break; + case LotusMode::SurroundingText: handleSurroundingText(keyEvent, currentSym); break; + case LotusMode::Preedit: handlePreeditMode(keyEvent, currentSym); break; + case LotusMode::Emoji: handleEmojiMode(keyEvent); break; + default: break; } } @@ -1123,10 +1044,8 @@ namespace fcitx { utf8::length(text); #endif realtextLen.store(textLen, std::memory_order_release); - if (is_deleting_.load(std::memory_order_acquire)) { + if (is_deleting_.load(std::memory_order_acquire)) return; - } - if (inputBackend_) { isPrevSpace_ = false; shouldCapitalize_ = false; @@ -1144,34 +1063,25 @@ namespace fcitx { } if (getFrontendName(ic_) != "dbus") clearAllBuffers(); - + if (realMode == LotusMode::Off) + return; + ic_->inputPanel().reset(); switch (realMode) { - case LotusMode::Preedit: { - ic_->inputPanel().reset(); - ic_->updateUserInterface(UserInterfaceComponent::InputPanel); - ic_->updatePreedit(); - break; - } - case LotusMode::SurroundingText: - case LotusMode::Uinput: - case LotusMode::UinputWine: - case LotusMode::Smooth: { - ic_->inputPanel().reset(); - break; - } + case LotusMode::Preedit: case LotusMode::Emoji: { - ic_->inputPanel().reset(); ic_->updateUserInterface(UserInterfaceComponent::InputPanel); ic_->updatePreedit(); break; } - default: { - break; - } + default: break; } } void LotusState::commitBuffer() { + if (realMode == LotusMode::Off) + return; + if (inputBackend_) + inputBackend_->resetEngine(); switch (realMode) { case LotusMode::Preedit: { ic_->inputPanel().reset(); @@ -1181,21 +1091,11 @@ namespace fcitx { inputBackend_->pullCommit(&commit); if (!commit.empty()) ic_->commitString(commit); - inputBackend_->resetEngine(); } ic_->updateUserInterface(UserInterfaceComponent::InputPanel); ic_->updatePreedit(); break; } - case LotusMode::Uinput: - case LotusMode::UinputWine: - case LotusMode::Smooth: - case LotusMode::SurroundingText: { - if (inputBackend_) { - inputBackend_->resetEngine(); - } - break; - } default: { break; } diff --git a/src/lotus-utils.cpp b/src/lotus-utils.cpp index 40a8624e..85df24aa 100644 --- a/src/lotus-utils.cpp +++ b/src/lotus-utils.cpp @@ -55,7 +55,7 @@ bool isBackspace(uint32_t sym) { return sym == 65288 || sym == 8 || sym == FcitxKey_BackSpace; } -int compareAndSplitStrings(const std::string& A, const std::string& B, std::string& commonPrefix, std::string& deletedPart, std::string& addedPart) { +int compareAndSplitStrings(const std::string& A, const std::string& B, std::string& deletedPart, std::string& addedPart) { size_t i = 0; size_t j = 0; #if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) @@ -81,7 +81,6 @@ int compareAndSplitStrings(const std::string& A, const std::string& B, std::stri } } #endif - commonPrefix.assign(A, 0, i); deletedPart.assign(A, i); addedPart.assign(B, j); return (deletedPart.empty() && addedPart.empty()) ? 1 : 2; diff --git a/src/lotus-utils.h b/src/lotus-utils.h index 753935c1..5cb20659 100644 --- a/src/lotus-utils.h +++ b/src/lotus-utils.h @@ -98,7 +98,7 @@ bool isBackspace(uint32_t sym); * @param addedPart Output added portion. * @return Comparison result code. */ -int compareAndSplitStrings(const std::string& A, const std::string& B, std::string& commonPrefix, std::string& deletedPart, std::string& addedPart); +int compareAndSplitStrings(const std::string& A, const std::string& B, std::string& deletedPart, std::string& addedPart); /** * @brief Checks if string starts with prefix. From 595e2ae212ef05433d34f2d72aa069cf6711e3c4 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 16:39:42 +0700 Subject: [PATCH 31/42] compact --- src/lotus-state.cpp | 415 ++++++++++---------------------------------- 1 file changed, 92 insertions(+), 323 deletions(-) diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index c12af415..cf528b13 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -12,7 +12,6 @@ #include "lotus-utils.h" #include "lotus-input-backend.hpp" #include "lotus.h" - #include #include #include @@ -20,23 +19,16 @@ #include #include #include - #include #include #include #include - #include - namespace fcitx { constexpr int MAX_SCAN_LENGTH = 15; - static inline bool isWordBreak(uint32_t ucs4) { - if (__builtin_expect(ucs4 > 64, 1)) - return false; - if (__builtin_expect(ucs4 == 64, 0)) - return true; // '@' - // btq: single-cycle bit-test (Linux kernel bitmap technique); replaces 6-branch chain. + if (__builtin_expect(ucs4 > 64, 1)) return false; + if (__builtin_expect(ucs4 == 64, 0)) return true; // '@' // Bits set: NUL(0) TAB(9) LF(10) CR(13) SPC(32) :;<=>?(58-63) static constexpr uint64_t kMask = (1ULL << 0) | (1ULL << 9) | (1ULL << 10) | (1ULL << 13) | (1ULL << 32) | (1ULL << 58) | (1ULL << 59) | (1ULL << 60) | (1ULL << 61) | (1ULL << 62) | (1ULL << 63); @@ -48,7 +40,6 @@ namespace fcitx { : "cc"); return r; } - inline void update_max(std::atomic& value, uint32_t target) { uint32_t current = value.load(std::memory_order_acquire); asm volatile("1:\n\t" @@ -61,11 +52,7 @@ namespace fcitx { : [target] "r"(target) : "memory"); } - - LotusState::LotusState(LotusEngine* engine, InputContext* ic) : engine_(engine), ic_(ic) { - setEngine(); - } - + LotusState::LotusState(LotusEngine* engine, InputContext* ic) : engine_(engine), ic_(ic) { setEngine(); } void LotusState::setEngine() { inputBackend_.reset(); inputBackend_ = makeLotusInputBackend(); @@ -73,16 +60,9 @@ namespace fcitx { inputBackend_->recreateEngine(engine_); setOption(); } - - void LotusState::setOption() { - if (!inputBackend_) - return; - inputBackend_->setOptions(engine_); - } - + void LotusState::setOption() { if (!inputBackend_) return; inputBackend_->setOptions(engine_); } bool LotusState::connect_uinput_server() { - if (uinput_client_fd_ >= 0) - return true; + if (uinput_client_fd_ >= 0) return true; const std::string current_path = buildSocketPath("kb_socket"); int current_fd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0); if (current_fd < 0) { @@ -94,22 +74,16 @@ namespace fcitx { addr.sun_path[0] = '\0'; memcpy(&addr.sun_path[1], current_path.c_str(), current_path.length()); socklen_t len = offsetof(struct sockaddr_un, sun_path) + current_path.length() + 1; - if (connect(current_fd, (struct sockaddr*)&addr, len) == 0) { uinput_client_fd_ = current_fd; return true; } LOTUS_ERROR("Failed to connect to socket: " + std::string(strerror(errno))); int old_fd = uinput_client_fd_.exchange(-1); - if (old_fd != -1) - close(old_fd); + if (old_fd != -1) close(old_fd); return false; } - - int LotusState::setup_uinput() { - return connect_uinput_server() ? uinput_client_fd_.load(std::memory_order_acquire) : -1; - } - + int LotusState::setup_uinput() { return connect_uinput_server() ? uinput_client_fd_.load(std::memory_order_acquire) : -1; } void LotusState::send_backspace_uinput(int count) const { if (uinput_client_fd_ < 0 && !connect_uinput_server()) { LOTUS_ERROR("Cannot send backspace since cannot connect to uinput server"); @@ -119,9 +93,7 @@ namespace fcitx { if (n < 0) { LOTUS_WARN("Failed to send backspace: " + std::string(strerror(errno))); int old_fd = uinput_client_fd_.exchange(-1); - if (old_fd != -1) { - close(old_fd); - } + if (old_fd != -1) close(old_fd); if (connect_uinput_server()) { LOTUS_INFO("Reconnected to uinput server successfully"); send(uinput_client_fd_, &count, sizeof(count), MSG_NOSIGNAL); @@ -143,19 +115,15 @@ namespace fcitx { // std::this_thread::sleep_for(std::chrono::milliseconds(count * 2)); //} } - void LotusState::send_backspace_forward(int count) const { - if (count <= 0) - return; + if (count <= 0) return; for (int i = 0; i < count; ++i) { ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), false); ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), true); } } - bool LotusState::isAutofillCertain(const SurroundingText& s) { - if (!s.isValid() || oldPreBuffer_.empty()) - return false; + if (!s.isValid() || oldPreBuffer_.empty()) return false; const unsigned int cursor = s.cursor(); const unsigned int anchor = s.anchor(); const auto& text = s.text(); @@ -170,7 +138,6 @@ namespace fcitx { const size_t pb = text.find(oldPreBuffer_); size_t rangeStart = buffLen >= cursor_sz ? 0 : cursor_sz - buffLen; const bool sameprefix = pb != std::string::npos && pb >= rangeStart && pb <= cursor_sz; - // Detect browser autofill/autocomplete suggestions via selection. // This check for wayland_input method v2/v3 and not dbus if (cursor != anchor) { @@ -180,8 +147,7 @@ namespace fcitx { // Only consider it browser autofill if the selection starts at the cursor // and extends to the end of the line (common address bar behavior). if (cursor <= selectionEnd) { - if (!sameprefix) - return false; + if (!sameprefix) return false; // If the selection contains a newline, it's likely a multiline editor (AI ghost text), // not a single-line URL/Search bar. size_t p = @@ -215,17 +181,14 @@ namespace fcitx { #endif && sameprefix) return true; - update_max(realtextLen, static_cast(cursor)); return false; } - void LotusState::handlePreeditMode(KeyEvent& keyEvent, KeySym currentSym) { std::string commitStr; std::string preeditStr; bool processed = inputBackend_->processKeyEventAndPull(currentSym, keyEvent.rawKey().states(), &commitStr, &preeditStr); - if (processed) - keyEvent.filterAndAccept(); + if (processed) keyEvent.filterAndAccept(); if (!commitStr.empty()) { LOTUS_INFO("Commit: " + commitStr); ic_->commitString(commitStr); @@ -235,63 +198,49 @@ namespace fcitx { std::string_view view = preeditStr; Text text; TextFormatFlags fmt = TextFormatFlag::NoFlag; - if (utf8::validate(view)) - text.append(std::string(view), fmt); + if (utf8::validate(view)) text.append(std::string(view), fmt); text.setCursor(static_cast(text.textLength())); - if (ic_->capabilityFlags().test(CapabilityFlag::Preedit)) - ic_->inputPanel().setClientPreedit(text); - else - ic_->inputPanel().setPreedit(text); + if (ic_->capabilityFlags().test(CapabilityFlag::Preedit)) ic_->inputPanel().setClientPreedit(text); + else ic_->inputPanel().setPreedit(text); } ic_->updatePreedit(); ic_->updateUserInterface(UserInterfaceComponent::InputPanel); } - void LotusState::updateEmojiPageStatus(CommonCandidateList* commonList) { - if ((commonList == nullptr) || commonList->empty()) - return; + if ((commonList == nullptr) || commonList->empty()) return; int pageSize = commonList->pageSize(); - if (pageSize <= 0) - pageSize = 9; + 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 LotusState::handleEmojiMode(KeyEvent& keyEvent) { const KeySym currentSym = keyEvent.rawKey().sym(); bool isCtrlBackspace = isBackspace(currentSym) && ((keyEvent.rawKey().states() & KeyState::Ctrl) != 0U); - if (keyEvent.key().hasModifier() && !isCtrlBackspace) { keyEvent.forward(); return; } - auto baseList = ic_->inputPanel().candidateList(); auto commonList = std::dynamic_pointer_cast(baseList); if (commonList && currentSym >= FcitxKey_1 && currentSym <= FcitxKey_9) { int offset = currentSym - FcitxKey_1; int globalIndex = (commonList->currentPage() * commonList->pageSize()) + offset; - if (globalIndex < commonList->totalSize()) { commonList->candidateFromAll(globalIndex).select(ic_); keyEvent.filterAndAccept(); return; } } - if (commonList && !commonList->empty()) { int globalCursorIndex = commonList->globalCursorIndex(); int totalSize = commonList->totalSize(); int currentPage = commonList->currentPage(); int pageSize = commonList->pageSize(); int localCursorIndex = globalCursorIndex - (currentPage * pageSize); - bool handled = false; - switch (currentSym) { case FcitxKey_Tab: case FcitxKey_Down: { @@ -307,7 +256,6 @@ namespace fcitx { handled = true; break; } - case FcitxKey_ISO_Left_Tab: case FcitxKey_Up: { if (globalCursorIndex == 0) { @@ -345,7 +293,6 @@ namespace fcitx { } default: break; } - if (handled) { updateEmojiPageStatus(commonList.get()); ic_->updateUserInterface(UserInterfaceComponent::InputPanel); @@ -353,25 +300,19 @@ namespace fcitx { return; } } - if (isBackspace(currentSym)) { if (!emojiBuffer_.empty()) { - if (isCtrlBackspace) { - emojiBuffer_.clear(); + if (isCtrlBackspace) { emojiBuffer_.clear(); } else { emojiBuffer_.pop_back(); - while (!emojiBuffer_.empty() && (emojiBuffer_.back() & 0xC0) == 0x80) { + while (!emojiBuffer_.empty() && (emojiBuffer_.back() & 0xC0) == 0x80) emojiBuffer_.pop_back(); - } } keyEvent.filterAndAccept(); - } else { - keyEvent.forward(); - } + } else keyEvent.forward(); updateEmojiPreedit(); return; } - switch (currentSym) { case FcitxKey_space: case FcitxKey_Return: { @@ -384,12 +325,9 @@ namespace fcitx { emojiBuffer_.clear(); updateEmojiPreedit(); keyEvent.filterAndAccept(); - } else { - keyEvent.forward(); - } + } else keyEvent.forward(); return; } - case FcitxKey_Escape: { emojiBuffer_.clear(); emojiCandidates_.clear(); @@ -398,20 +336,14 @@ namespace fcitx { keyEvent.filterAndAccept(); return; } - default: break; } - - { - std::string utf8Char = Key::keySymToUTF8(currentSym); - if (!utf8Char.empty()) { - emojiBuffer_.append(utf8Char); - keyEvent.filterAndAccept(); - updateEmojiPreedit(); - } else { - keyEvent.forward(); - } - } + std::string utf8Char = Key::keySymToUTF8(currentSym); + if (!utf8Char.empty()) { + emojiBuffer_.append(utf8Char); + keyEvent.filterAndAccept(); + updateEmojiPreedit(); + } else keyEvent.forward(); } void LotusState::updateEmojiPreedit() { if (emojiBuffer_.empty()) { @@ -422,71 +354,48 @@ namespace fcitx { ic_->updateUserInterface(UserInterfaceComponent::InputPanel); return; } - } else { - emojiCandidates_ = engine_->emojiLoader().search(emojiBuffer_); - } - + } else emojiCandidates_ = engine_->emojiLoader().search(emojiBuffer_); if (!emojiBuffer_.empty()) { Text preeditText; preeditText.append(emojiBuffer_, TextFormatFlag::Underline); preeditText.setCursor(static_cast(preeditText.textLength())); - if (ic_->capabilityFlags().test(CapabilityFlag::Preedit)) - ic_->inputPanel().setClientPreedit(preeditText); - else - ic_->inputPanel().setPreedit(preeditText); + if (ic_->capabilityFlags().test(CapabilityFlag::Preedit)) ic_->inputPanel().setClientPreedit(preeditText); + else ic_->inputPanel().setPreedit(preeditText); } else { ic_->inputPanel().setClientPreedit(Text()); ic_->inputPanel().setPreedit(Text()); } - if (!emojiCandidates_.empty()) { auto candidateList = std::make_unique(); candidateList->setLayoutHint(CandidateLayoutHint::Vertical); candidateList->setPageSize(9); - for (size_t i = 0; i < emojiCandidates_.size(); ++i) { size_t localIndex = (i % 9) + 1; Text displayLabel; - if (emojiBuffer_.empty()) { - displayLabel.append(std::to_string(localIndex) + ": " + emojiCandidates_[i].output, TextFormatFlag::NoFlag); - } else { - displayLabel.append(std::to_string(localIndex) + ": " + emojiCandidates_[i].trigger + " " + emojiCandidates_[i].output, TextFormatFlag::NoFlag); - } + if (emojiBuffer_.empty()){displayLabel.append(std::to_string(localIndex) + ": " + emojiCandidates_[i].output, TextFormatFlag::NoFlag); + }else{displayLabel.append(std::to_string(localIndex)+": "+ emojiCandidates_[i].trigger + " " + emojiCandidates_[i].output,TextFormatFlag::NoFlag);} candidateList->append(std::make_unique(displayLabel, this, emojiCandidates_[i])); } 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); - } - + } else ic_->inputPanel().setCandidateList(nullptr); ic_->updatePreedit(); ic_->updateUserInterface(UserInterfaceComponent::InputPanel); } - bool LotusState::handleUInputKeyPress(KeyEvent& event, KeySym currentSym, int sleepTime) { - if (!is_deleting_.load()) { - return false; - } + if (!is_deleting_.load()) return false; if (isBackspace(currentSym)) { current_backspace_count_ += 1; - if (current_backspace_count_ < expected_backspaces_) { - return false; // Allow intermediate backspaces to reach the app to clear autofill/old text. - } + if (current_backspace_count_ < expected_backspaces_) return false; // Allow intermediate backspaces to reach the app to clear autofill/old text. is_deleting_.store(false); replacement_start_ms_.store(0, std::memory_order_release); replacement_thread_id_.store(0, std::memory_order_release); int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); int64_t wait_ms = static_cast(sleepTime) - elapsed_ms; - if (wait_ms > 0) - std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); - if (waitAck_) { - const int wait_ms_ack = 5; - std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms_ack)); - } + if (wait_ms > 0) std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); + if (waitAck_) std::this_thread::sleep_for(std::chrono::milliseconds(5)); //wait more ic_->commitString(pending_commit_string_); LOTUS_INFO("Commit: " + pending_commit_string_); expected_backspaces_ = 0; @@ -497,7 +406,6 @@ namespace fcitx { } return false; } - bool LotusState::performReplacement(const std::string& deletedPart, const std::string& addedPart) { LOTUS_INFO("Perform replacement: " + deletedPart + " -> " + addedPart); //NOLINT int my_id = ++current_thread_id_; @@ -512,20 +420,16 @@ namespace fcitx { utf8::length(deletedPart) #endif ) + 1 + autofillOffset; - if (realMode == LotusMode::UinputWine) - --expected_backspaces_; + if (realMode == LotusMode::UinputWine) expected_backspaces_-=1; // Use deleteSurroundingText for apps that support it for smooth typing - bool test_flags = false; // use for testing only :v LOTUS_INFO("surr: \""+surrounding.text()+"\""); - if (surrtp) - LOTUS_INFO("surrtp"); - if ((test_flags || surrtp) // Lmfao, only this work :> + if (surrtp) LOTUS_INFO("surrtp"); + if ((false || surrtp) // Lmfao, only this work :> && (surrounding.isValid() && ic_->capabilityFlags().test(CapabilityFlag::SurroundingText)) && (!surrounding.text().empty() && surrounding.text().back() != '\n' // firefox and discord insert '\n' into surr cause bug && !autofillOffset) // TODO: Guard, remove this when bug of surrounding is fixes ) { LOTUS_INFO("deleteSurroundingText branch"); - auto cur = static_cast(surrounding.cursor()); const int bsCount = static_cast( #if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) utf8_length_avx512(deletedPart.data(), deletedPart.size()) @@ -543,17 +447,14 @@ namespace fcitx { utf8::length(surr) #endif ); + auto cur = static_cast(surrounding.cursor()); int realLen = static_cast(cur); int suggestionLen = surrLen - realLen; // delete suggestion tail - if (suggestionLen > 0) - ic_->deleteSurroundingText(0, 1); + if (suggestionLen > 0) ic_->deleteSurroundingText(0, 1); } - // delete addedPart - if (bsCount > 0) - ic_->deleteSurroundingText(-bsCount, static_cast(bsCount)); + if (bsCount > 0) ic_->deleteSurroundingText(-bsCount, static_cast(bsCount)); ic_->commitString(addedPart); - //clearAllBuffers(); return true; } else { replacement_thread_id_.store(my_id, std::memory_order_release); @@ -563,15 +464,12 @@ namespace fcitx { if (0 && isTerm) { send_backspace_forward(expected_backspaces_ - 1); return true; - } else - send_backspace_uinput(expected_backspaces_); + } else send_backspace_uinput(expected_backspaces_); LOTUS_INFO("Send " + std::to_string(expected_backspaces_ - 1 - autofillOffset) + " backspaces + 1 trigger"); - if (autofillOffset) - LOTUS_INFO("Send more 1 extra delete suggestions"); + if (autofillOffset) LOTUS_INFO("Send more 1 extra delete suggestions"); } return false; } - bool LotusState::checkForwardSpecialKey(KeyEvent& keyEvent, KeySym& currentSym) { if (keyEvent.key().isCursorMove() || currentSym == FcitxKey_Tab || currentSym == FcitxKey_KP_Tab || currentSym == FcitxKey_ISO_Left_Tab || currentSym == FcitxKey_Escape || keyEvent.key().hasModifier()) { @@ -584,16 +482,11 @@ namespace fcitx { oldPreBuffer_.clear(); return true; } - - if (currentSym == FcitxKey_Delete) { - return true; - } - + if (currentSym == FcitxKey_Delete) return true; if (currentSym >= FcitxKey_KP_0 && currentSym <= FcitxKey_KP_9) { currentSym = static_cast(FcitxKey_0 + (currentSym - FcitxKey_KP_0)); return false; } - switch (currentSym) { case FcitxKey_KP_Add: currentSym = FcitxKey_plus; break; case FcitxKey_KP_Subtract: currentSym = FcitxKey_minus; break; @@ -607,17 +500,12 @@ namespace fcitx { } return false; } - void LotusState::handleUinputMode(KeyEvent& keyEvent, KeySym currentSym) { if (checkForwardSpecialKey(keyEvent, currentSym)) { keyEvent.forward(); return; } - - if (uinput_client_fd_ < 0) { - setup_uinput(); - } - + if (uinput_client_fd_ < 0) setup_uinput(); if (isBackspace(currentSym) || currentSym == FcitxKey_Return) { if (isBackspace(currentSym)) { hasHistory_ = true; @@ -632,22 +520,18 @@ namespace fcitx { keyEvent.forward(); return; } - std::string keyUtf8 = Key::keySymToUTF8(currentSym); if (keyUtf8.empty()) { keyEvent.forward(); return; } - std::string commitStr; std::string preeditStrBuf; bool processed = inputBackend_->processKeyEventAndPull(currentSym, keyEvent.rawKey().states(), &commitStr, &preeditStrBuf); - if (!commitStr.empty()) { std::string deletedPart; std::string addedPart; compareAndSplitStrings(oldPreBuffer_, commitStr, deletedPart, addedPart); - if (!deletedPart.empty()) { keyEvent.filterAndAccept(); performReplacement(deletedPart, addedPart); @@ -655,29 +539,23 @@ namespace fcitx { bool wasAutoCapitalized = (currentSym != keyEvent.rawKey().sym()); if (!addedPart.empty() && (keyUtf8 != addedPart || wasAutoCapitalized)) { // Prevent auto-capitalized character replacement from stripping out Vietnamese chars - if (addedPart.size() > 1 && addedPart.back() == ' ') { + if (addedPart.size() > 1 && addedPart.back() == ' ') // Stripping the trigger key (space) from addedPart #if __cplusplus >= 202002L addedPart.resize(addedPart.size() - 1); #else addedPart = addedPart.substr(0, addedPart.size() - 1); #endif - } ic_->commitString(addedPart); LOTUS_INFO("Commit: " + addedPart); keyEvent.filterAndAccept(); - } else { - keyEvent.forward(); - } + } else keyEvent.forward(); } - hasHistory_ = false; inputBackend_->resetEngine(); oldPreBuffer_.clear(); - return; } - // Treat "processed but no effect" as passthrough if (!processed || (!commitStr.empty() && !preeditStrBuf.empty())) { if (!preeditStrBuf.empty()) { @@ -688,25 +566,18 @@ namespace fcitx { keyEvent.forward(); return; } - hasHistory_ = true; realtextLen.fetch_add(1, std::memory_order_acq_rel); - std::string preeditStr = preeditStrBuf; - std::string deletedPart; std::string addedPart; - - if (wa_flag) - keyEvent.filterAndAccept(); - + if (wa_flag) keyEvent.filterAndAccept(); if (compareAndSplitStrings(oldPreBuffer_, preeditStr, deletedPart, addedPart) != 0) { if (deletedPart.empty()) { bool isCommit = false; bool wasAutoCapitalized = (currentSym != keyEvent.rawKey().sym()); if (!addedPart.empty()) { - if (wa_flag) - ic_->commitString(addedPart); + if (wa_flag) ic_->commitString(addedPart); oldPreBuffer_ = preeditStr; if (!wa_flag) if (wasAutoCapitalized || addedPart != keyUtf8) { @@ -741,23 +612,16 @@ namespace fcitx { if (uinput_client_fd_ < 0) { LOTUS_ERROR("Cannot connect to uinput server, commit rawkey"); std::string rawKey = keyEvent.key().toString(); - if (!rawKey.empty()) { - ic_->commitString(rawKey); - } + if (!rawKey.empty()) ic_->commitString(rawKey); return; } - - if (is_deleting_.load()) { - is_deleting_.store(false, std::memory_order_release); - } - if (!wa_flag) - keyEvent.filterAndAccept(); + if (is_deleting_.load()) is_deleting_.store(false, std::memory_order_release); + if (!wa_flag) keyEvent.filterAndAccept(); performReplacement(deletedPart, addedPart); oldPreBuffer_ = preeditStr; } } } - void LotusState::handleSurroundingText(KeyEvent& keyEvent, KeySym currentSym) { if (checkForwardSpecialKey(keyEvent, currentSym)) { keyEvent.forward(); @@ -769,68 +633,44 @@ namespace fcitx { keyEvent.forward(); return; } - const auto& surrounding = ic->surroundingText(); if (!surrounding.isValid()) { LOTUS_WARN("Surrounding text is invalid"); keyEvent.forward(); return; } - if (isBackspace(keyEvent.rawKey().sym())) { inputBackend_->resetEngine(); keyEvent.forward(); return; } - - if (surrounding.anchor() != surrounding.cursor()) { - ic->deleteSurroundingText(0, 0); - } - + if (surrounding.anchor() != surrounding.cursor()) ic->deleteSurroundingText(0, 0); const std::string& text = surrounding.text(); unsigned int cursor = surrounding.cursor(); - size_t textLen = utf8::lengthValidated(text); - if (textLen == utf8::INVALID_LENGTH || cursor <= 0 || cursor > textLen) { processNormalKey(keyEvent, currentSym); return; } - { auto startIter = utf8::nextNChar(text.begin(), cursor); auto endIter = startIter; - int scanCount = 0; while (startIter != text.begin() && scanCount < MAX_SCAN_LENGTH) { auto prev = startIter; if (prev != text.begin()) { --prev; - while (prev != text.begin() && ((*prev & 0xC0) == 0x80)) { - --prev; - } + while (prev != text.begin() && ((*prev & 0xC0) == 0x80)) { --prev;} } - uint32_t ucs4 = utf8::getChar(prev, text.end()); - - if (isWordBreak(ucs4)) - break; - + if (isWordBreak(ucs4)) break; startIter = prev; ++scanCount; } - std::string oldWord(startIter, endIter); - - if (oldWord.empty()) { - processNormalKey(keyEvent, currentSym); - return; - } - + if (oldWord.empty()) { processNormalKey(keyEvent, currentSym);return;} inputBackend_->rebuildFromText(oldWord.c_str()); - bool processed = inputBackend_->processKeyEvent(currentSym, keyEvent.rawKey().states()); - if (!processed) { keyEvent.forward(); inputBackend_->resetEngine(); @@ -840,10 +680,8 @@ namespace fcitx { std::string preeditPart; inputBackend_->pullCommitAndPreedit(&commitPart, &preeditPart); std::string newWord; - if (!commitPart.empty()) - newWord += commitPart; - if (!preeditPart.empty()) - newWord += preeditPart; + if (!commitPart.empty()) newWord += commitPart; + if (!preeditPart.empty()) newWord += preeditPart; std::string deletedPart; std::string addedPart; compareAndSplitStrings(oldWord, newWord, deletedPart, addedPart); @@ -854,19 +692,14 @@ namespace fcitx { } if (!deletedPart.empty() || !addedPart.empty()) { size_t charsToDelete = utf8::length(deletedPart); - if (charsToDelete > 0) - ic->deleteSurroundingText(-static_cast(charsToDelete), static_cast(charsToDelete)); - if (!addedPart.empty()) { - ic->commitString(addedPart); - LOTUS_INFO("Commit: " + addedPart); - } + if (charsToDelete > 0) ic->deleteSurroundingText(-static_cast(charsToDelete), static_cast(charsToDelete)); + if (!addedPart.empty()) {ic->commitString(addedPart);LOTUS_INFO("Commit: " + addedPart);} } inputBackend_->resetEngine(); keyEvent.filterAndAccept(); return; } } - void LotusState::processNormalKey(KeyEvent& keyEvent, KeySym currentSym) { auto* ic = keyEvent.inputContext(); inputBackend_->resetEngine(); @@ -875,28 +708,19 @@ namespace fcitx { bool processed = inputBackend_->processKeyEventAndPull(currentSym, keyEvent.rawKey().states(), &commitPart, &preeditPart); if (processed) { std::string out; - if (!commitPart.empty()) - out += commitPart; - if (!preeditPart.empty()) - out += preeditPart; - if (!out.empty()) { - LOTUS_INFO("Commit: " + out); - ic->commitString(out); - } + if (!commitPart.empty()) out += commitPart; + if (!preeditPart.empty()) out += preeditPart; + if (!out.empty()) {LOTUS_INFO("Commit: " + out);ic->commitString(out);} inputBackend_->resetEngine(); keyEvent.filterAndAccept(); - } else { - keyEvent.forward(); - } + } else keyEvent.forward(); } - void LotusState::handleDoubleSpaceReplacement() { switch (realMode) { case LotusMode::SurroundingText: { ic_->deleteSurroundingText(-1, 1); ic_->commitString(". "); LOTUS_INFO("Commit: . "); - break; } default: { // Uinput, Smooth, Preedit, etc. @@ -910,12 +734,10 @@ namespace fcitx { shouldCapitalize_ = true; } } - void LotusState::keyEvent(KeyEvent& keyEvent) { - if (!inputBackend_ || keyEvent.isRelease()) - return; + if (!inputBackend_ || keyEvent.isRelease()) return; if (uinput_client_fd_ < 0) { - LOTUS_WARN("Cannot connect to uinput server, reconnecting...."); + LOTUS_WARN("uinput connect failed, reconnecting...."); connect_uinput_server(); } if (current_backspace_count_ >= expected_backspaces_ && is_deleting_.load()) { @@ -935,21 +757,18 @@ namespace fcitx { isPrevPunctuation_ = false; needEngineReset.store(false); } - if (g_mouse_clicked.load(std::memory_order_acquire) && !is_deleting_.load(std::memory_order_acquire)) { g_mouse_clicked.store(false, std::memory_order_release); clearAllBuffers(); } - if (needFallbackCommit.load(std::memory_order_acquire)) { LOTUS_INFO("Need fallback commit"); needFallbackCommit.store(false, std::memory_order_release); - if (current_thread_id_.load(std::memory_order_acquire) == replacement_thread_id_.load(std::memory_order_acquire)) { + if (current_thread_id_.load(std::memory_order_acquire) == replacement_thread_id_.load(std::memory_order_acquire)) if (!pending_commit_string_.empty()) { ic_->commitString(pending_commit_string_); pending_commit_string_.clear(); } - } replacement_thread_id_.store(0, std::memory_order_release); replacement_start_ms_.store(0, std::memory_order_release); } @@ -957,49 +776,29 @@ namespace fcitx { if (*engine_->config().autoCapitalizeAfterPunctuation && realMode != LotusMode::Off) { // Ignore auto-capitalize side-effects if we're processing automated replacement backspaces bool isAutomatedBackspace = is_deleting_.load(std::memory_order_acquire) && isBackspace(currentSym); - if (!isAutomatedBackspace) { - if (shouldCapitalize_) { + if (shouldCapitalize_) if (currentSym >= FcitxKey_a && currentSym <= FcitxKey_z) { auto upperSym = static_cast(currentSym - (FcitxKey_a - FcitxKey_A)); currentSym = upperSym; keyEvent.setKey(Key(upperSym, keyEvent.rawKey().states())); shouldCapitalize_ = false; - } else if (currentSym != FcitxKey_space) { - shouldCapitalize_ = false; - } - } - + } else if (currentSym != FcitxKey_space) shouldCapitalize_ = false; switch (currentSym) { case FcitxKey_period: case FcitxKey_exclam: case FcitxKey_question: isPrevPunctuation_ = true; break; case FcitxKey_Return: - case FcitxKey_KP_Enter: - shouldCapitalize_ = true; - isPrevPunctuation_ = false; - break; - case FcitxKey_space: - if (isPrevPunctuation_) { - shouldCapitalize_ = true; - isPrevPunctuation_ = false; - } - break; - default: - if (currentSym != FcitxKey_space) - isPrevPunctuation_ = false; - break; + case FcitxKey_KP_Enter: shouldCapitalize_ = true; isPrevPunctuation_ = false; break; + case FcitxKey_space: if (isPrevPunctuation_) { shouldCapitalize_ = true; isPrevPunctuation_ = false;} break; + default: if (currentSym != FcitxKey_space) isPrevPunctuation_ = false; break; } } } - if (is_deleting_.load(std::memory_order_acquire)) { if (isBackspace(currentSym)) { - if (realtextLen.load(std::memory_order_acquire) > 0) - realtextLen.fetch_sub(1, std::memory_order_acq_rel); - if (handleUInputKeyPress(keyEvent, currentSym, (realMode == LotusMode::Smooth) ? 3 : 10)) { - return; - } + if (realtextLen.load(std::memory_order_acquire) > 0) realtextLen.fetch_sub(1, std::memory_order_acq_rel); + if (handleUInputKeyPress(keyEvent, currentSym, (realMode == LotusMode::Smooth) ? 3 : 10)) return; } else { std::string keyUtf8Check = Key::keySymToUTF8(currentSym); if (!keyUtf8Check.empty() && buffered_keys_.size() < MAX_BUFFERED_KEYS) { @@ -1010,7 +809,6 @@ namespace fcitx { } return; } - if (*engine_->config().doubleSpaceToPeriod && realMode != LotusMode::Off) { if (currentSym == FcitxKey_space) { if (isPrevSpace_) { @@ -1020,11 +818,8 @@ namespace fcitx { return; } isPrevSpace_ = true; - } else { - isPrevSpace_ = false; - } + } else isPrevSpace_ = false; } - switch (realMode) { case LotusMode::Uinput: case LotusMode::Smooth: case LotusMode::UinputWine: handleUinputMode(keyEvent, currentSym); break; case LotusMode::SurroundingText: handleSurroundingText(keyEvent, currentSym); break; @@ -1033,7 +828,6 @@ namespace fcitx { default: break; } } - void LotusState::reset(bool isFocusOut) { const auto& surrounding = ic_->surroundingText(); const auto& text = surrounding.text(); @@ -1044,8 +838,7 @@ namespace fcitx { utf8::length(text); #endif realtextLen.store(textLen, std::memory_order_release); - if (is_deleting_.load(std::memory_order_acquire)) - return; + if (is_deleting_.load(std::memory_order_acquire)) return; if (inputBackend_) { isPrevSpace_ = false; shouldCapitalize_ = false; @@ -1054,59 +847,43 @@ namespace fcitx { inputBackend_->commitPreedit(); std::string commit; inputBackend_->pullCommit(&commit); - if (!commit.empty()) { - ic_->commitString(commit); - LOTUS_INFO("Commit: " + commit); - } + if (!commit.empty()) { ic_->commitString(commit); LOTUS_INFO("Commit: "+commit);} } inputBackend_->resetEngine(); } - if (getFrontendName(ic_) != "dbus") - clearAllBuffers(); - if (realMode == LotusMode::Off) - return; + if (getFrontendName(ic_) != "dbus") clearAllBuffers(); + if (realMode == LotusMode::Off) return; ic_->inputPanel().reset(); switch (realMode) { case LotusMode::Preedit: - case LotusMode::Emoji: { + case LotusMode::Emoji: ic_->updateUserInterface(UserInterfaceComponent::InputPanel); ic_->updatePreedit(); break; - } default: break; } } - void LotusState::commitBuffer() { - if (realMode == LotusMode::Off) - return; - if (inputBackend_) - inputBackend_->resetEngine(); + if (realMode == LotusMode::Off) return; + if (inputBackend_) inputBackend_->resetEngine(); switch (realMode) { - case LotusMode::Preedit: { + case LotusMode::Preedit: ic_->inputPanel().reset(); if (inputBackend_) { inputBackend_->commitPreedit(); std::string commit; inputBackend_->pullCommit(&commit); - if (!commit.empty()) - ic_->commitString(commit); + if (!commit.empty()) ic_->commitString(commit); } ic_->updateUserInterface(UserInterfaceComponent::InputPanel); ic_->updatePreedit(); break; - } - default: { - break; - } + default: break; } } - void LotusState::clearAllBuffers() { LOTUS_DEBUG("Clear all buffers"); - if (is_deleting_.load(std::memory_order_acquire)) { - return; - } + if (is_deleting_.load(std::memory_order_acquire)) return; oldPreBuffer_.clear(); hasHistory_ = false; expected_backspaces_ = 0; @@ -1117,17 +894,9 @@ namespace fcitx { buffered_keys_.clear(); shouldCapitalize_ = false; isPrevPunctuation_ = false; - if (inputBackend_) - inputBackend_->resetEngine(); - } - - bool LotusState::isEmptyHistory() const { - return !hasHistory_; - } - bool LotusState::isReplacing() const { - return expected_backspaces_ > 0 && current_backspace_count_ < expected_backspaces_; - } - bool LotusState::isX11() const { - return false; //cat /proc//maps | grep -E 'libX11|libxcb' + if (inputBackend_) inputBackend_->resetEngine(); } + bool LotusState::isEmptyHistory() const { return !hasHistory_;} + bool LotusState::isReplacing() const { return expected_backspaces_ > 0 && current_backspace_count_ < expected_backspaces_;} + bool LotusState::isX11() const { return false; /*cat /proc//maps | grep -E 'libX11|libxcb' */} } // namespace fcitx From c67e9d14c3f91229f17eab3d2e1d6f40c8b3e27d Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 17:25:59 +0700 Subject: [PATCH 32/42] compact ... --- src/lotus-engine.cpp | 378 ++++++++--------------------------- src/lotus-state.cpp | 3 +- src/lotus-unikey-backend.cpp | 111 +++------- 3 files changed, 113 insertions(+), 379 deletions(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index f344be0d..bceb50d5 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -15,53 +15,31 @@ #include "app_quirks.h" #include #include - #include #include #include #include - #include #include #include #include - #include #include - namespace fcitx { constexpr const char* CharsetActionPrefix = "lotus-charset-"; const std::string CustomKeymapFile = "conf/lotus-custom-keymap.conf"; const std::string MacroTableFile = "conf/lotus-macro-table.conf"; - - // Returns the KeySym that triggers the "Type hotkey char" action in the mode - // menu. If the hotkey itself conflicts with a reserved menu key, falls back - // to FcitxKey_f. + // Returns the KeySym that triggers the "Type hotkey char" action in the mode menu. + // If the hotkey itself conflicts with a reserved menu key, falls back to FcitxKey_f. static bool isAppModeMenuReservedKey(KeySym sym) { switch (sym) { - case FcitxKey_1: - case FcitxKey_2: - case FcitxKey_3: - case FcitxKey_4: - case FcitxKey_q: - case FcitxKey_w: - case FcitxKey_e: - case FcitxKey_r: - case FcitxKey_Escape: - case FcitxKey_Tab: - case FcitxKey_ISO_Left_Tab: - case FcitxKey_Return: - case FcitxKey_space: - case FcitxKey_Up: - case FcitxKey_Down: return true; + case FcitxKey_1: case FcitxKey_2: case FcitxKey_3: case FcitxKey_4: case FcitxKey_q: case FcitxKey_w: case FcitxKey_e: + case FcitxKey_r: case FcitxKey_Escape: case FcitxKey_Tab: case FcitxKey_ISO_Left_Tab: case FcitxKey_Return: + case FcitxKey_space: case FcitxKey_Up: case FcitxKey_Down: return true; default: return false; } } - - static KeySym typeKeyForModeMenuHotkey(KeySym hotkeySym) { - return isAppModeMenuReservedKey(hotkeySym) ? FcitxKey_f : hotkeySym; - } - + static KeySym typeKeyForModeMenuHotkey(KeySym hotkeySym) { return isAppModeMenuReservedKey(hotkeySym) ? FcitxKey_f : hotkeySym;} static inline std::vector convertToStringList(char** list) { std::vector result; if (list != nullptr) { @@ -73,7 +51,6 @@ namespace fcitx { } return result; } - LotusEngine::LotusEngine(Instance* instance) : instance_(instance), factory_([this](InputContext& ic) { return new LotusState(this, &ic); }) { //NOLINT const char* desktop = std::getenv("XDG_CURRENT_DESKTOP"); isGnome_ = (desktop != nullptr) && std::string(desktop).find("GNOME") != std::string::npos; @@ -81,16 +58,12 @@ namespace fcitx { startMonitoring(); imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "VIQR", "Microsoft"}; config_.inputMethod.annotation().setList(imNames_); - auto& uiManager = instance_->userInterfaceManager(); - initToggleAction(spellCheckAction_, config_.spellCheck, "lotus-spellcheck", "tools-check-spelling", _("Enable Spell Check"), _("Spell Check"), uiManager); initToggleAction(macroAction_, config_.enableMacro, "lotus-macro", "document-edit", _("Enable Macro"), _("Macro"), uiManager); - initToggleAction(capitalizeMacroAction_, config_.capitalizeMacro, "lotus-capitalizemacro", "format-text-uppercase", _("Capitalize Macro"), _("Capitalize Macro"), - uiManager); + initToggleAction(capitalizeMacroAction_, config_.capitalizeMacro, "lotus-capitalizemacro", "format-text-uppercase", _("Capitalize Macro"), _("Capitalize Macro"),uiManager); initToggleAction(autoNonVnRestoreAction_, config_.autoNonVnRestore, "lotus-autonvnrestore", "edit-undo", _("Auto Restore Keys With Invalid Words"), _("Auto Non-VN Restore"), uiManager); - settingsAction_ = std::make_unique(); settingsAction_->setShortText(_("Settings")); settingsAction_->setIcon("configure"); @@ -101,16 +74,12 @@ namespace fcitx { } })); uiManager.registerAction("lotus-settings", settingsAction_.get()); - #if LOTUS_USE_MODERN_FCITX_API std::string configDir = (StandardPaths::global().userDirectory(StandardPathsType::Config) / "fcitx5" / "conf").string(); #else std::string configDir = StandardPath::global().userDirectory(StandardPath::Type::Config) + "/fcitx5/conf"; #endif - - if (!std::filesystem::exists(configDir)) { - std::filesystem::create_directories(configDir); - } + if (!std::filesystem::exists(configDir)) { std::filesystem::create_directories(configDir);} reloadConfig(); instance_->inputContextManager().registerProperty("LotusState", &factory_); appRulesPath_ = configDir + "/lotus-app-rules.conf"; @@ -119,7 +88,6 @@ namespace fcitx { spellCheckAction_.get(), macroAction_.get(), capitalizeMacroAction_.get(), autoNonVnRestoreAction_.get(), settingsAction_.get()}; } - void LotusEngine::initToggleAction(std::unique_ptr& action, Option& option, const std::string& actionId, const std::string& iconName, const std::string& textLong, const std::string& textOnOff, UserInterfaceManager& uiManager) { action = std::make_unique(); @@ -134,63 +102,41 @@ namespace fcitx { })); uiManager.registerAction(actionId, action.get()); } - void LotusEngine::updateAction(InputContext* ic, std::unique_ptr& action, Option& option, const std::string& textOnOff) { action->setShortText((option.value() ? "✔ " : "✖ ") + textOnOff); - if (ic != nullptr) { - action->update(ic); - } + if (ic != nullptr) action->update(ic); } - LotusEngine::~LotusEngine() { stop_flag_monitor.store(true, std::memory_order_release); monitor_cv.notify_all(); int fd = mouse_socket_fd.load(std::memory_order_acquire); - if (fd >= 0) { - shutdown(fd, SHUT_RDWR); - } - if (mouse_thread.joinable()) { - mouse_thread.join(); - } - if (monitor_thread.joinable()) { - monitor_thread.join(); - } + if (fd >= 0) shutdown(fd, SHUT_RDWR); + if (mouse_thread.joinable()) mouse_thread.join(); + if (monitor_thread.joinable()) monitor_thread.join(); int old_fd = uinput_client_fd_.exchange(-1); - if (old_fd != -1) { - close(old_fd); - } + if (old_fd != -1) close(old_fd); LOTUS_INFO("Engine destroyed."); } - const lotusCustomKeymap& LotusEngine::customKeymap() const { - if (config_.enableCustomKeymap.value()) { - return customKeymap_; - } + if (config_.enableCustomKeymap.value()) return customKeymap_; return emptyCustomKeymap_; } - void LotusEngine::reloadConfig() { readAsIni(config_, "conf/lotus.conf"); readAsIni(customKeymap_, CustomKeymapFile); loadAppRules(); populateConfig(); } - const Configuration* LotusEngine::getSubConfig(const std::string& path) const { - if (path == "custom_keymap") - return &customKeymap_; - if (path == "app_rules") { - return &appRulesTables_; - } + if (path == "custom_keymap") return &customKeymap_; + if (path == "app_rules") return &appRulesTables_; return nullptr; } - void LotusEngine::setConfig(const RawConfig& config) { config_.load(config, true); saveConfig(); populateConfig(); } - void LotusEngine::populateConfig() { refreshEngine(); refreshOption(); @@ -200,7 +146,6 @@ namespace fcitx { updateAction(nullptr, capitalizeMacroAction_, config_.capitalizeMacro, _("Capitalize Macro")); updateAction(nullptr, autoNonVnRestoreAction_, config_.autoNonVnRestore, _("Auto Non-VN Restore")); } - void LotusEngine::setSubConfig(const std::string& path, const RawConfig& config) { if (path == "custom_keymap") { customKeymap_.load(config, true); @@ -225,42 +170,27 @@ namespace fcitx { refreshEngine(); } } - - std::string LotusEngine::subMode(const InputMethodEntry& /*entry*/, InputContext& /*inputContext*/) { - return *config_.inputMethod; - } - + std::string LotusEngine::subMode(const InputMethodEntry& /*entry*/, InputContext& /*inputContext*/) {return *config_.inputMethod;} void LotusEngine::activate(const InputMethodEntry& /*entry*/, InputContextEvent& event) { auto* ic = event.inputContext(); const bool surrvalid = ic->surroundingText().isValid(); const bool is_dbus = getFrontendName(ic) == "dbus"; static std::atomic mouseThreadStarted{false}; - if (!mouseThreadStarted.exchange(true)) - startMouseReset(); - + if (!mouseThreadStarted.exchange(true)) startMouseReset(); auto& statusArea = event.inputContext()->statusArea(); - if (ic->capabilityFlags().test(CapabilityFlag::Preedit)) - instance_->inputContextManager().setPreeditEnabledByDefault(true); - + if (ic->capabilityFlags().test(CapabilityFlag::Preedit)) instance_->inputContextManager().setPreeditEnabledByDefault(true); std::string appName = getProgramName(ic); LOTUS_INFO("App name: " + appName); - const LotusMode targetMode = getAppRule(appName); LOTUS_INFO("Target mode: " + modeEnumToString(targetMode)); - updateCharsetAction(event.inputContext()); - setMode(targetMode, event.inputContext()); - auto* state = ic->propertyFor(&factory_); - // Workaround for chromium wayland issue where suggestions cause a doubled // first character. Forwarding may prevent BS from being sent // to the client. - // // Note that with chromium x11 we can't do anything to fixes this because // it not support surrounding text so can't know when it show suggestions - // // TODO: Properly fixes instead ugly WA state->isTerm = false; state->wa_flag = false; @@ -272,19 +202,14 @@ namespace fcitx { #if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) tolower_avx512(appName.data(), appName.size()); #elif __cplusplus >= 202002L - std::ranges::transform(appName, appName.begin(), - [](unsigned char c) { return std::tolower(c); }); + std::ranges::transform(appName, appName.begin(), [](unsigned char c) { return std::tolower(c); }); #else std::transform(appName.begin(), appName.end(), appName.begin(), ::tolower); #endif #if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) - auto contains = [&](std::string_view s) { - return strfind_avx512(appName.data(), appName.size(), s.data(), s.size()) != static_cast(-1); - }; + auto contains = [&](std::string_view s) { return strfind_avx512(appName.data(), appName.size(), s.data(), s.size()) != static_cast(-1); }; #else - auto contains = [&](std::string_view s) { - return appName.find(s) != std::string::npos; - }; + auto contains = [&](std::string_view s) { return appName.find(s) != std::string::npos; }; #endif for (const auto& ackApp : ack_apps) { if (contains(ackApp)) { @@ -312,27 +237,20 @@ namespace fcitx { } } } - if (prevAck != state->waitAck_ && !state->waitAck_ && uinput_client_fd_ >= 0) { - // close(uinput_client_fd_); - // uinput_client_fd_ = -1; + if (prevAck != state->waitAck_ && !state->waitAck_ && uinput_client_fd_>=0) { char drain[64]; - recv(uinput_client_fd_, drain, sizeof(drain), MSG_DONTWAIT | MSG_NOSIGNAL); + recv(uinput_client_fd_,drain,sizeof(drain),MSG_DONTWAIT | MSG_NOSIGNAL); } - if (event.type() == EventType::InputContextFocusIn && is_dbus && !surrvalid) { - LOTUS_INFO("Skip clearAllBuffers"); - } else if (surrvalid && !state->oldPreBuffer_.empty() && (now_ms() - state->lastDeactivateTime_) < 100) { - state->clearAllBuffers(); - } - if (!state->isReplacing()) - is_deleting_.store(false); + if (event.type() == EventType::InputContextFocusIn && is_dbus && !surrvalid) { LOTUS_INFO("Skip clearAllBuffers"); + } else if (surrvalid && !state->oldPreBuffer_.empty() && (now_ms() - state->lastDeactivateTime_) < 100) { state->clearAllBuffers(); } + if (!state->isReplacing()) is_deleting_.store(false); needEngineReset.store(false); if (targetMode == LotusMode::Emoji) { state->updateEmojiPreedit(); } else { LOTUS_INFO("inputPanel reset"); ic->inputPanel().reset(); - if (realMode == LotusMode::Preedit - || realMode == LotusMode::SurroundingText) { + if (realMode == LotusMode::Preedit || realMode == LotusMode::SurroundingText) { ic->updateUserInterface(UserInterfaceComponent::InputPanel); ic->updatePreedit(); } @@ -341,10 +259,8 @@ namespace fcitx { statusArea.addAction(StatusGroup::InputMethod, action); } } - void LotusEngine::keyEvent(const InputMethodEntry& /*entry*/, KeyEvent& keyEvent) { auto* ic = keyEvent.inputContext(); - if (isSelectingAppMode_ && g_mouse_clicked.load(std::memory_order_acquire)) { closeAppModeMenu(); LOTUS_INFO("reset inputPanel"); @@ -354,110 +270,51 @@ namespace fcitx { state->commitBuffer(); state->reset(); } - if (isSelectingAppMode_) { - if (keyEvent.isRelease()) - return; - + if (keyEvent.isRelease()) return; auto baseList = ic->inputPanel().candidateList(); auto menuList = std::dynamic_pointer_cast(baseList); KeySym keySym = keyEvent.key().sym(); - auto moveCursor = [&](int delta) { - if (!menuList || menuList->empty()) { - return false; - } - + if (!menuList || menuList->empty()) return false; int totalSize = menuList->totalSize(); - if (totalSize <= 1) { - return false; - } - + if (totalSize <= 1) return false; int cursorIndex = menuList->globalCursorIndex(); - if (cursorIndex < 1 || cursorIndex >= totalSize) { - cursorIndex = 1; - } - + if (cursorIndex < 1 || cursorIndex >= totalSize) { cursorIndex = 1; } int nextIndex = cursorIndex + delta; - if (nextIndex < 1) { - nextIndex = totalSize - 1; - } else if (nextIndex >= totalSize) { - nextIndex = 1; - } - + if (nextIndex < 1) { nextIndex = totalSize - 1; + } else if (nextIndex >= totalSize) { nextIndex = 1; } menuList->setGlobalCursorIndex(nextIndex); ic->updateUserInterface(UserInterfaceComponent::InputPanel); return true; }; - keyEvent.filterAndAccept(); - LotusMode selectedMode = LotusMode::NoMode; bool selectionMade = false; - switch (keySym) { case FcitxKey_Tab: - case FcitxKey_Down: { - if (moveCursor(1)) { - return; - } - break; - } + case FcitxKey_Down: { if (moveCursor(1)) return; break;} case FcitxKey_ISO_Left_Tab: - case FcitxKey_Up: { - if (moveCursor(-1)) { - return; - } - break; - } + 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; - } + if (selectedIndex < 1 || selectedIndex >= menuList->totalSize()) selectedIndex = 1; menuList->candidateFromAll(selectedIndex).select(ic); return; } break; } - case FcitxKey_1: { - selectedMode = LotusMode::Smooth; - break; - } - case FcitxKey_2: { - selectedMode = LotusMode::Uinput; - break; - } - case FcitxKey_3: { - selectedMode = LotusMode::UinputWine; - break; - } - case FcitxKey_4: { - selectedMode = LotusMode::SurroundingText; - break; - } - case FcitxKey_q: { - selectedMode = LotusMode::Preedit; - break; - } - case FcitxKey_w: { - selectedMode = LotusMode::Emoji; - break; - } - case FcitxKey_e: { - selectedMode = LotusMode::Off; - break; - } - case FcitxKey_r: { - selectedMode = modeStringToEnum(config_.mode.value()); - break; - } - case FcitxKey_Escape: { - selectionMade = true; - break; - } + case FcitxKey_1: selectedMode = LotusMode::Smooth; break; + case FcitxKey_2: selectedMode = LotusMode::Uinput; break; + case FcitxKey_3: selectedMode = LotusMode::UinputWine; break; + case FcitxKey_4: selectedMode = LotusMode::SurroundingText; break; + case FcitxKey_q: selectedMode = LotusMode::Preedit; break; + case FcitxKey_w: selectedMode = LotusMode::Emoji; break; + case FcitxKey_e: selectedMode = LotusMode::Off; break; + case FcitxKey_r: selectedMode = modeStringToEnum(config_.mode.value()); break; + case FcitxKey_Escape: selectionMade = true; break; default: { const auto& kl = *config_.modeMenuKey; if (kl.size() == 1 && !kl[0].hasModifier()) { @@ -478,36 +335,28 @@ namespace fcitx { break; } } - if (selectedMode != LotusMode::NoMode) { LOTUS_INFO("Selected mode: " + modeEnumToString(selectedMode)); if (selectedMode != LotusMode::Emoji) { setAppRule(currentConfigureApp_, selectedMode); - if (!isStartsWith(currentConfigureApp_, "ctx_")) { - saveAppRules(); - } + if (!isStartsWith(currentConfigureApp_, "ctx_")) saveAppRules(); } selectionMade = true; } - if (selectionMade) { isSelectingAppMode_ = false; ic->inputPanel().reset(); ic->updateUserInterface(UserInterfaceComponent::InputPanel); auto* state = ic->propertyFor(&factory_); - if (selectedMode != LotusMode::NoMode) { state->commitBuffer(); state->reset(); setMode(selectedMode, ic); - if (selectedMode == LotusMode::Emoji) { - state->updateEmojiPreedit(); - } + if (selectedMode == LotusMode::Emoji) { state->updateEmojiPreedit();} } } return; } - if (!keyEvent.isRelease() && !config_.modeMenuKey->empty() && keyEvent.key().checkKeyList(*config_.modeMenuKey)) { LOTUS_INFO("Mode menu key pressed"); currentConfigureApp_ = getProgramName(ic); @@ -522,14 +371,11 @@ namespace fcitx { const auto& text = s.text(); size_t textLen = fcitx_utf8_strlen(text.c_str()); unsigned int cursor = s.cursor(); - if (textLen == static_cast(cursor)) - realtextLen.store(static_cast(textLen), std::memory_order_release); + if (textLen == static_cast(cursor)) realtextLen.store(static_cast(textLen), std::memory_order_release); } - void LotusEngine::reset(const InputMethodEntry& /*entry*/, InputContextEvent& event) { auto* state = event.inputContext()->propertyFor(&factory_); - if (is_deleting_.load(std::memory_order_acquire)) - return; + if (is_deleting_.load(std::memory_order_acquire)) return; if (!state->isEmptyHistory() && event.type() != EventType::InputContextFocusOut) { int64_t now = now_ms(); if (now - state->lastSkippedResetMs_ >= 500) { @@ -545,7 +391,6 @@ namespace fcitx { state->reset(event.type() == EventType::InputContextFocusOut); } } - void LotusEngine::deactivate(const InputMethodEntry& /*entry*/, InputContextEvent& event) { auto* ic = event.inputContext(); auto* state = ic->propertyFor(&factory_); @@ -558,60 +403,48 @@ namespace fcitx { state->lastDeactivateTime_ = now_ms(); LOTUS_INFO("Skip clearAllBuffers"); } else { - if (surrvalid && state->oldPreBuffer_.empty()) - state->clearAllBuffers(); + if (surrvalid && state->oldPreBuffer_.empty()) state->clearAllBuffers(); } - if (!state->isReplacing()) - is_deleting_.store(false); + if (!state->isReplacing()) is_deleting_.store(false); needEngineReset.store(false); ic->inputPanel().reset(); - ic->updateUserInterface(UserInterfaceComponent::InputPanel); - if (realMode == LotusMode::Preedit) + if (realMode == LotusMode::Preedit || realMode == LotusMode::SurroundingText || realMode == LotusMode::Emoji) { + ic->updateUserInterface(UserInterfaceComponent::InputPanel); ic->updatePreedit(); + } } } - void LotusEngine::refreshEngine() { - if (!factory_.registered()) - return; + if (!factory_.registered()) return; instance_->inputContextManager().foreach ([this](InputContext* ic) { auto* state = ic->propertyFor(&factory_); state->setEngine(); - if (ic->hasFocus()) - state->reset(); + if (ic->hasFocus()) state->reset(); return true; }); } - void LotusEngine::refreshOption() { - if (!factory_.registered()) - return; + if (!factory_.registered()) return; instance_->inputContextManager().foreach ([this](InputContext* ic) { auto* state = ic->propertyFor(&factory_); state->setOption(); - if (ic->hasFocus()) - state->reset(); + if (ic->hasFocus()) state->reset(); return true; }); } - void LotusEngine::updateCharsetAction(InputContext* ic) { auto name = stringutils::concat(CharsetActionPrefix, *config_.outputCharset); for (const auto& action : charsetSubAction_) { action->setChecked(action->name() == name); - if (ic != nullptr) - action->update(ic); + if (ic != nullptr) action->update(ic); } } - void LotusEngine::loadAppRules() { { std::lock_guard lock(appRulesMutex_); std::unordered_map ctxRules; for (const auto& [app, mode] : appRules_) { - if (isStartsWith(app, "ctx_")) { - ctxRules[app] = mode; - } + if (isStartsWith(app, "ctx_")) { ctxRules[app] = mode;} } appRules_ = std::move(ctxRules); } @@ -621,14 +454,11 @@ namespace fcitx { return; } std::ifstream file(path); - if (!file.is_open()) - return; - + if (!file.is_open()) return; std::unordered_map tempRules; std::string line; while (std::getline(file, line)) { - if (line.empty() || line[0] == '#') - continue; + if (line.empty() || line[0] == '#') continue; auto delimiterPos = line.find('='); if (delimiterPos != std::string::npos) { std::string app = line.substr(0, delimiterPos); @@ -639,19 +469,14 @@ namespace fcitx { } } file.close(); - std::lock_guard lock(appRulesMutex_); - for (const auto& [app, mode] : tempRules) { - appRules_[app] = mode; - } + for (const auto& [app, mode] : tempRules) { appRules_[app] = mode;} }; loadFromFile(appRulesPath_); - std::lock_guard lock(appRulesMutex_); std::vector rules; for (const auto& pair : appRules_) { - if (pair.first.find("ctx_") == 0) - continue; + if (pair.first.find("ctx_") == 0) continue; lotusAppRule rule; rule.app.setValue(pair.first); rule.mode.setValue(static_cast(pair.second)); @@ -659,42 +484,30 @@ namespace fcitx { } appRulesTables_.rules.setValue(std::move(rules)); } - void LotusEngine::saveAppRules() const { // Method is const but locks mutable appRulesMutex_ to safely read appRules_ state std::ofstream file(appRulesPath_, std::ios::trunc); - if (!file.is_open()) - return; - + if (!file.is_open()) return; file << "# Lotus Per-App Configuration\n"; file << "# 0 = Off, 1 = Uinput (Smooth), 2 = Uinput (Slow), 3 = Uinput (Hardcore), 4 = Surrounding Text, 5 = Preedit, 6 = Emoji Picker\n"; std::lock_guard lock(appRulesMutex_); for (const auto& pair : appRules_) { bool currentIsCtx = isStartsWith(pair.first, "ctx_"); - if (!currentIsCtx) { - file << pair.first << "=" << static_cast(pair.second) << "\n"; - } + if (!currentIsCtx) { file << pair.first << "=" << static_cast(pair.second) << "\n"; } } file.close(); } - LotusMode LotusEngine::getAppRule(const std::string& appName) { std::lock_guard lock(appRulesMutex_); - auto it = appRules_.find(appName); - if (it != appRules_.end()) { - return it->second; - } - + if (it != appRules_.end()) return it->second; const auto globalMode = modeStringToEnum(config_.mode.value()); return globalMode; } - void LotusEngine::setAppRule(const std::string& appName, LotusMode mode) { std::lock_guard lock(appRulesMutex_); - auto rules = *appRulesTables_.rules; - - bool found = false; + auto rules = *appRulesTables_.rules; + bool found = false; for (auto& rule : rules) { if (*rule.app == appName) { rule.mode.setValue(static_cast(mode)); @@ -702,32 +515,26 @@ namespace fcitx { break; } } - if (!found) { lotusAppRule newRule; newRule.app.setValue(appName); newRule.mode.setValue(static_cast(mode)); rules.push_back(std::move(newRule)); } - appRules_[appName] = mode; appRulesTables_.rules.setValue(std::move(rules)); } - void LotusEngine::closeAppModeMenu() { isSelectingAppMode_ = false; g_mouse_clicked.store(false, std::memory_order_release); } - void LotusEngine::showAppModeMenu(InputContext* ic) { isSelectingAppMode_ = true; auto candidateList = std::make_unique(); candidateList->setLayoutHint(CandidateLayoutHint::Vertical); candidateList->setPageSize(10); auto getLabel = [&](const LotusMode& modeName, const std::string& modeLabel) { - if (modeName == realMode) { - return Text(">> " + modeLabel); - } + if (modeName == realMode) { return Text(">> " + modeLabel); } return Text(" " + modeLabel); }; auto cleanup = [this](InputContext* ic) { @@ -742,8 +549,7 @@ namespace fcitx { return [this, mode, cleanup](InputContext* ic) { if (mode != LotusMode::Emoji) { setAppRule(currentConfigureApp_, mode); - if (!isStartsWith(currentConfigureApp_, "ctx_")) - saveAppRules(); + if (!isStartsWith(currentConfigureApp_, "ctx_")) saveAppRules(); } cleanup(ic); setMode(mode, ic); @@ -753,7 +559,6 @@ namespace fcitx { } }; }; - candidateList->append(std::make_unique(Text(_("App: ") + currentConfigureApp_))); candidateList->append(std::make_unique(getLabel(LotusMode::Smooth, _("[1] Uinput (Smooth)")), applyMode(LotusMode::Smooth))); candidateList->append(std::make_unique(getLabel(LotusMode::Uinput, _("[2] Uinput (Slow)")), applyMode(LotusMode::Uinput))); @@ -762,28 +567,23 @@ namespace fcitx { candidateList->append(std::make_unique(getLabel(LotusMode::Preedit, _("[q] Preedit")), applyMode(LotusMode::Preedit))); candidateList->append(std::make_unique(getLabel(LotusMode::Emoji, _("[w] Emoji Picker")), applyMode(LotusMode::Emoji))); candidateList->append(std::make_unique(getLabel(LotusMode::Off, _("[e] OFF")), applyMode(LotusMode::Off))); - candidateList->append(std::make_unique(Text(_("[r] Default Typing")), [this, cleanup](InputContext* ic) { setMode(modeStringToEnum(config_.mode.value()), ic); cleanup(ic); })); - - { - const auto& kl = *config_.modeMenuKey; - if (kl.size() == 1 && !kl[0].hasModifier()) { - std::string charStr = Key::keySymToUTF8(kl[0].sym()); - if (!charStr.empty()) { - KeySym typeKeySym = typeKeyForModeMenuHotkey(kl[0].sym()); - std::string typeKeyLabel = Key::keySymToUTF8(typeKeySym); - std::string label = "[" + typeKeyLabel + "] " + _("Type") + " " + charStr; - candidateList->append(std::make_unique(Text(label), [cleanup, charStr](InputContext* ic) { - cleanup(ic); - ic->commitString(charStr); - })); - } + const auto& kl = *config_.modeMenuKey; + if (kl.size() == 1 && !kl[0].hasModifier()) { + std::string charStr = Key::keySymToUTF8(kl[0].sym()); + if (!charStr.empty()) { + KeySym typeKeySym = typeKeyForModeMenuHotkey(kl[0].sym()); + std::string typeKeyLabel = Key::keySymToUTF8(typeKeySym); + std::string label = "[" + typeKeyLabel + "] " + _("Type") + " " + charStr; + candidateList->append(std::make_unique(Text(label), [cleanup, charStr](InputContext* ic) { + cleanup(ic); + ic->commitString(charStr); + })); } } - int selectedIndex = 1; switch (realMode) { case LotusMode::Smooth: selectedIndex = 1; break; @@ -796,19 +596,14 @@ namespace fcitx { default: selectedIndex = 1; break; } candidateList->setGlobalCursorIndex(selectedIndex); - ic->inputPanel().reset(); ic->inputPanel().setCandidateList(std::move(candidateList)); ic->updateUserInterface(UserInterfaceComponent::InputPanel); } - void LotusEngine::setMode(LotusMode mode, InputContext* ic) { realMode = mode; - if (ic != nullptr) { - ic->updateUserInterface(UserInterfaceComponent::StatusArea); - } + if (ic != nullptr) { ic->updateUserInterface(UserInterfaceComponent::StatusArea);} } - std::string LotusEngine::subModeIconImpl(const InputMethodEntry& /*entry*/, InputContext& /*inputContext*/) { if (!*config_.useLotusIcons) { bool useBlack = *config_.useBlackDefaultIcons; @@ -824,7 +619,6 @@ namespace fcitx { default: return "fcitx-lotus"; } } - std::string LotusEngine::subModeLabelImpl(const InputMethodEntry& /*entry*/, InputContext& /*inputContext*/) { switch (realMode) { case LotusMode::Off: return _("Lotus - Off"); @@ -832,10 +626,8 @@ namespace fcitx { default: return isGnome_ ? "vi" : "🪷"; } } - std::string LotusEngine::getProgramName(InputContext* ic) { - if (ic == nullptr) - return "unknown-app"; + if (ic == nullptr) return "unknown-app"; std::string programName = ic->program(); if (programName.empty() || programName == "wayland" || programName == "x11") { // Fallback: InputContext address-based resolution diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index cf528b13..a1eedec9 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -777,13 +777,14 @@ namespace fcitx { // Ignore auto-capitalize side-effects if we're processing automated replacement backspaces bool isAutomatedBackspace = is_deleting_.load(std::memory_order_acquire) && isBackspace(currentSym); if (!isAutomatedBackspace) { - if (shouldCapitalize_) + if (shouldCapitalize_) { if (currentSym >= FcitxKey_a && currentSym <= FcitxKey_z) { auto upperSym = static_cast(currentSym - (FcitxKey_a - FcitxKey_A)); currentSym = upperSym; keyEvent.setKey(Key(upperSym, keyEvent.rawKey().states())); shouldCapitalize_ = false; } else if (currentSym != FcitxKey_space) shouldCapitalize_ = false; + } switch (currentSym) { case FcitxKey_period: case FcitxKey_exclam: diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-unikey-backend.cpp index 69159c0e..9182f0a0 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-unikey-backend.cpp @@ -6,32 +6,25 @@ * Native Unikey engine (fcitx5-unikey patterns; no Go/CGO). */ #ifdef LOTUS_ENGINE_UNIKEY - #include "lotus-input-backend.hpp" #include "lotus-config.h" #include "lotus-engine.h" #include "../unikey/LotusUnikeyEngine.hpp" #include "unikeyinputcontext.h" - #include #include #include #include #include - #include - namespace fcitx { - namespace { - static bool isWordBreakSym(unsigned char c) { static const std::unordered_set WordBreakSyms = { ',', ';', ':', '.', '\"', '\'', '!', '?', ' ', }; return WordBreakSyms.contains(c); } - static UkInputMethod mapLotusIm(const std::string& name) { if (name.find("Telex 2") != std::string::npos && name.find("VNI") == std::string::npos) return UkSimpleTelex2; @@ -47,7 +40,6 @@ namespace fcitx { return UkTelex; return UkSimpleTelex; } - class LotusUnikeyInputBackend final : public LotusInputBackend { public: void recreateEngine(LotusEngine* engine) override { @@ -56,87 +48,62 @@ namespace fcitx { applyFromConfig(engine); resetEngine(); } - void setOptions(LotusEngine* engine) override { applyFromConfig(engine); } - void resetEngine() override { pendingPullCommit_.clear(); preeditStr_.clear(); lastShiftPressed_ = FcitxKey_None; lastKeyWithShift_ = false; autoCommit_ = false; - if (uk_) - uk_->resetBuf(); + if (uk_) uk_->resetBuf(); } - void rebuildFromText(const char* utf8) override { resetEngine(); - if (!uk_ || utf8 == nullptr) - return; + if (!uk_ || utf8 == nullptr) return; for (auto ucs : utf8::MakeUTF8CharRange(std::string_view(utf8))) { if (ucs < 128U) uk_->putChar(static_cast(ucs)); - else - uk_->putChar(ucs); + else uk_->putChar(ucs); } syncState(FcitxKey_None); } - bool processKeyEventAndPull(uint32_t sym, uint32_t state, std::string* commit, std::string* preedit) override { pendingPullCommit_.clear(); bool ok = dispatch(sym, state); - if (commit) - *commit = pendingPullCommit_; - if (preedit) - *preedit = preeditStr_; + if (commit) *commit = pendingPullCommit_; + if (preedit) *preedit = preeditStr_; pendingPullCommit_.clear(); return ok; } - bool processKeyEvent(uint32_t sym, uint32_t state) override { pendingPullCommit_.clear(); return dispatch(sym, state); } - void pullCommitAndPreedit(std::string* commit, std::string* preedit) override { - if (commit) - *commit = pendingPullCommit_; - if (preedit) - *preedit = preeditStr_; + if (commit) *commit = pendingPullCommit_; + if (preedit) *preedit = preeditStr_; pendingPullCommit_.clear(); } - void pullCommit(std::string* out) override { - if (out) - *out = pendingPullCommit_; + if (out) *out = pendingPullCommit_; pendingPullCommit_.clear(); } - void pullPreedit(std::string* out) override { - if (out) - *out = preeditStr_; + if (out) *out = preeditStr_; } - void commitPreedit() override { - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; + if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - if (uk_) - uk_->resetBuf(); + if (uk_) uk_->resetBuf(); } - private: void applyFromConfig(LotusEngine* engine) { - if (!uk_) - return; - + if (!uk_) return; UkInputMethod currentIM_ = mapLotusIm(engine->config().inputMethod.value()); - uk_->setInputMethod(currentIM_); uk_->setOutputCharset(CONV_CHARSET_XUTF8); - UnikeyOptions opt{}; opt.freeMarking = *engine->config().freeMarking ? 1 : 0; opt.modernStyle = *engine->config().modernStyle ? 1 : 0; @@ -147,10 +114,8 @@ namespace fcitx { opt.useIME = 0; opt.spellCheckEnabled = *engine->config().spellCheck ? 1 : 0; opt.autoNonVnRestore = *engine->config().autoNonVnRestore ? 1 : 0; - uk_->setOptions(&opt); } - void eraseChars(int num_chars) { int i; int k = num_chars; @@ -162,7 +127,6 @@ namespace fcitx { } preeditStr_.erase(static_cast(i + 1)); } - void syncState(KeySym sym) { auto* uic = uk_->context(); if (uic->backspaces() > 0) { @@ -177,34 +141,25 @@ namespace fcitx { preeditStr_.append(utf8::UCS4ToUTF8(sym)); } } - bool dispatch(uint32_t sym, uint32_t state) { - if (!uk_) - return false; - + if (!uk_) return false; KeyStates st(static_cast(state)); const auto rawSym = static_cast(sym); - if (st.testAny(KeyState::Ctrl_Alt) || rawSym == FcitxKey_Control_L || rawSym == FcitxKey_Control_R || rawSym == FcitxKey_Tab || rawSym == FcitxKey_Return || rawSym == FcitxKey_Delete || rawSym == FcitxKey_KP_Enter || (rawSym >= FcitxKey_Home && rawSym <= FcitxKey_Insert) || (rawSym >= FcitxKey_KP_Home && rawSym <= FcitxKey_KP_Delete)) { uk_->context()->filter(0); - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; + if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); return false; } - if (st.test(KeyState::Super)) - return false; - if ((rawSym >= FcitxKey_Caps_Lock && rawSym <= FcitxKey_Hyper_R) || rawSym == FcitxKey_Shift_L || rawSym == FcitxKey_Shift_R) - return false; - + if (st.test(KeyState::Super)) return false; + if ((rawSym >= FcitxKey_Caps_Lock && rawSym <= FcitxKey_Hyper_R) || rawSym == FcitxKey_Shift_L || rawSym == FcitxKey_Shift_R) return false; if (rawSym == FcitxKey_BackSpace) { uk_->backspacePress(); if (uk_->context()->backspaces() == 0 || preeditStr_.empty()) { - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; + if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); return !pendingPullCommit_.empty(); @@ -217,58 +172,44 @@ namespace fcitx { preeditStr_.append(reinterpret_cast(uk_->context()->buf()), static_cast(uk_->context()->bufChars())); return true; } - if (rawSym >= FcitxKey_KP_Multiply && rawSym <= FcitxKey_KP_9) { uk_->context()->filter(0); - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; + if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); return false; } - if (rawSym >= FcitxKey_space && rawSym <= FcitxKey_asciitilde) { - const bool beginWord = uk_->isAtWordBeginning(); - + //const bool beginWord = uk_->isAtWordBeginning(); uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); - uk_->filter(sym); syncState(rawSym); - if (!preeditStr_.empty() && preeditStr_.back() == static_cast(sym) && isWordBreakSym(static_cast(sym))) { pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); return true; } - return true; } - uk_->context()->filter(0); syncState(rawSym); - if (!preeditStr_.empty()) - pendingPullCommit_ = preeditStr_; + if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); uk_->resetBuf(); return false; } - std::unique_ptr<::fcitx::lotus::LotusUnikeyEngine> uk_; - LotusEngine* engineRef_ = nullptr; - std::string preeditStr_; - std::string pendingPullCommit_; - KeySym lastShiftPressed_ = FcitxKey_None; - bool lastKeyWithShift_ = false; - bool autoCommit_ = false; + LotusEngine* engineRef_ = nullptr; + std::string preeditStr_; + std::string pendingPullCommit_; + KeySym lastShiftPressed_ = FcitxKey_None; + bool lastKeyWithShift_ = false; + bool autoCommit_ = false; }; - } // namespace - std::unique_ptr makeLotusInputBackend() { return std::make_unique(); } - } // namespace fcitx - #endif // LOTUS_ENGINE_UNIKEY From 0717ed106b00b44ef79391b047db5195a94ff733 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Thu, 14 May 2026 19:16:25 +0700 Subject: [PATCH 33/42] fix firefox and reduce work on loop active-deactive spam --- src/app_quirks.h | 19 +++++++++++++++---- src/lotus-engine.cpp | 23 ++++++++++++++++++++++- src/lotus-engine.h | 4 +--- src/lotus-state.cpp | 37 ++++++++++--------------------------- 4 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/app_quirks.h b/src/app_quirks.h index 446f736d..51485c30 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -15,18 +15,29 @@ #include #include + +// TODO: alow user to set delay config per app +// set defaut config in /etc /** * @brief List of application names requiring ACK workaround. * * Chromium-based browsers that need special handling for text replacement. */ -inline constexpr std::array ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", - "cromite", "helium", "thorium", "slimjet", "yandex", "vesktop", "obsidian"}; +inline constexpr std::array ack_apps = { + "chrome", "chromium", "brave", "edge", "vivaldi", + "opera", "coccoc", "cromite", "helium", "thorium", + "slimjet", "yandex", "vesktop", "obsidian", "mullvad", + "firefox", "zen" +}; /** * @brief List of application names have goood support surrowding text * */ -inline constexpr std::array surrtp_apps = {"soffice"}; +inline constexpr std::array surrtp_apps = { + "soffice" +}; -inline constexpr std::array terminalm = {"foot", "kitty", "alacritty", "ghostty", "st"}; +inline constexpr std::array terminalm = { + "foot", "kitty", "alacritty", "ghostty", "st" +}; diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index bceb50d5..4578976e 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -29,6 +29,22 @@ namespace fcitx { constexpr const char* CharsetActionPrefix = "lotus-charset-"; const std::string CustomKeymapFile = "conf/lotus-custom-keymap.conf"; const std::string MacroTableFile = "conf/lotus-macro-table.conf"; + class AppActivationCache { + public: + bool needsSetup(InputContext* ic, LotusMode mode) { + auto* group = ic->focusGroup(); + ICUUID uuid = ic->uuid(); + if (group == lastGroup_ && uuid == lastUUID_ && mode == lastMode_) return false; + lastGroup_ = group; lastUUID_ = uuid; lastMode_ = mode; + return true; + } + void invalidate() { lastGroup_ = nullptr; } + private: + FocusGroup* lastGroup_ = nullptr; + ICUUID lastUUID_ = {}; + LotusMode lastMode_ = LotusMode::NoMode; + }; + static AppActivationCache s_activationCache; // Returns the KeySym that triggers the "Type hotkey char" action in the mode menu. // If the hotkey itself conflicts with a reserved menu key, falls back to FcitxKey_f. static bool isAppModeMenuReservedKey(KeySym sym) { @@ -171,6 +187,7 @@ namespace fcitx { } } std::string LotusEngine::subMode(const InputMethodEntry& /*entry*/, InputContext& /*inputContext*/) {return *config_.inputMethod;} + void LotusEngine::activate(const InputMethodEntry& /*entry*/, InputContextEvent& event) { auto* ic = event.inputContext(); const bool surrvalid = ic->surroundingText().isValid(); @@ -183,9 +200,11 @@ namespace fcitx { LOTUS_INFO("App name: " + appName); const LotusMode targetMode = getAppRule(appName); LOTUS_INFO("Target mode: " + modeEnumToString(targetMode)); + auto* state = ic->propertyFor(&factory_); + const bool alreadySetup = !s_activationCache.needsSetup(ic, targetMode); +if (!alreadySetup) { updateCharsetAction(event.inputContext()); setMode(targetMode, event.inputContext()); - auto* state = ic->propertyFor(&factory_); // Workaround for chromium wayland issue where suggestions cause a doubled // first character. Forwarding may prevent BS from being sent // to the client. @@ -243,6 +262,7 @@ namespace fcitx { } if (event.type() == EventType::InputContextFocusIn && is_dbus && !surrvalid) { LOTUS_INFO("Skip clearAllBuffers"); } else if (surrvalid && !state->oldPreBuffer_.empty() && (now_ms() - state->lastDeactivateTime_) < 100) { state->clearAllBuffers(); } +} if (!state->isReplacing()) is_deleting_.store(false); needEngineReset.store(false); if (targetMode == LotusMode::Emoji) { @@ -415,6 +435,7 @@ namespace fcitx { } } void LotusEngine::refreshEngine() { + s_activationCache.invalidate(); if (!factory_.registered()) return; instance_->inputContextManager().foreach ([this](InputContext* ic) { auto* state = ic->propertyFor(&factory_); diff --git a/src/lotus-engine.h b/src/lotus-engine.h index a96bfce3..dab513ca 100644 --- a/src/lotus-engine.h +++ b/src/lotus-engine.h @@ -25,11 +25,9 @@ #include #include #include - namespace fcitx { class LotusState; - /** * @brief Main engine class for Lotus input method. * @@ -303,7 +301,7 @@ namespace fcitx { * @param ic Current input context. * @return Name of current program */ - std::string getProgramName(InputContext* ic); + static std::string getProgramName(InputContext* ic); }; /** diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index a1eedec9..d83041ef 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -100,9 +100,9 @@ namespace fcitx { } } //HACK - //if (waitAck_) { + if (waitAck_) { LOTUS_INFO("Waiting for ack"); - // LOTUS_INFO("chrome x11 hit me"); + LOTUS_INFO("chrome x11 hit me"); char ack; recv(uinput_client_fd_, &ack, sizeof(ack), MSG_NOSIGNAL); // keep safe that bs is finish by app @@ -110,10 +110,11 @@ namespace fcitx { replacement_start_ms_.store(0, std::memory_order_release); // ez way but cause alot of problem //std::this_thread::sleep_for(std::chrono::milliseconds(count * 5)); - //} else { - // LOTUS_INFO("firefox hit me"); - // std::this_thread::sleep_for(std::chrono::milliseconds(count * 2)); - //} + } else { + LOTUS_INFO("firefox hit me"); + std::this_thread::sleep_for(std::chrono::milliseconds(count * 10)); + replacement_start_ms_.store(0, std::memory_order_release); + } } void LotusState::send_backspace_forward(int count) const { if (count <= 0) return; @@ -587,27 +588,9 @@ namespace fcitx { isCommit = true; } } - if (!wa_flag) - if (!isCommit) { - keyEvent.forward(); - bool hasMultibyte = false; - for (unsigned char c : oldPreBuffer_) - if (c > 0x7F) { - hasMultibyte = true; - break; - } - if (!hasMultibyte && -#if defined(LOTUS_ENABLE_AVX512) && defined(__AVX512F__) - utf8_length_avx512(oldPreBuffer_.data(), oldPreBuffer_.size()) -#else - utf8::length(oldPreBuffer_) -#endif - > 8) { - inputBackend_->resetEngine(); - hasHistory_ = false; - oldPreBuffer_.clear(); - } - } + if (!wa_flag && !isCommit) { + keyEvent.forward(); + } } else { if (uinput_client_fd_ < 0) { LOTUS_ERROR("Cannot connect to uinput server, commit rawkey"); From 78b7e3c9eb91470a2906cd1c0d15e3a93abe7838 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 15 May 2026 11:04:41 +0700 Subject: [PATCH 34/42] eee --- server/CMakeLists.txt | 2 +- server/lotus-logger.cpp | 86 ------------ server/lotus-logger.h | 115 ---------------- server/lotus-server.cpp | 292 ++++++++++++++-------------------------- server/lotus-server.h | 131 +++--------------- src/app_quirks.h | 4 +- src/lotus-engine.cpp | 15 +-- src/lotus-utils.h | 10 +- unikey/core/inputproc.h | 2 +- 9 files changed, 136 insertions(+), 521 deletions(-) delete mode 100644 server/lotus-logger.cpp delete mode 100644 server/lotus-logger.h diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index d0957e3e..0d1079f1 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -3,7 +3,7 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(LIBINPUT REQUIRED IMPORTED_TARGET libinput) pkg_check_modules(LIBUDEV REQUIRED IMPORTED_TARGET libudev) -add_executable(fcitx5-lotus-server lotus-server.cpp lotus-logger.cpp) +add_executable(fcitx5-lotus-server lotus-server.cpp) target_link_libraries(fcitx5-lotus-server PkgConfig::LIBINPUT diff --git a/server/lotus-logger.cpp b/server/lotus-logger.cpp deleted file mode 100644 index c5e904da..00000000 --- a/server/lotus-logger.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 Nguyễn Hoàng Kỳ - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -#include "lotus-logger.h" - -#include -#include -#include -#include -#include -#include - -#include - -LotusLogger::LotusLogger(std::string log_file, LogLevel level) : log_file_(std::move(log_file)) { - level_.store(level); - - std::filesystem::path path(log_file_); - if (path.has_parent_path()) { - std::filesystem::create_directories(path.parent_path()); - } - - file_.open(log_file_, std::ios_base::app | std::ios_base::out); - - if (!file_.is_open()) { - std::cerr << "[ERROR] Failed to open log file: " << log_file_ << '\n'; - } -} - -LotusLogger::~LotusLogger() { - if (file_.is_open()) { - file_.close(); - } -} - -void LotusLogger::setLevel(LogLevel level) { - level_.store(level); -} - -bool LotusLogger::isEnabled(LogLevel level) const { - return level >= level_.load(); -} - -void LotusLogger::log(LogLevel level, const std::string& message) { - if (!isEnabled(level) || !file_.is_open()) { - return; - } - - std::lock_guard lock(mutex_); - - std::string entry = getTimestamp() + " [" + levelToString(level) + "] " + message + "\n"; - - file_ << entry; - if (level >= LogLevel::WARN) { - file_.flush(); - } -} - -std::string LotusLogger::getTimestamp() { - auto now = std::chrono::system_clock::now(); - auto time_t = std::chrono::system_clock::to_time_t(now); - // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) - auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; - - struct tm buf{}; - localtime_r(&time_t, &buf); - - std::stringstream ss; - ss << std::put_time(&buf, "%Y-%m-%d %H:%M:%S"); - ss << "." << std::setfill('0') << std::setw(3) << ms.count(); - return ss.str(); -} - -std::string LotusLogger::levelToString(LogLevel level) { - switch (level) { - case LogLevel::DEBUG: return "DEBUG"; - case LogLevel::INFO: return "INFO"; - case LogLevel::WARN: return "WARN"; - case LogLevel::ERROR: return "ERROR"; - default: return "UNKNOWN"; - } -} \ No newline at end of file diff --git a/server/lotus-logger.h b/server/lotus-logger.h deleted file mode 100644 index d2227dd9..00000000 --- a/server/lotus-logger.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 Nguyễn Hoàng Kỳ - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -/** - * @file lotus-logger.h - * @brief Simple file logger with rotation for fcitx5-lotus-server - * - * Features: - * - Thread-safe logging - * - Automatic file rotation (max 10MB, keep 5 files) - * - Timestamp and level formatting - * - Configurable log levels - */ - -#ifndef _LOTUS_LOGGER_H_ -#define _LOTUS_LOGGER_H_ - -#include -#include -#include -#include -#include - -enum class LogLevel : std::uint8_t { - DEBUG, - INFO, - WARN, - ERROR, - NONE -}; - -class LotusLogger { - public: - /** - * @brief Instance constructor - */ - static LotusLogger& instance() { - static LotusLogger instance_; - return instance_; - } - - /** - * @brief Destructor - */ - ~LotusLogger(); - - // Rule of five - LotusLogger(const LotusLogger&) = delete; - LotusLogger& operator=(const LotusLogger&) = delete; - LotusLogger(LotusLogger&&) = delete; - LotusLogger& operator=(LotusLogger&&) = delete; - - /** - * @brief Set minimum log level - */ - void setLevel(LogLevel level); - - /** - * @brief Check if logging is enabled for given level - */ - bool isEnabled(LogLevel level) const; - - /** - * @brief Log a message - */ - void log(LogLevel level, const std::string& message); - - // Convenience methods - void debug(const std::string& msg) { - if (isEnabled(LogLevel::DEBUG)) - log(LogLevel::DEBUG, msg); - } - void info(const std::string& msg) { - if (isEnabled(LogLevel::INFO)) - log(LogLevel::INFO, msg); - } - void warn(const std::string& msg) { - if (isEnabled(LogLevel::WARN)) - log(LogLevel::WARN, msg); - } - void error(const std::string& msg) { - if (isEnabled(LogLevel::ERROR)) - log(LogLevel::ERROR, msg); - } - - private: - /** - * @brief Constructor - * @param log_file Path to log file - * @param max_size Maximum file size before rotation (bytes) - * @param max_files Maximum number of backup files to keep - * @param level Minimum log level to output - */ - LotusLogger(std::string log_file = "/tmp/fcitx5-lotus-server.log", LogLevel level = LogLevel::INFO); - - /** - * @brief Get current timestamp string - */ - static std::string getTimestamp(); - - /** - * @brief Get log level string - */ - static std::string levelToString(LogLevel level); - - std::string log_file_; - std::atomic level_; - std::ofstream file_; - std::mutex mutex_; -}; -#endif // _LOTUS_LOGGER_H_ \ No newline at end of file diff --git a/server/lotus-server.cpp b/server/lotus-server.cpp index 39b3625d..5354d75f 100644 --- a/server/lotus-server.cpp +++ b/server/lotus-server.cpp @@ -3,58 +3,43 @@ * SPDX-FileCopyrightText: 2026 Nguyễn Hoàng Kỳ * * SPDX-License-Identifier: GPL-3.0-or-later - * */ #include "lotus-server.h" -#include "lotus-logger.h" - -#include -#include +//#include #include -#include +#include +#include #include std::atomic g_running{true}; -FdGuard::~FdGuard() { - reset(); -} +FdGuard::~FdGuard() { reset(); } -FdGuard::FdGuard(FdGuard&& other) noexcept : fd_(other.fd_) { - other.fd_ = -1; -} +FdGuard::FdGuard(FdGuard&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } FdGuard& FdGuard::operator=(FdGuard&& other) noexcept { - if (this != &other) { - reset(other.fd_); - other.fd_ = -1; - } + if (this != &other) { reset(other.fd_); other.fd_ = -1; } return *this; } void FdGuard::reset(int new_fd) { - if (fd_ >= 0) - close(fd_); + if (fd_ >= 0) close(fd_); fd_ = new_fd; } UinputDevice::~UinputDevice() { - if (guard_.is_valid()) { - ioctl(guard_.get(), UI_DEV_DESTROY); - } + if (guard_.is_valid()) ioctl(guard_.get(), UI_DEV_DESTROY); } bool UinputDevice::initialize() { int fd = open("/dev/uinput", O_WRONLY); - if (fd < 0) - return false; + if (fd < 0) return false; guard_.reset(fd); - if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || ioctl(fd, UI_SET_KEYBIT, KEY_BACKSPACE) < 0) { + if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || ioctl(fd, UI_SET_KEYBIT, KEY_BACKSPACE) < 0) return false; - } struct uinput_setup usetup{}; usetup.id.bustype = BUS_USB; @@ -62,28 +47,29 @@ bool UinputDevice::initialize() { usetup.id.product = 0x5678; strncpy(usetup.name, "Lotus-Uinput-Server", UINPUT_MAX_NAME_SIZE - 1); - if (ioctl(fd, UI_DEV_SETUP, &usetup) < 0 || ioctl(fd, UI_DEV_CREATE) < 0) { + if (ioctl(fd, UI_DEV_SETUP, &usetup) < 0 || ioctl(fd, UI_DEV_CREATE) < 0) return false; - } + sleep(1); return true; } void UinputDevice::send_backspace() { - if (!guard_.is_valid()) - return; - struct input_event ev[4]{}; - ev[0].type = EV_KEY; - ev[0].code = KEY_BACKSPACE; - ev[0].value = 1; - ev[1].type = EV_SYN; - ev[1].code = SYN_REPORT; - ev[2].type = EV_KEY; - ev[2].code = KEY_BACKSPACE; - ev[2].value = 0; - ev[3].type = EV_SYN; - ev[3].code = SYN_REPORT; - (void)write(guard_.get(), ev, sizeof(ev)); + if (!guard_.is_valid()) return; + static struct input_event ev[4] = { + {.time = {}, .type = EV_KEY, .code = KEY_BACKSPACE, .value = 1}, + {.time = {}, .type = EV_SYN, .code = SYN_REPORT, .value = 0}, + {.time = {}, .type = EV_KEY, .code = KEY_BACKSPACE, .value = 0}, + {.time = {}, .type = EV_SYN, .code = SYN_REPORT, .value = 0}, + }; + int fd = guard_.get(); + ssize_t ret; + __asm__ volatile ( + "syscall" + : "=a"(ret) + : "0"(1L), "D"((long)fd), "S"(ev), "d"(sizeof(ev)) + : "rcx", "r11", "memory" + ); } LibinputContext::LibinputContext(const struct libinput_interface* interface) : udev_(udev_new()) { @@ -99,61 +85,44 @@ LibinputContext::LibinputContext(const struct libinput_interface* interface) : u } LibinputContext::~LibinputContext() { - if (li_ != nullptr) - libinput_unref(li_); - if (udev_ != nullptr) - udev_unref(udev_); + if (li_ != nullptr) libinput_unref(li_); + if (udev_ != nullptr) udev_unref(udev_); } void signal_handler(int sig) { - if (sig == SIGTERM || sig == SIGINT) { - g_running.store(false); - } + if (sig == SIGTERM || sig == SIGINT) g_running.store(false); } -std::string get_current_username() { - struct passwd pwd{}; - struct passwd* result = nullptr; - long buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); - if (buf_size == -1) { - buf_size = 16384; +bool get_current_username(char* out, size_t len) { + struct passwd pwd{}; + struct passwd* result = nullptr; + char buf[128]; + if (getpwuid_r(getuid(), &pwd, buf, sizeof(buf), &result) == 0 && result) { + strncpy(out, result->pw_name, len - 1); + return true; } - std::vector buf(buf_size); - std::string username; - int res = getpwuid_r(getuid(), &pwd, buf.data(), buf_size, &result); - if (res == 0 && result != nullptr) { - username = result->pw_name; - } else { - username = "unknown"; - } - return username; + strncpy(out, "unknown", len - 1); + return false; } -uid_t get_uid_for_user(const std::string& username) { - struct passwd pw_buf{}; +uid_t get_uid_for_user(const char* username) { + struct passwd pw_buf{}; struct passwd* pw = nullptr; - char buf[1024]; - int res = getpwnam_r(username.c_str(), &pw_buf, buf, sizeof(buf), &pw); - if (res == 0 && pw != nullptr) { + char buf[128]; + if (getpwnam_r(username, &pw_buf, buf, sizeof(buf), &pw) == 0 && pw) return pw->pw_uid; - } return (uid_t)-1; } void boost_process_priority() { - if (setpriority(PRIO_PROCESS, 0, -10) != 0) { //NOLINT - ;//LotusLogger::instance().error("Failed to boost process priority"); - } + setpriority(PRIO_PROCESS, 0, -10); //NOLINT } void pin_to_pcore() { cpu_set_t cpuset; CPU_ZERO(&cpuset); - for (int i = 0; i <= 3; ++i) - CPU_SET(i, &cpuset); - if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { - ;//LotusLogger::instance().error("Failed to pin process to core"); - } + for (int i = 0; i <= 3; ++i) CPU_SET(i, &cpuset); + sched_setaffinity(0, sizeof(cpuset), &cpuset); } int open_restricted(const char* path, int flags, void* /*user_data*/) { @@ -161,9 +130,7 @@ int open_restricted(const char* path, int flags, void* /*user_data*/) { return fd < 0 ? -errno : fd; } -void close_restricted(int fd, void* /*user_data*/) { - close(fd); -} +void close_restricted(int fd, void* /*user_data*/) { close(fd); } const struct libinput_interface interface = { .open_restricted = open_restricted, @@ -171,110 +138,80 @@ const struct libinput_interface interface = { }; int main(int argc, char* argv[]) { - std::string target_user; + char target_user[64] = {0}; if (argc == 3 && strcmp(argv[1], "-u") == 0) { // NOLINT - target_user = argv[2]; // NOLINT + strncpy(target_user, argv[2], sizeof(target_user) - 1); + target_user[sizeof(target_user) - 1] = '\0'; } else { - target_user = get_current_username(); + if (!get_current_username(target_user, sizeof(target_user))) return 1; } - //LotusLogger::instance().info("Target user: " + target_user); uid_t expected_uid = get_uid_for_user(target_user); - if (expected_uid == (uid_t)-1) { - //LotusLogger::instance().error("Failed to find UID for target user: " + target_user); - return 1; - } + if (expected_uid == (uid_t)-1) return 1; boost_process_priority(); pin_to_pcore(); - std::string backspace_socket; - backspace_socket.reserve(40); - backspace_socket += "lotussocket-"; - backspace_socket += target_user; - backspace_socket += "-kb_socket"; + char backspace_socket[128]; + char mouse_flag_socket[128]; + snprintf(backspace_socket, sizeof(backspace_socket), "lotussocket-%s-kb_socket", target_user); + snprintf(mouse_flag_socket, sizeof(mouse_flag_socket), "lotussocket-%s-mouse_socket", target_user); + size_t kb_str_len = strlen(backspace_socket); + size_t mouse_str_len = strlen(mouse_flag_socket); - std::string mouse_flag_socket; - mouse_flag_socket.reserve(48); - mouse_flag_socket += "lotussocket-"; - mouse_flag_socket += target_user; - mouse_flag_socket += "-mouse_socket"; - - const size_t max_socket_path_length = UNIX_PATH_MAX - 1; - backspace_socket.resize(std::min(backspace_socket.length(), max_socket_path_length)); - mouse_flag_socket.resize(std::min(mouse_flag_socket.length(), max_socket_path_length)); - - // Setup Uinput UinputDevice uinput; - if (!uinput.initialize()) { - //LotusLogger::instance().error("Failed to initialize uinput device"); - return 1; - } + if (!uinput.initialize()) return 1; - FdGuard server_fd(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0)); - FdGuard mouse_server_fd(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0)); + FdGuard server_fd(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0)); + FdGuard mouse_server_fd(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0)); struct sockaddr_un addr_kb{}; struct sockaddr_un addr_mouse{}; - addr_kb.sun_family = AF_UNIX; addr_mouse.sun_family = AF_UNIX; - addr_kb.sun_path[0] = '\0'; addr_mouse.sun_path[0] = '\0'; + memcpy(&addr_kb.sun_path[1], backspace_socket, kb_str_len); + memcpy(&addr_mouse.sun_path[1], mouse_flag_socket, mouse_str_len); - memcpy(&addr_kb.sun_path[1], backspace_socket.c_str(), backspace_socket.length()); - memcpy(&addr_mouse.sun_path[1], mouse_flag_socket.c_str(), mouse_flag_socket.length()); + socklen_t kb_len = offsetof(struct sockaddr_un, sun_path) + kb_str_len + 1; + socklen_t mouse_len = offsetof(struct sockaddr_un, sun_path) + mouse_str_len + 1; - socklen_t kb_len = offsetof(struct sockaddr_un, sun_path) + backspace_socket.length() + 1; - socklen_t mouse_len = offsetof(struct sockaddr_un, sun_path) + mouse_flag_socket.length() + 1; - - if (bind(server_fd.get(), (struct sockaddr*)&addr_kb, kb_len) != 0) { - //LotusLogger::instance().error("Failed to bind socket"); - return 1; - } - - if (bind(mouse_server_fd.get(), (struct sockaddr*)&addr_mouse, mouse_len) != 0) { - //LotusLogger::instance().error("Failed to bind socket"); - return 1; - } + if (bind(server_fd.get(), (struct sockaddr*)&addr_kb, kb_len) != 0) return 1; + if (bind(mouse_server_fd.get(), (struct sockaddr*)&addr_mouse, mouse_len) != 0) return 1; listen(server_fd.get(), 5); listen(mouse_server_fd.get(), 5); LibinputContext li_ctx(&interface); - if (!li_ctx.is_valid()) { - //LotusLogger::instance().error("Failed to create libinput/udev context"); - return 1; - } + if (!li_ctx.is_valid()) return 1; - std::vector fds; - const int KB_CLIENT_INDEX = 3; - fds.push_back({server_fd.get(), POLLIN, 0}); - fds.push_back({li_ctx.get_fd(), POLLIN, 0}); - fds.push_back({mouse_server_fd.get(), POLLIN, 0}); - fds.push_back({-1, POLLIN, 0}); // Keyboard socket client + constexpr int KB_CLIENT_INDEX = 3; + struct pollfd fds[4] = { + {server_fd.get(), POLLIN, 0}, + {li_ctx.get_fd(), POLLIN, 0}, + {mouse_server_fd.get(), POLLIN, 0}, + {-1, POLLIN, 0}, + }; - FdGuard addon_fd; - FdGuard kb_client_fd; - int pending_backspaces = 0; + FdGuard addon_fd; + FdGuard kb_client_fd; + int pending_backspaces = 0; struct sigaction sa{}; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, nullptr); - sigaction(SIGINT, &sa, nullptr); + sigaction(SIGINT, &sa, nullptr); int64_t last_bs_ms = 0; while (g_running.load(std::memory_order_acquire)) { int poll_timeout = (pending_backspaces > 0) ? 1 : -1; - int ret = poll(fds.data(), fds.size(), poll_timeout); + int ret = poll(fds, 4, poll_timeout); if (ret < 0) { - if (errno == EINTR) { - continue; - } + if (errno == EINTR) continue; break; } @@ -295,39 +232,25 @@ int main(int argc, char* argv[]) { libinput_dispatch(li_ctx.get_li()); - // handle socket (backspace) if ((fds[0].revents & POLLIN) != 0) { int client_fd = accept4(server_fd.get(), nullptr, nullptr, SOCK_NONBLOCK); if (client_fd >= 0) { struct ucred cred{}; - socklen_t len = sizeof(struct ucred); - char exe_path[PATH_MAX] = {0}; - + socklen_t cred_len = sizeof(struct ucred); bool authorized = false; - if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0) { - if (cred.uid == expected_uid) { - char path[64]; - snprintf(path, sizeof(path), "/proc/%d/exe", cred.pid); - - ssize_t ret = readlink(path, exe_path, sizeof(exe_path) - 1); - if (ret != -1) { - exe_path[ret] = '\0'; // NOLINT - } - - if (strcmp(exe_path, "/usr/bin/fcitx5") == 0) { - authorized = true; - } else { - ;//LotusLogger::instance().warn("Unauthorized executable connection attempt to keyboard socket from: " + std::string(exe_path)); - } - } else { - ;//LotusLogger::instance().warn("Unauthorized UID connection attempt to keyboard socket from UID: " + std::to_string(cred.uid)); - } - } else { - ;//LotusLogger::instance().warn("Failed to get peer credentials for keyboard socket"); + + if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) == 0 + && cred.uid == expected_uid) + { + char path[64]; + char exe_path[64] = {0}; + snprintf(path, sizeof(path), "/proc/%d/exe", cred.pid); + ssize_t n = readlink(path, exe_path, sizeof(exe_path) - 1); + if (n > 0) exe_path[n] = '\0'; + authorized = (strcmp(exe_path, "/usr/bin/fcitx5") == 0); } if (authorized) { - //LotusLogger::instance().info("Fcitx5 connected to keyboard socket (PID: " + std::to_string(cred.pid) + ")"); kb_client_fd.reset(client_fd); fds[KB_CLIENT_INDEX].fd = kb_client_fd.get(); } else { @@ -336,12 +259,12 @@ int main(int argc, char* argv[]) { } } - // handle connect from addon - if (fds[KB_CLIENT_INDEX].fd >= 0 && (fds[KB_CLIENT_INDEX].revents & (POLLIN | POLLHUP | POLLERR)) != 0) { + if (fds[KB_CLIENT_INDEX].fd >= 0 + && (fds[KB_CLIENT_INDEX].revents & (POLLIN | POLLHUP | POLLERR)) != 0) + { int count = 0; ssize_t n = recv(fds[KB_CLIENT_INDEX].fd, &count, sizeof(count), 0); if (n <= 0) { - //LotusLogger::instance().warn("Keyboard client disconnected or connection error"); kb_client_fd.reset(-1); fds[KB_CLIENT_INDEX].fd = -1; } else if (count > 0) { @@ -350,36 +273,26 @@ int main(int argc, char* argv[]) { } } - // connect to mouse socket if ((fds[2].revents & POLLIN) != 0) { int new_fd = accept4(mouse_server_fd.get(), nullptr, nullptr, SOCK_NONBLOCK); - if (new_fd >= 0) { - //LotusLogger::instance().info("New mouse flag client connected"); - addon_fd.reset(new_fd); - } + if (new_fd >= 0) addon_fd.reset(new_fd); } - // handle mouse (libinput) if ((fds[1].revents & POLLIN) != 0) { struct libinput_event* event = nullptr; - while ((event = libinput_get_event(li_ctx.get_li())) != nullptr) { enum libinput_event_type type = libinput_event_get_type(event); if (type == LIBINPUT_EVENT_POINTER_BUTTON) { struct libinput_event_pointer* p = libinput_event_get_pointer_event(event); - if (libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED) { - if (addon_fd.is_valid()) { - if (send(addon_fd.get(), "C", 1, MSG_NOSIGNAL | MSG_DONTWAIT) <= 0) { - //LotusLogger::instance().warn("Failed to send to mouse flag client, closing connection"); - addon_fd.reset(-1); - } - } + if (libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED + && addon_fd.is_valid()) + { + if (send(addon_fd.get(), "C", 1, MSG_NOSIGNAL | MSG_DONTWAIT) <= 0) + addon_fd.reset(-1); } } else if (type == LIBINPUT_EVENT_DEVICE_ADDED) { - struct libinput_device* dev = libinput_event_get_device(event); - const char* name = libinput_device_get_name(dev); - //LotusLogger::instance().info("Device added: " + std::string(name)); + struct libinput_device* dev = libinput_event_get_device(event); if (libinput_device_config_tap_get_finger_count(dev) > 0) { libinput_device_config_tap_set_enabled(dev, LIBINPUT_CONFIG_TAP_ENABLED); libinput_device_config_tap_set_button_map(dev, LIBINPUT_CONFIG_TAP_MAP_LRM); @@ -389,6 +302,5 @@ int main(int argc, char* argv[]) { } } } - //LotusLogger::instance().info("Terminating server..."); return 0; } diff --git a/server/lotus-server.h b/server/lotus-server.h index 80075cf8..de8e5e03 100644 --- a/server/lotus-server.h +++ b/server/lotus-server.h @@ -3,17 +3,6 @@ * SPDX-FileCopyrightText: 2026 Nguyễn Hoàng Kỳ * * SPDX-License-Identifier: GPL-3.0-or-later - * - */ - -/** - * @file lotus-server.h - * @brief Uinput server for handling backspace and mouse events. - * - * This server runs as a separate process with elevated privileges to: - * - Send backspace key events via uinput device - * - Monitor mouse button presses via libinput - * - Communicate with fcitx5 addon via Unix sockets */ #ifndef _LOTUS_SERVER_H_ @@ -22,176 +11,94 @@ #include #include #include +#include #include #include #include -#include #include #include #include #include -/** - * @brief RAII Wrapper for Unix File Descriptors. - * Ensures that close() is called when the object goes out of scope. - * Complies with Rule of Five by disabling copy and implementing move. - */ class FdGuard { public: explicit FdGuard(int fd = -1) : fd_(fd) {} ~FdGuard(); - // Rule of Five: Disable copying, allow moving FdGuard(const FdGuard&) = delete; FdGuard& operator=(const FdGuard&) = delete; FdGuard(FdGuard&& other) noexcept; FdGuard& operator=(FdGuard&& other) noexcept; - int get() const { - return fd_; - } - bool is_valid() const { - return fd_ >= 0; - } + int get() const { return fd_; } + bool is_valid() const { return fd_ >= 0; } void reset(int new_fd = -1); private: int fd_; }; -/** - * @brief RAII Wrapper for uinput device. - * Handles UI_DEV_CREATE and UI_DEV_DESTROY automatically. - */ class UinputDevice { public: UinputDevice() = default; ~UinputDevice(); - // Disable copy, allow move UinputDevice(const UinputDevice&) = delete; UinputDevice& operator=(const UinputDevice&) = delete; UinputDevice(UinputDevice&&) = default; UinputDevice& operator=(UinputDevice&&) = default; - bool initialize(); - void send_backspace(); - int get_fd() const { - return guard_.get(); - } + bool initialize(); + void send_backspace(); + int get_fd() const { return guard_.get(); } private: FdGuard guard_; }; -/** - * @brief RAII Wrapper for Libinput and Udev contexts. - */ class LibinputContext { public: - LibinputContext(const struct libinput_interface* interface); + explicit LibinputContext(const struct libinput_interface* interface); ~LibinputContext(); - // Disable copy, allow move LibinputContext(const LibinputContext&) = delete; LibinputContext& operator=(const LibinputContext&) = delete; LibinputContext(LibinputContext&& other) noexcept : udev_(other.udev_), li_(other.li_) { other.udev_ = nullptr; other.li_ = nullptr; } - LibinputContext& operator=(LibinputContext&& other) noexcept { if (this != &other) { this->~LibinputContext(); - udev_ = other.udev_; - li_ = other.li_; - other.udev_ = nullptr; - other.li_ = nullptr; + udev_ = other.udev_; li_ = other.li_; + other.udev_ = nullptr; other.li_ = nullptr; } return *this; } - bool is_valid() const { - return li_ != nullptr; - } - struct libinput* get_li() const { - return li_; - } - int get_fd() const { - return libinput_get_fd(li_); - } + bool is_valid() const { return li_ != nullptr; } + struct libinput* get_li() const { return li_; } + int get_fd() const { return libinput_get_fd(li_); } private: struct udev* udev_ = nullptr; struct libinput* li_ = nullptr; }; -/** - * @brief Maximum length of Unix socket paths. -*/ #define UNIX_PATH_MAX sizeof(((struct sockaddr_un*)0)->sun_path) -/** - * @brief Global flag to control server running state. - */ extern std::atomic g_running; //NOLINT -/** - * @brief Signal handler for graceful shutdown. - * @param sig Signal number (SIGTERM or SIGINT). - */ -void signal_handler(int sig); - -/** - * @brief Gets the current username. - * @return Username string or "unknown" if cannot determine. - */ -std::string get_current_username(); - -/** - * @brief Gets the UID for a given username. - * @param username Username to resolve. - * @return UID of the user, or (uid_t)-1 if not found. - */ -uid_t get_uid_for_user(const std::string& username); - -/** - * @brief Boosts process priority for real-time responsiveness. - */ -void boost_process_priority(); +void signal_handler(int sig); +bool get_current_username(char* out, size_t len); +uid_t get_uid_for_user(const char* username); +void boost_process_priority(); +void pin_to_pcore(); +int open_restricted(const char* path, int flags, void* user_data); +void close_restricted(int fd, void* user_data); -/** - * @brief Pins process to performance cores (cores 0-3). - */ -void pin_to_pcore(); - -/** - * @brief Opens input device with restricted permissions. - * @param path Device path. - * @param flags Open flags. - * @param user_data Unused user data. - * @return File descriptor or negative errno on error. - */ -int open_restricted(const char* path, int flags, void* user_data); - -/** - * @brief Closes input device. - * @param fd File descriptor to close. - * @param user_data Unused user data. - */ -void close_restricted(int fd, void* user_data); - -/** - * @brief libinput interface callbacks. - */ extern const struct libinput_interface interface; -/** - * @brief Main entry point for the uinput server. - * @param argc Argument count. - * @param argv Argument vector. - * @return Exit code (0 on success). - */ int main(int argc, char* argv[]); #endif // _LOTUS_SERVER_H_ diff --git a/src/app_quirks.h b/src/app_quirks.h index 51485c30..5b2c7dfe 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -23,11 +23,11 @@ * * Chromium-based browsers that need special handling for text replacement. */ -inline constexpr std::array ack_apps = { +inline constexpr std::array ack_apps = { "chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex", "vesktop", "obsidian", "mullvad", - "firefox", "zen" + "firefox", "zen", "waterfox" }; /** diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 4578976e..8dd75254 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -56,17 +56,6 @@ namespace fcitx { } } static KeySym typeKeyForModeMenuHotkey(KeySym hotkeySym) { return isAppModeMenuReservedKey(hotkeySym) ? FcitxKey_f : hotkeySym;} - static inline std::vector convertToStringList(char** list) { - std::vector result; - if (list != nullptr) { - for (size_t i = 0; list[i] != nullptr; ++i) { //NOLINT - result.emplace_back(list[i]); //NOLINT - free(list[i]); //NOLINT - } - free(list); //NOLINT - } - return result; - } LotusEngine::LotusEngine(Instance* instance) : instance_(instance), factory_([this](InputContext& ic) { return new LotusState(this, &ic); }) { //NOLINT const char* desktop = std::getenv("XDG_CURRENT_DESKTOP"); isGnome_ = (desktop != nullptr) && std::string(desktop).find("GNOME") != std::string::npos; @@ -190,8 +179,6 @@ namespace fcitx { void LotusEngine::activate(const InputMethodEntry& /*entry*/, InputContextEvent& event) { auto* ic = event.inputContext(); - const bool surrvalid = ic->surroundingText().isValid(); - const bool is_dbus = getFrontendName(ic) == "dbus"; static std::atomic mouseThreadStarted{false}; if (!mouseThreadStarted.exchange(true)) startMouseReset(); auto& statusArea = event.inputContext()->statusArea(); @@ -203,6 +190,8 @@ namespace fcitx { auto* state = ic->propertyFor(&factory_); const bool alreadySetup = !s_activationCache.needsSetup(ic, targetMode); if (!alreadySetup) { + const bool surrvalid = ic->surroundingText().isValid(); + const bool is_dbus = getFrontendName(ic) == "dbus"; updateCharsetAction(event.inputContext()); setMode(targetMode, event.inputContext()); // Workaround for chromium wayland issue where suggestions cause a doubled diff --git a/src/lotus-utils.h b/src/lotus-utils.h index 5cb20659..76b8de4b 100644 --- a/src/lotus-utils.h +++ b/src/lotus-utils.h @@ -18,7 +18,9 @@ #include #include #include +#if defined(LOTUS_ENABLE_LOG) #include +#endif #include #include "lotus-config.h" @@ -45,11 +47,17 @@ extern "C" size_t strfind_avx512(const char* hay, size_t hlen, const char* needl FCITX_DECLARE_LOG_CATEGORY(lotus); +#if defined(LOTUS_ENABLE_LOG) #define LOTUS_DEBUG(msg) FCITX_LOGC(lotus, Debug) << "[DEBUG] " << msg #define LOTUS_INFO(msg) FCITX_LOGC(lotus, Info) << "[INFO] " << msg #define LOTUS_WARN(msg) FCITX_LOGC(lotus, Warn) << "[WARN] " << msg #define LOTUS_ERROR(msg) FCITX_LOGC(lotus, Error) << "[ERROR] " << msg - +#else +#define LOTUS_DEBUG(msg) ((void)0) +#define LOTUS_INFO(msg) ((void)0) +#define LOTUS_WARN(msg) ((void)0) +#define LOTUS_ERROR(msg) ((void)0) +#endif // Forward declaration for fcitx types using KeySym = uint32_t; diff --git a/unikey/core/inputproc.h b/unikey/core/inputproc.h index dabfbcb8..b123c2ea 100644 --- a/unikey/core/inputproc.h +++ b/unikey/core/inputproc.h @@ -24,7 +24,7 @@ #define DllImport #endif -enum UkKeyEvName { +enum UkKeyEvName : int { vneRoofAll, vneRoof_a, vneRoof_e, From 816492ea61cfbe4559015af54595040e34720f02 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Fri, 15 May 2026 16:22:11 +0700 Subject: [PATCH 35/42] icon upstream --- .gitignore | 4 +- .../status/22/fcitx-lotus-default-black.svg | 68 +---------- .../status/22/fcitx-lotus-default.svg | 68 +---------- .../22/fcitx-lotus-emoji-default-black.svg | 64 +--------- .../status/22/fcitx-lotus-emoji-default.svg | 64 +--------- .../status/22/fcitx-lotus-emoji.svg | 111 +++--------------- .../22/fcitx-lotus-off-default-black.svg | 69 +---------- .../status/22/fcitx-lotus-off-default.svg | 69 +---------- .../breeze-dark/status/22/fcitx-lotus-off.svg | 88 ++------------ .../breeze-dark/status/22/fcitx-lotus.svg | 89 ++------------ .../status/24/fcitx-lotus-default-black.svg | 68 +---------- .../status/24/fcitx-lotus-default.svg | 68 +---------- .../24/fcitx-lotus-emoji-default-black.svg | 76 ++---------- .../status/24/fcitx-lotus-emoji-default.svg | 76 ++---------- .../status/24/fcitx-lotus-emoji.svg | 111 +++--------------- .../24/fcitx-lotus-off-default-black.svg | 69 +---------- .../status/24/fcitx-lotus-off-default.svg | 69 +---------- .../breeze-dark/status/24/fcitx-lotus-off.svg | 88 ++------------ .../breeze-dark/status/24/fcitx-lotus.svg | 89 ++------------ .../status/22/fcitx-lotus-default-black.svg | 68 +---------- .../breeze/status/22/fcitx-lotus-default.svg | 68 +---------- .../22/fcitx-lotus-emoji-default-black.svg | 65 +--------- .../status/22/fcitx-lotus-emoji-default.svg | 65 +--------- .../breeze/status/22/fcitx-lotus-emoji.svg | 111 +++--------------- .../22/fcitx-lotus-off-default-black.svg | 69 +---------- .../status/22/fcitx-lotus-off-default.svg | 69 +---------- .../breeze/status/22/fcitx-lotus-off.svg | 88 ++------------ data/icons/breeze/status/22/fcitx-lotus.svg | 89 ++------------ .../status/24/fcitx-lotus-default-black.svg | 68 +---------- .../breeze/status/24/fcitx-lotus-default.svg | 68 +---------- .../24/fcitx-lotus-emoji-default-black.svg | 76 ++---------- .../status/24/fcitx-lotus-emoji-default.svg | 76 ++---------- .../breeze/status/24/fcitx-lotus-emoji.svg | 111 +++--------------- .../24/fcitx-lotus-off-default-black.svg | 69 +---------- .../status/24/fcitx-lotus-off-default.svg | 69 +---------- .../breeze/status/24/fcitx-lotus-off.svg | 88 ++------------ data/icons/breeze/status/24/fcitx-lotus.svg | 89 ++------------ .../apps/fcitx-lotus-default-black.svg | 47 +------- .../scalable/apps/fcitx-lotus-default.svg | 47 +------- .../apps/fcitx-lotus-emoji-default-black.svg | 76 ++---------- .../apps/fcitx-lotus-emoji-default.svg | 76 ++---------- .../scalable/apps/fcitx-lotus-emoji.svg | 111 +++--------------- .../apps/fcitx-lotus-off-default-black.svg | 45 +------ .../scalable/apps/fcitx-lotus-off-default.svg | 45 +------ .../hicolor/scalable/apps/fcitx-lotus-off.svg | 88 ++------------ .../hicolor/scalable/apps/fcitx-lotus.svg | 89 ++------------ .../org.fcitx.Fcitx5.fcitx-lotus-emoji.svg | 18 ++- .../apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg | 11 +- .../apps/org.fcitx.Fcitx5.fcitx-lotus.svg | 11 +- 49 files changed, 379 insertions(+), 3099 deletions(-) mode change 120000 => 100644 data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-emoji.svg mode change 120000 => 100644 data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg mode change 120000 => 100644 data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus.svg diff --git a/.gitignore b/.gitignore index a0d42df3..6c68c0c0 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,8 @@ compile_commands.json !.all-contributorsrc refresh-contributor +unikey-im/ + # Qt AUTOMOC generated files *_autogen/ @@ -38,4 +40,4 @@ refresh-contributor lotus-version.h # Logs *.log -__pycache__/ \ No newline at end of file +__pycache__/ diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-default-black.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-default-black.svg index 909d4f14..54f14857 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-default-black.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-default-black.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-default.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-default.svg index f191e0d0..c976f710 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-default.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-default.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default-black.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default-black.svg index 2086b99b..339050ab 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default-black.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default-black.svg @@ -1,61 +1,9 @@ - - - - - - - - + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default.svg index 287ab7bd..a762ae8f 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-emoji-default.svg @@ -1,61 +1,9 @@ - - - - - - - - + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-emoji.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-emoji.svg index c759318f..df9af9f8 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-emoji.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-emoji.svg @@ -1,94 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-off-default-black.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-off-default-black.svg index 5608b669..5efcd9fd 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-off-default-black.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-off-default-black.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-off-default.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-off-default.svg index d66ee19c..0e38e6da 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-off-default.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-off-default.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus-off.svg b/data/icons/breeze-dark/status/22/fcitx-lotus-off.svg index 638c314a..e20fb95d 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus-off.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus-off.svg @@ -1,82 +1,10 @@ - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/breeze-dark/status/22/fcitx-lotus.svg b/data/icons/breeze-dark/status/22/fcitx-lotus.svg index c973dc26..646f4f74 100644 --- a/data/icons/breeze-dark/status/22/fcitx-lotus.svg +++ b/data/icons/breeze-dark/status/22/fcitx-lotus.svg @@ -1,83 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-default-black.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-default-black.svg index 909d4f14..54f14857 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-default-black.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-default-black.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-default.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-default.svg index f191e0d0..c976f710 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-default.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-default.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default-black.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default-black.svg index 831df9ba..2705ce9c 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default-black.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default-black.svg @@ -1,69 +1,7 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default.svg index dfa11e03..1aa3749b 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-emoji-default.svg @@ -1,69 +1,7 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-emoji.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-emoji.svg index c759318f..df9af9f8 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-emoji.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-emoji.svg @@ -1,94 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-off-default-black.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-off-default-black.svg index 5608b669..5efcd9fd 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-off-default-black.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-off-default-black.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-off-default.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-off-default.svg index d66ee19c..0e38e6da 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-off-default.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-off-default.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus-off.svg b/data/icons/breeze-dark/status/24/fcitx-lotus-off.svg index 638c314a..e20fb95d 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus-off.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus-off.svg @@ -1,82 +1,10 @@ - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/breeze-dark/status/24/fcitx-lotus.svg b/data/icons/breeze-dark/status/24/fcitx-lotus.svg index c973dc26..646f4f74 100644 --- a/data/icons/breeze-dark/status/24/fcitx-lotus.svg +++ b/data/icons/breeze-dark/status/24/fcitx-lotus.svg @@ -1,83 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/breeze/status/22/fcitx-lotus-default-black.svg b/data/icons/breeze/status/22/fcitx-lotus-default-black.svg index d3532518..54f14857 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-default-black.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-default-black.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/22/fcitx-lotus-default.svg b/data/icons/breeze/status/22/fcitx-lotus-default.svg index d7cfb70f..c976f710 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-default.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-default.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/22/fcitx-lotus-emoji-default-black.svg b/data/icons/breeze/status/22/fcitx-lotus-emoji-default-black.svg index e0d1ef99..4a78de2e 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-emoji-default-black.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-emoji-default-black.svg @@ -1,62 +1,9 @@ - - - - - - - - + diff --git a/data/icons/breeze/status/22/fcitx-lotus-emoji-default.svg b/data/icons/breeze/status/22/fcitx-lotus-emoji-default.svg index 07bcd92e..22e871c1 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-emoji-default.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-emoji-default.svg @@ -1,62 +1,9 @@ - - - - - - - - + diff --git a/data/icons/breeze/status/22/fcitx-lotus-emoji.svg b/data/icons/breeze/status/22/fcitx-lotus-emoji.svg index c759318f..df9af9f8 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-emoji.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-emoji.svg @@ -1,94 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/data/icons/breeze/status/22/fcitx-lotus-off-default-black.svg b/data/icons/breeze/status/22/fcitx-lotus-off-default-black.svg index 603e7883..5efcd9fd 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-off-default-black.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-off-default-black.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/22/fcitx-lotus-off-default.svg b/data/icons/breeze/status/22/fcitx-lotus-off-default.svg index 02332508..0e38e6da 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-off-default.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-off-default.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/22/fcitx-lotus-off.svg b/data/icons/breeze/status/22/fcitx-lotus-off.svg index 638c314a..e20fb95d 100644 --- a/data/icons/breeze/status/22/fcitx-lotus-off.svg +++ b/data/icons/breeze/status/22/fcitx-lotus-off.svg @@ -1,82 +1,10 @@ - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/breeze/status/22/fcitx-lotus.svg b/data/icons/breeze/status/22/fcitx-lotus.svg index c973dc26..646f4f74 100644 --- a/data/icons/breeze/status/22/fcitx-lotus.svg +++ b/data/icons/breeze/status/22/fcitx-lotus.svg @@ -1,83 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/breeze/status/24/fcitx-lotus-default-black.svg b/data/icons/breeze/status/24/fcitx-lotus-default-black.svg index 909d4f14..54f14857 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-default-black.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-default-black.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/24/fcitx-lotus-default.svg b/data/icons/breeze/status/24/fcitx-lotus-default.svg index f191e0d0..c976f710 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-default.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-default.svg @@ -1,65 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/24/fcitx-lotus-emoji-default-black.svg b/data/icons/breeze/status/24/fcitx-lotus-emoji-default-black.svg index 831df9ba..2705ce9c 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-emoji-default-black.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-emoji-default-black.svg @@ -1,69 +1,7 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/data/icons/breeze/status/24/fcitx-lotus-emoji-default.svg b/data/icons/breeze/status/24/fcitx-lotus-emoji-default.svg index dfa11e03..1aa3749b 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-emoji-default.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-emoji-default.svg @@ -1,69 +1,7 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/data/icons/breeze/status/24/fcitx-lotus-emoji.svg b/data/icons/breeze/status/24/fcitx-lotus-emoji.svg index c759318f..df9af9f8 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-emoji.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-emoji.svg @@ -1,94 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/data/icons/breeze/status/24/fcitx-lotus-off-default-black.svg b/data/icons/breeze/status/24/fcitx-lotus-off-default-black.svg index 5608b669..5efcd9fd 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-off-default-black.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-off-default-black.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/24/fcitx-lotus-off-default.svg b/data/icons/breeze/status/24/fcitx-lotus-off-default.svg index d66ee19c..0e38e6da 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-off-default.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-off-default.svg @@ -1,66 +1,9 @@ - - - - - + + + + + - - - - + diff --git a/data/icons/breeze/status/24/fcitx-lotus-off.svg b/data/icons/breeze/status/24/fcitx-lotus-off.svg index 638c314a..e20fb95d 100644 --- a/data/icons/breeze/status/24/fcitx-lotus-off.svg +++ b/data/icons/breeze/status/24/fcitx-lotus-off.svg @@ -1,82 +1,10 @@ - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/breeze/status/24/fcitx-lotus.svg b/data/icons/breeze/status/24/fcitx-lotus.svg index c973dc26..646f4f74 100644 --- a/data/icons/breeze/status/24/fcitx-lotus.svg +++ b/data/icons/breeze/status/24/fcitx-lotus.svg @@ -1,83 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-default-black.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-default-black.svg index 4ea3f991..fc236049 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-default-black.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-default-black.svg @@ -1,45 +1,4 @@ - - - - - + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-default.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-default.svg index 0957bd6e..24a59f35 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-default.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-default.svg @@ -1,45 +1,4 @@ - - - - - + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default-black.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default-black.svg index 831df9ba..2705ce9c 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default-black.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default-black.svg @@ -1,69 +1,7 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default.svg index dfa11e03..1aa3749b 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji-default.svg @@ -1,69 +1,7 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji.svg index c759318f..df9af9f8 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-emoji.svg @@ -1,94 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default-black.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default-black.svg index 4b9a40c3..a28d9ab9 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default-black.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default-black.svg @@ -1,43 +1,4 @@ - - - - - + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default.svg index 910437c9..97c15052 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-off-default.svg @@ -1,43 +1,4 @@ - - - - - + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus-off.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus-off.svg index 638c314a..e20fb95d 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus-off.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus-off.svg @@ -1,82 +1,10 @@ - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/fcitx-lotus.svg b/data/icons/hicolor/scalable/apps/fcitx-lotus.svg index c973dc26..646f4f74 100644 --- a/data/icons/hicolor/scalable/apps/fcitx-lotus.svg +++ b/data/icons/hicolor/scalable/apps/fcitx-lotus.svg @@ -1,83 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-emoji.svg b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-emoji.svg deleted file mode 120000 index dfe78f08..00000000 --- a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-emoji.svg +++ /dev/null @@ -1 +0,0 @@ -fcitx-lotus-emoji.svg \ No newline at end of file diff --git a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-emoji.svg b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-emoji.svg new file mode 100644 index 00000000..df9af9f8 --- /dev/null +++ b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-emoji.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg deleted file mode 120000 index d685c4e0..00000000 --- a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg +++ /dev/null @@ -1 +0,0 @@ -fcitx-lotus-off.svg \ No newline at end of file diff --git a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg new file mode 100644 index 00000000..e20fb95d --- /dev/null +++ b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus-off.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus.svg b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus.svg deleted file mode 120000 index 76c46cc2..00000000 --- a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus.svg +++ /dev/null @@ -1 +0,0 @@ -fcitx-lotus.svg \ No newline at end of file diff --git a/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus.svg b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus.svg new file mode 100644 index 00000000..646f4f74 --- /dev/null +++ b/data/icons/hicolor/scalable/apps/org.fcitx.Fcitx5.fcitx-lotus.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From eef65a1ac4febd7176bde48cbe8bc8cc4379d0c1 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 16 May 2026 11:04:38 +0700 Subject: [PATCH 36/42] uh --- src/CMakeLists.txt | 4 ++++ src/app_quirks.h | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4f04df5f..cbd754a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ option(LOTUS_ENABLE_AVX512 "Enable AVX-512 acceleration for lotus-utils" ON) +option(LOTUS_ENABLE_LOG "Enable LOG" ON) set(fcitx_lotus_sources lotus.cpp lotus-engine.cpp @@ -27,6 +28,9 @@ target_link_libraries(lotus Pthread::Pthread X11::X11 ) +if (LOTUS_ENABLE_LOG) + target_compile_definitions(lotus PRIVATE LOTUS_ENABLE_LOG=1) +endif() target_compile_definitions(lotus PRIVATE LOTUS_ENGINE_UNIKEY=1) target_include_directories(lotus PRIVATE "${PROJECT_SOURCE_DIR}/unikey/core") diff --git a/src/app_quirks.h b/src/app_quirks.h index 5b2c7dfe..1c5d3afe 100644 --- a/src/app_quirks.h +++ b/src/app_quirks.h @@ -23,11 +23,11 @@ * * Chromium-based browsers that need special handling for text replacement. */ -inline constexpr std::array ack_apps = { +inline constexpr std::array ack_apps = { "chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex", "vesktop", "obsidian", "mullvad", - "firefox", "zen", "waterfox" + "firefox", "zen", "waterfox", "wps" }; /** From 189c8d15e6c1c75c2bd25fa0311a5070aa7e1c39 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 16 May 2026 11:10:46 +0700 Subject: [PATCH 37/42] disable avx512 buid by default --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cbd754a3..48090ca3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -option(LOTUS_ENABLE_AVX512 "Enable AVX-512 acceleration for lotus-utils" ON) +option(LOTUS_ENABLE_AVX512 "Enable AVX-512 acceleration for lotus-utils" OFF) option(LOTUS_ENABLE_LOG "Enable LOG" ON) set(fcitx_lotus_sources lotus.cpp From b2f559a9769d2ff675d4c69fe39df003a86b3111 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 16 May 2026 15:04:14 +0700 Subject: [PATCH 38/42] kjaksj --- src/lotus-engine.cpp | 38 +++++++++++++++++++++++--------------- src/lotus-state.cpp | 43 +++++++++++++++++++++---------------------- src/lotus-state.h | 1 + 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 8dd75254..e025ce5e 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -87,6 +87,7 @@ namespace fcitx { if (!std::filesystem::exists(configDir)) { std::filesystem::create_directories(configDir);} reloadConfig(); instance_->inputContextManager().registerProperty("LotusState", &factory_); + instance_->inputContextManager().setPreeditEnabledByDefault(true); appRulesPath_ = configDir + "/lotus-app-rules.conf"; loadAppRules(); toggleActions_ = { @@ -182,18 +183,19 @@ namespace fcitx { static std::atomic mouseThreadStarted{false}; if (!mouseThreadStarted.exchange(true)) startMouseReset(); auto& statusArea = event.inputContext()->statusArea(); - if (ic->capabilityFlags().test(CapabilityFlag::Preedit)) instance_->inputContextManager().setPreeditEnabledByDefault(true); std::string appName = getProgramName(ic); LOTUS_INFO("App name: " + appName); const LotusMode targetMode = getAppRule(appName); LOTUS_INFO("Target mode: " + modeEnumToString(targetMode)); - auto* state = ic->propertyFor(&factory_); const bool alreadySetup = !s_activationCache.needsSetup(ic, targetMode); -if (!alreadySetup) { const bool surrvalid = ic->surroundingText().isValid(); const bool is_dbus = getFrontendName(ic) == "dbus"; - updateCharsetAction(event.inputContext()); - setMode(targetMode, event.inputContext()); + auto* state = ic->propertyFor(&factory_); +if (!alreadySetup) { + if (event.type() == EventType::InputContextFocusIn) { + updateCharsetAction(ic); + setMode(targetMode, ic); + } // Workaround for chromium wayland issue where suggestions cause a doubled // first character. Forwarding may prevent BS from being sent // to the client. @@ -249,17 +251,20 @@ if (!alreadySetup) { char drain[64]; recv(uinput_client_fd_,drain,sizeof(drain),MSG_DONTWAIT | MSG_NOSIGNAL); } - if (event.type() == EventType::InputContextFocusIn && is_dbus && !surrvalid) { LOTUS_INFO("Skip clearAllBuffers"); - } else if (surrvalid && !state->oldPreBuffer_.empty() && (now_ms() - state->lastDeactivateTime_) < 100) { state->clearAllBuffers(); } + if (event.type() == EventType::InputContextFocusIn && is_dbus && !surrvalid) { + LOTUS_INFO("Skip clearAllBuffers"); + } else if (surrvalid && !state->oldPreBuffer_.empty() && (now_ms() - state->lastDeactivateTime_) < 100) { + state->clearAllBuffers(); + } } if (!state->isReplacing()) is_deleting_.store(false); needEngineReset.store(false); if (targetMode == LotusMode::Emoji) { state->updateEmojiPreedit(); - } else { - LOTUS_INFO("inputPanel reset"); - ic->inputPanel().reset(); + } else if (event.type() == EventType::InputContextFocusIn) { if (realMode == LotusMode::Preedit || realMode == LotusMode::SurroundingText) { + LOTUS_INFO("inputPanel reset"); + ic->inputPanel().reset(); ic->updateUserInterface(UserInterfaceComponent::InputPanel); ic->updatePreedit(); } @@ -402,6 +407,7 @@ if (!alreadySetup) { } void LotusEngine::deactivate(const InputMethodEntry& /*entry*/, InputContextEvent& event) { auto* ic = event.inputContext(); + if (!ic->hasFocus() && event.type() != EventType::InputContextFocusOut) return; auto* state = ic->propertyFor(&factory_); const bool surrvalid = ic->surroundingText().isValid(); const bool is_dbus = getFrontendName(ic) == "dbus"; @@ -416,10 +422,12 @@ if (!alreadySetup) { } if (!state->isReplacing()) is_deleting_.store(false); needEngineReset.store(false); - ic->inputPanel().reset(); - if (realMode == LotusMode::Preedit || realMode == LotusMode::SurroundingText || realMode == LotusMode::Emoji) { - ic->updateUserInterface(UserInterfaceComponent::InputPanel); - ic->updatePreedit(); + if (event.type() != EventType::InputContextFocusOut) { + if (realMode == LotusMode::Preedit || realMode == LotusMode::SurroundingText || realMode == LotusMode::Emoji) { + ic->inputPanel().reset(); + ic->updateUserInterface(UserInterfaceComponent::InputPanel); + ic->updatePreedit(); + } } } } @@ -612,7 +620,7 @@ if (!alreadySetup) { } void LotusEngine::setMode(LotusMode mode, InputContext* ic) { realMode = mode; - if (ic != nullptr) { ic->updateUserInterface(UserInterfaceComponent::StatusArea);} + if (ic != nullptr && ic->hasFocus()) { ic->updateUserInterface(UserInterfaceComponent::StatusArea);} } std::string LotusEngine::subModeIconImpl(const InputMethodEntry& /*entry*/, InputContext& /*inputContext*/) { if (!*config_.useLotusIcons) { diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index d83041ef..afcaa4fa 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -107,13 +107,11 @@ namespace fcitx { recv(uinput_client_fd_, &ack, sizeof(ack), MSG_NOSIGNAL); // keep safe that bs is finish by app std::this_thread::sleep_for(std::chrono::milliseconds(1)); - replacement_start_ms_.store(0, std::memory_order_release); // ez way but cause alot of problem //std::this_thread::sleep_for(std::chrono::milliseconds(count * 5)); } else { LOTUS_INFO("firefox hit me"); std::this_thread::sleep_for(std::chrono::milliseconds(count * 10)); - replacement_start_ms_.store(0, std::memory_order_release); } } void LotusState::send_backspace_forward(int count) const { @@ -123,6 +121,15 @@ namespace fcitx { ic_->forwardKey(Key(FcitxKey_BackSpace, KeyState::NoState), true); } } + void LotusState::finishReplacement() { + is_deleting_.store(false, std::memory_order_release); + replacement_start_ms_.store(0, std::memory_order_release); + replacement_thread_id_.store(0, std::memory_order_release); + expected_backspaces_ = 0; + current_backspace_count_ = 0; + pending_commit_string_.clear(); + buffered_keys_.clear(); + } bool LotusState::isAutofillCertain(const SurroundingText& s) { if (!s.isValid() || oldPreBuffer_.empty()) return false; const unsigned int cursor = s.cursor(); @@ -390,18 +397,13 @@ namespace fcitx { if (isBackspace(currentSym)) { current_backspace_count_ += 1; if (current_backspace_count_ < expected_backspaces_) return false; // Allow intermediate backspaces to reach the app to clear autofill/old text. - is_deleting_.store(false); - replacement_start_ms_.store(0, std::memory_order_release); - replacement_thread_id_.store(0, std::memory_order_release); int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); int64_t wait_ms = static_cast(sleepTime) - elapsed_ms; if (wait_ms > 0) std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); if (waitAck_) std::this_thread::sleep_for(std::chrono::milliseconds(5)); //wait more ic_->commitString(pending_commit_string_); LOTUS_INFO("Commit: " + pending_commit_string_); - expected_backspaces_ = 0; - current_backspace_count_ = 0; - pending_commit_string_ = ""; + finishReplacement(); event.filterAndAccept(); // Filter out the final trigger backspace. return true; } @@ -474,10 +476,7 @@ namespace fcitx { bool LotusState::checkForwardSpecialKey(KeyEvent& keyEvent, KeySym& currentSym) { if (keyEvent.key().isCursorMove() || currentSym == FcitxKey_Tab || currentSym == FcitxKey_KP_Tab || currentSym == FcitxKey_ISO_Left_Tab || currentSym == FcitxKey_Escape || keyEvent.key().hasModifier()) { - is_deleting_.store(false, std::memory_order_release); - expected_backspaces_ = 0; - current_backspace_count_ = 0; - pending_commit_string_.clear(); + finishReplacement(); hasHistory_ = false; inputBackend_->resetEngine(); oldPreBuffer_.clear(); @@ -572,6 +571,7 @@ namespace fcitx { std::string preeditStr = preeditStrBuf; std::string deletedPart; std::string addedPart; + wa_flag = false; if (wa_flag) keyEvent.filterAndAccept(); if (compareAndSplitStrings(oldPreBuffer_, preeditStr, deletedPart, addedPart) != 0) { if (deletedPart.empty()) { @@ -598,7 +598,7 @@ namespace fcitx { if (!rawKey.empty()) ic_->commitString(rawKey); return; } - if (is_deleting_.load()) is_deleting_.store(false, std::memory_order_release); + if (is_deleting_.load()) finishReplacement(); if (!wa_flag) keyEvent.filterAndAccept(); performReplacement(deletedPart, addedPart); oldPreBuffer_ = preeditStr; @@ -724,17 +724,14 @@ namespace fcitx { connect_uinput_server(); } if (current_backspace_count_ >= expected_backspaces_ && is_deleting_.load()) { - is_deleting_.store(false); - current_backspace_count_ = 0; - expected_backspaces_ = 0; + finishReplacement(); } if (needEngineReset.load() && realMode != LotusMode::Off) { LOTUS_INFO("Need engine reset"); oldPreBuffer_.clear(); hasHistory_ = false; inputBackend_->resetEngine(); - is_deleting_.store(false); - current_backspace_count_ = 0; + finishReplacement(); isPrevSpace_ = false; shouldCapitalize_ = false; isPrevPunctuation_ = false; @@ -744,18 +741,20 @@ namespace fcitx { g_mouse_clicked.store(false, std::memory_order_release); clearAllBuffers(); } + KeySym currentSym = keyEvent.rawKey().sym(); if (needFallbackCommit.load(std::memory_order_acquire)) { LOTUS_INFO("Need fallback commit"); needFallbackCommit.store(false, std::memory_order_release); if (current_thread_id_.load(std::memory_order_acquire) == replacement_thread_id_.load(std::memory_order_acquire)) if (!pending_commit_string_.empty()) { ic_->commitString(pending_commit_string_); - pending_commit_string_.clear(); } - replacement_thread_id_.store(0, std::memory_order_release); - replacement_start_ms_.store(0, std::memory_order_release); + finishReplacement(); + if (isBackspace(currentSym)) { + keyEvent.filterAndAccept(); + return; + } } - KeySym currentSym = keyEvent.rawKey().sym(); if (*engine_->config().autoCapitalizeAfterPunctuation && realMode != LotusMode::Off) { // Ignore auto-capitalize side-effects if we're processing automated replacement backspaces bool isAutomatedBackspace = is_deleting_.load(std::memory_order_acquire) && isBackspace(currentSym); diff --git a/src/lotus-state.h b/src/lotus-state.h index 8b048c5b..bd039a62 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -133,6 +133,7 @@ namespace fcitx { */ void send_backspace_uinput(int count) const; void send_backspace_forward(int count) const; + void finishReplacement(); /** * @brief Checks if autofill is certain for surrounding text. * @param s The surrounding text. From 1917542fe31d5662fc07fbfde85b5a9be6cf31b9 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 16 May 2026 15:37:30 +0700 Subject: [PATCH 39/42] rm stuff --- src/CMakeLists.txt | 2 +- src/lotus-engine.cpp | 3 - ...ey-backend.cpp => lotus-input-backend.cpp} | 3 +- src/lotus-monitor.cpp | 66 +------------------ src/lotus-monitor.h | 13 ---- src/lotus-state.cpp | 23 +------ src/lotus-state.h | 1 - src/lotus-utils.cpp | 3 - src/lotus-utils.h | 3 - 9 files changed, 6 insertions(+), 111 deletions(-) rename src/{lotus-unikey-backend.cpp => lotus-input-backend.cpp} (99%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 48090ca3..5c09dc8e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,7 +15,7 @@ if (LOTUS_ENABLE_AVX512) endif() enable_language(ASM) set_source_files_properties(lotus-utils-avx512.S PROPERTIES LANGUAGE ASM) -list(APPEND fcitx_lotus_sources lotus-unikey-backend.cpp) +list(APPEND fcitx_lotus_sources lotus-input-backend.cpp) add_library(lotus MODULE ${fcitx_lotus_sources}) set_target_properties(lotus PROPERTIES OUTPUT_NAME "lotus") diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index e025ce5e..4cee0a21 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -60,7 +60,6 @@ namespace fcitx { const char* desktop = std::getenv("XDG_CURRENT_DESKTOP"); isGnome_ = (desktop != nullptr) && std::string(desktop).find("GNOME") != std::string::npos; // emptyCustomKeymap_.customKeymap is implicitly initialized to empty by fcitx::Option default value macro. - startMonitoring(); imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "VIQR", "Microsoft"}; config_.inputMethod.annotation().setList(imNames_); auto& uiManager = instance_->userInterfaceManager(); @@ -114,11 +113,9 @@ namespace fcitx { } LotusEngine::~LotusEngine() { stop_flag_monitor.store(true, std::memory_order_release); - monitor_cv.notify_all(); int fd = mouse_socket_fd.load(std::memory_order_acquire); if (fd >= 0) shutdown(fd, SHUT_RDWR); if (mouse_thread.joinable()) mouse_thread.join(); - if (monitor_thread.joinable()) monitor_thread.join(); int old_fd = uinput_client_fd_.exchange(-1); if (old_fd != -1) close(old_fd); LOTUS_INFO("Engine destroyed."); diff --git a/src/lotus-unikey-backend.cpp b/src/lotus-input-backend.cpp similarity index 99% rename from src/lotus-unikey-backend.cpp rename to src/lotus-input-backend.cpp index 9182f0a0..55380666 100644 --- a/src/lotus-unikey-backend.cpp +++ b/src/lotus-input-backend.cpp @@ -3,7 +3,8 @@ * * SPDX-License-Identifier: GPL-3.0-or-later * - * Native Unikey engine (fcitx5-unikey patterns; no Go/CGO). + * Lotus input backend using the native Unikey engine + * (fcitx5-unikey patterns; no Go/CGO). */ #ifdef LOTUS_ENGINE_UNIKEY #include "lotus-input-backend.hpp" diff --git a/src/lotus-monitor.cpp b/src/lotus-monitor.cpp index cff00c80..e51d5afb 100644 --- a/src/lotus-monitor.cpp +++ b/src/lotus-monitor.cpp @@ -19,70 +19,8 @@ #include #include -std::thread monitor_thread = std::thread(); -std::thread mouse_thread = std::thread(); - -void deletingTimeMonitor() { - LOTUS_INFO("Deleting monitor thread started."); - while (!stop_flag_monitor.load()) { - int64_t deleting_since = 0; - - { - std::unique_lock lock(monitor_mutex); - monitor_cv.wait(lock, [] { return is_deleting_.load(std::memory_order_acquire) || stop_flag_monitor.load(std::memory_order_acquire); }); - } - - if (stop_flag_monitor.load()) - break; - - deleting_since = now_ms(); - - while (is_deleting_.load(std::memory_order_acquire) && !stop_flag_monitor.load()) { - int64_t current_time = now_ms(); - - int64_t rep_start = replacement_start_ms_.load(std::memory_order_acquire); - if (rep_start > 0 && (current_time - rep_start) > 200) { - LOTUS_WARN("Replacement timeout (200ms). Falling back to commit."); - int expected_id = replacement_thread_id_.load(std::memory_order_acquire); - if (expected_id > 0) { - is_deleting_.store(false, std::memory_order_release); - needFallbackCommit.store(true, std::memory_order_release); - replacement_start_ms_.store(0, std::memory_order_release); - break; - } - } - - if ((current_time - deleting_since) >= 1500) { - LOTUS_WARN("Critical delete timeout (1500ms). Forcing engine reset."); - is_deleting_.store(false); - needEngineReset.store(true); - replacement_start_ms_.store(0, std::memory_order_release); - break; - } - - { - std::unique_lock lock(monitor_mutex); - monitor_cv.wait_for(lock, std::chrono::milliseconds(2)); - } - } - } - monitor_running.store(false, std::memory_order_release); - LOTUS_INFO("Deleting monitor thread stopped."); -} - -void startMonitoring() { - if (monitor_running.load()) - return; - if (!monitor_running.exchange(true, std::memory_order_acq_rel)) { - LOTUS_INFO("Initializing monitor threads..."); - if (monitor_thread.joinable()) { - monitor_thread.join(); - } - stop_flag_monitor.store(false, std::memory_order_release); - monitor_thread = std::thread(deletingTimeMonitor); - } -} - +std::thread mouse_thread = std::thread(); +// void mousePressResetThread() { const std::string mouse_socket_path = buildSocketPath("mouse_socket"); LOTUS_INFO("Mouse press reset thread started."); diff --git a/src/lotus-monitor.h b/src/lotus-monitor.h index ea6a00bd..5f62da27 100644 --- a/src/lotus-monitor.h +++ b/src/lotus-monitor.h @@ -16,21 +16,8 @@ #include -extern std::thread monitor_thread; extern std::thread mouse_thread; -/** - * @brief Monitors deletion timing to handle race conditions. - * - * Runs in background thread to track deletion operations. - */ -void deletingTimeMonitor(); - -/** - * @brief Starts the monitoring thread. - */ -void startMonitoring(); - /** * @brief Thread function for mouse press detection and reset. * diff --git a/src/lotus-state.cpp b/src/lotus-state.cpp index afcaa4fa..e220f21a 100644 --- a/src/lotus-state.cpp +++ b/src/lotus-state.cpp @@ -123,8 +123,6 @@ namespace fcitx { } void LotusState::finishReplacement() { is_deleting_.store(false, std::memory_order_release); - replacement_start_ms_.store(0, std::memory_order_release); - replacement_thread_id_.store(0, std::memory_order_release); expected_backspaces_ = 0; current_backspace_count_ = 0; pending_commit_string_.clear(); @@ -397,9 +395,7 @@ namespace fcitx { if (isBackspace(currentSym)) { current_backspace_count_ += 1; if (current_backspace_count_ < expected_backspaces_) return false; // Allow intermediate backspaces to reach the app to clear autofill/old text. - int64_t elapsed_ms = now_ms() - replacement_start_ms_.load(std::memory_order_acquire); - int64_t wait_ms = static_cast(sleepTime) - elapsed_ms; - if (wait_ms > 0) std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms)); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); if (waitAck_) std::this_thread::sleep_for(std::chrono::milliseconds(5)); //wait more ic_->commitString(pending_commit_string_); LOTUS_INFO("Commit: " + pending_commit_string_); @@ -411,7 +407,6 @@ namespace fcitx { } bool LotusState::performReplacement(const std::string& deletedPart, const std::string& addedPart) { LOTUS_INFO("Perform replacement: " + deletedPart + " -> " + addedPart); //NOLINT - int my_id = ++current_thread_id_; current_backspace_count_ = 0; pending_commit_string_ = addedPart; const auto& surrounding = ic_->surroundingText(); @@ -460,10 +455,7 @@ namespace fcitx { ic_->commitString(addedPart); return true; } else { - replacement_thread_id_.store(my_id, std::memory_order_release); - replacement_start_ms_.store(now_ms(), std::memory_order_release); is_deleting_.store(true, std::memory_order_release); - monitor_cv.notify_one(); if (0 && isTerm) { send_backspace_forward(expected_backspaces_ - 1); return true; @@ -742,19 +734,6 @@ namespace fcitx { clearAllBuffers(); } KeySym currentSym = keyEvent.rawKey().sym(); - if (needFallbackCommit.load(std::memory_order_acquire)) { - LOTUS_INFO("Need fallback commit"); - needFallbackCommit.store(false, std::memory_order_release); - if (current_thread_id_.load(std::memory_order_acquire) == replacement_thread_id_.load(std::memory_order_acquire)) - if (!pending_commit_string_.empty()) { - ic_->commitString(pending_commit_string_); - } - finishReplacement(); - if (isBackspace(currentSym)) { - keyEvent.filterAndAccept(); - return; - } - } if (*engine_->config().autoCapitalizeAfterPunctuation && realMode != LotusMode::Off) { // Ignore auto-capitalize side-effects if we're processing automated replacement backspaces bool isAutomatedBackspace = is_deleting_.load(std::memory_order_acquire) && isBackspace(currentSym); diff --git a/src/lotus-state.h b/src/lotus-state.h index bd039a62..a3ce7230 100644 --- a/src/lotus-state.h +++ b/src/lotus-state.h @@ -101,7 +101,6 @@ namespace fcitx { int expected_backspaces_ = 0; int current_backspace_count_ = 0; std::string pending_commit_string_; - std::atomic current_thread_id_{0}; std::string emojiBuffer_; std::vector emojiCandidates_; bool waitAck_ = false; diff --git a/src/lotus-utils.cpp b/src/lotus-utils.cpp index 85df24aa..7bbcb7fd 100644 --- a/src/lotus-utils.cpp +++ b/src/lotus-utils.cpp @@ -24,9 +24,6 @@ std::atomic uinput_client_fd_{-1}; std::atomic realtextLen{0}; std::atomic mouse_socket_fd{-1}; -std::atomic replacement_start_ms_{0}; -std::atomic replacement_thread_id_{0}; -std::atomic needFallbackCommit{false}; std::mutex monitor_mutex; std::condition_variable monitor_cv; diff --git a/src/lotus-utils.h b/src/lotus-utils.h index 76b8de4b..94be4394 100644 --- a/src/lotus-utils.h +++ b/src/lotus-utils.h @@ -71,9 +71,6 @@ extern std::atomic monitor_running; ///< Monitor thread extern std::atomic uinput_client_fd_; ///< Uinput client file descriptor extern std::atomic realtextLen; ///< Current text length extern std::atomic mouse_socket_fd; ///< Mouse socket file descriptor -extern std::atomic replacement_start_ms_; ///< Timestamp for replacement -extern std::atomic replacement_thread_id_; ///< Thread ID for replacement -extern std::atomic needFallbackCommit; ///< Fallback commit flag extern std::mutex monitor_mutex; ///< Mutex for monitor synchronization extern std::condition_variable monitor_cv; ///< Condition variable for monitor From 4562253514bd2131bba8f3cfc641229f8b2703bf Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 16 May 2026 15:59:37 +0700 Subject: [PATCH 40/42] rf --- .gitignore | 1 - CMakeLists.txt | 14 ++-- Messages.sh | 4 +- ...g.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in | 4 +- po/fcitx5-lotus.pot | 6 +- po/vi.po | 6 +- src/CMakeLists.txt | 2 +- src/lotus-input-backend.cpp | 73 +++++++++---------- src/lotus-utils.cpp | 1 - src/lotus-utils.h | 1 - unikey/CMakeLists.txt | 6 ++ 11 files changed, 59 insertions(+), 59 deletions(-) rename org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in => org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in (89%) diff --git a/.gitignore b/.gitignore index 6c68c0c0..8b9e2d44 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,6 @@ cmake_install.cmake ecm_uninstall.cmake install_manifest.txt org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml -org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in Makefile .vscode/ compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index b510fbd6..52b7e0fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,19 +45,17 @@ add_subdirectory(data) add_subdirectory(server) add_subdirectory(misc) -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in" - "${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in" - @ONLY -) +set(LOTUS_METAINFO_ID org.fcitx.Fcitx5.Addon.Lotus.metainfo) +set(LOTUS_METAINFO_TRANSLATION_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/${LOTUS_METAINFO_ID}.xml.in") +set(LOTUS_METAINFO_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${LOTUS_METAINFO_ID}.xml") fcitx5_translate_desktop_file( - "${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in" - org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml + "${LOTUS_METAINFO_TRANSLATION_INPUT}" + "${LOTUS_METAINFO_ID}.xml" XML ) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml" DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) +install(FILES "${LOTUS_METAINFO_OUTPUT}" DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) install(FILES LICENSES/GPL-3.0-or-later.txt diff --git a/Messages.sh b/Messages.sh index da93b6b0..707721d9 100755 --- a/Messages.sh +++ b/Messages.sh @@ -12,7 +12,7 @@ xgettext \ --language=appdata \ --from-code=UTF-8 \ -o /tmp/lotus-xml.pot \ -org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in +org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in xgettext \ --language=Python \ @@ -47,4 +47,4 @@ msgcat \ /tmp/lotus-conf.pot \ /tmp/lotus-python.pot \ /tmp/lotus-desktop.pot \ --o po/fcitx5-lotus.pot \ No newline at end of file +-o po/fcitx5-lotus.pot diff --git a/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in b/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in similarity index 89% rename from org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in rename to org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in index e7496e97..e105defc 100644 --- a/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in +++ b/org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in @@ -14,6 +14,6 @@ https://github.com/LotusInputMethod/fcitx5-lotus/issues Fcitx - + - \ No newline at end of file + diff --git a/po/fcitx5-lotus.pot b/po/fcitx5-lotus.pot index f4b14214..1b093974 100644 --- a/po/fcitx5-lotus.pot +++ b/po/fcitx5-lotus.pot @@ -196,15 +196,15 @@ msgstr "" msgid "Page " msgstr "" -#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in:7 +#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in:7 msgid "Lotus" msgstr "" -#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in:8 +#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in:8 msgid "Vietnamese input method (Lotus)" msgstr "" -#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in:10 +#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in:10 msgid "" "Lotus is a Vietnamese input method for Fcitx5, supporting multiple typing " "modes and input methods." diff --git a/po/vi.po b/po/vi.po index 296620d4..9dc67f63 100644 --- a/po/vi.po +++ b/po/vi.po @@ -197,15 +197,15 @@ msgstr "Lotus - Tắt" msgid "Page " msgstr "Trang " -#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in:7 +#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in:7 msgid "Lotus" msgstr "Lotus" -#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in:8 +#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in:8 msgid "Vietnamese input method (Lotus)" msgstr "Bộ gõ tiếng Việt (Lotus)" -#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in.in:10 +#: org.fcitx.Fcitx5.Addon.Lotus.metainfo.xml.in:10 msgid "" "Lotus is a Vietnamese input method for Fcitx5, supporting multiple typing " "modes and input methods." diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5c09dc8e..1a8f520d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ target_link_libraries(lotus Fcitx5::Core Fcitx5::Config Fcitx5::Module::Emoji - lotus-unikey-bridge + Unikey::Core Pthread::Pthread X11::X11 ) diff --git a/src/lotus-input-backend.cpp b/src/lotus-input-backend.cpp index 55380666..ed95cb23 100644 --- a/src/lotus-input-backend.cpp +++ b/src/lotus-input-backend.cpp @@ -10,7 +10,6 @@ #include "lotus-input-backend.hpp" #include "lotus-config.h" #include "lotus-engine.h" -#include "../unikey/LotusUnikeyEngine.hpp" #include "unikeyinputcontext.h" #include #include @@ -44,8 +43,8 @@ namespace fcitx { class LotusUnikeyInputBackend final : public LotusInputBackend { public: void recreateEngine(LotusEngine* engine) override { - engineRef_ = engine; - uk_ = std::make_unique<::fcitx::lotus::LotusUnikeyEngine>(); + im_ = std::make_unique(); + uic_ = std::make_unique(im_.get()); applyFromConfig(engine); resetEngine(); } @@ -58,15 +57,15 @@ namespace fcitx { lastShiftPressed_ = FcitxKey_None; lastKeyWithShift_ = false; autoCommit_ = false; - if (uk_) uk_->resetBuf(); + if (uic_) uic_->resetBuf(); } void rebuildFromText(const char* utf8) override { resetEngine(); - if (!uk_ || utf8 == nullptr) return; + if (!uic_ || utf8 == nullptr) return; for (auto ucs : utf8::MakeUTF8CharRange(std::string_view(utf8))) { if (ucs < 128U) - uk_->putChar(static_cast(ucs)); - else uk_->putChar(ucs); + uic_->putChar(static_cast(ucs)); + else uic_->putChar(ucs); } syncState(FcitxKey_None); } @@ -97,14 +96,14 @@ namespace fcitx { void commitPreedit() override { if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - if (uk_) uk_->resetBuf(); + if (uic_) uic_->resetBuf(); } private: void applyFromConfig(LotusEngine* engine) { - if (!uk_) return; + if (!im_) return; UkInputMethod currentIM_ = mapLotusIm(engine->config().inputMethod.value()); - uk_->setInputMethod(currentIM_); - uk_->setOutputCharset(CONV_CHARSET_XUTF8); + im_->setInputMethod(currentIM_); + im_->setOutputCharset(CONV_CHARSET_XUTF8); UnikeyOptions opt{}; opt.freeMarking = *engine->config().freeMarking ? 1 : 0; opt.modernStyle = *engine->config().modernStyle ? 1 : 0; @@ -115,7 +114,7 @@ namespace fcitx { opt.useIME = 0; opt.spellCheckEnabled = *engine->config().spellCheck ? 1 : 0; opt.autoNonVnRestore = *engine->config().autoNonVnRestore ? 1 : 0; - uk_->setOptions(&opt); + im_->setOptions(&opt); } void eraseChars(int num_chars) { int i; @@ -129,79 +128,79 @@ namespace fcitx { preeditStr_.erase(static_cast(i + 1)); } void syncState(KeySym sym) { - auto* uic = uk_->context(); - if (uic->backspaces() > 0) { - if (static_cast(preeditStr_.length()) <= uic->backspaces()) + if (!uic_) return; + if (uic_->backspaces() > 0) { + if (static_cast(preeditStr_.length()) <= uic_->backspaces()) preeditStr_.clear(); else - eraseChars(uic->backspaces()); + eraseChars(uic_->backspaces()); } - if (uic->bufChars() > 0) { - preeditStr_.append(reinterpret_cast(uic->buf()), static_cast(uic->bufChars())); + if (uic_->bufChars() > 0) { + preeditStr_.append(reinterpret_cast(uic_->buf()), static_cast(uic_->bufChars())); } else if (sym != FcitxKey_Shift_L && sym != FcitxKey_Shift_R && sym != FcitxKey_None) { preeditStr_.append(utf8::UCS4ToUTF8(sym)); } } bool dispatch(uint32_t sym, uint32_t state) { - if (!uk_) return false; + if (!uic_) return false; KeyStates st(static_cast(state)); const auto rawSym = static_cast(sym); if (st.testAny(KeyState::Ctrl_Alt) || rawSym == FcitxKey_Control_L || rawSym == FcitxKey_Control_R || rawSym == FcitxKey_Tab || rawSym == FcitxKey_Return || rawSym == FcitxKey_Delete || rawSym == FcitxKey_KP_Enter || (rawSym >= FcitxKey_Home && rawSym <= FcitxKey_Insert) || (rawSym >= FcitxKey_KP_Home && rawSym <= FcitxKey_KP_Delete)) { - uk_->context()->filter(0); + uic_->filter(0); if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - uk_->resetBuf(); + uic_->resetBuf(); return false; } if (st.test(KeyState::Super)) return false; if ((rawSym >= FcitxKey_Caps_Lock && rawSym <= FcitxKey_Hyper_R) || rawSym == FcitxKey_Shift_L || rawSym == FcitxKey_Shift_R) return false; if (rawSym == FcitxKey_BackSpace) { - uk_->backspacePress(); - if (uk_->context()->backspaces() == 0 || preeditStr_.empty()) { + uic_->backspacePress(); + if (uic_->backspaces() == 0 || preeditStr_.empty()) { if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - uk_->resetBuf(); + uic_->resetBuf(); return !pendingPullCommit_.empty(); } - if (static_cast(preeditStr_.length()) <= uk_->context()->backspaces()) + if (static_cast(preeditStr_.length()) <= uic_->backspaces()) preeditStr_.clear(); else - eraseChars(uk_->context()->backspaces()); - if (uk_->context()->bufChars() > 0) - preeditStr_.append(reinterpret_cast(uk_->context()->buf()), static_cast(uk_->context()->bufChars())); + eraseChars(uic_->backspaces()); + if (uic_->bufChars() > 0) + preeditStr_.append(reinterpret_cast(uic_->buf()), static_cast(uic_->bufChars())); return true; } if (rawSym >= FcitxKey_KP_Multiply && rawSym <= FcitxKey_KP_9) { - uk_->context()->filter(0); + uic_->filter(0); if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - uk_->resetBuf(); + uic_->resetBuf(); return false; } if (rawSym >= FcitxKey_space && rawSym <= FcitxKey_asciitilde) { //const bool beginWord = uk_->isAtWordBeginning(); - uk_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); - uk_->filter(sym); + uic_->setCapsState(st.test(KeyState::Shift) ? 1 : 0, st.test(KeyState::CapsLock) ? 1 : 0); + uic_->filter(sym); syncState(rawSym); if (!preeditStr_.empty() && preeditStr_.back() == static_cast(sym) && isWordBreakSym(static_cast(sym))) { pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - uk_->resetBuf(); + uic_->resetBuf(); return true; } return true; } - uk_->context()->filter(0); + uic_->filter(0); syncState(rawSym); if (!preeditStr_.empty()) pendingPullCommit_ = preeditStr_; preeditStr_.clear(); - uk_->resetBuf(); + uic_->resetBuf(); return false; } - std::unique_ptr<::fcitx::lotus::LotusUnikeyEngine> uk_; - LotusEngine* engineRef_ = nullptr; + std::unique_ptr im_; + std::unique_ptr uic_; std::string preeditStr_; std::string pendingPullCommit_; KeySym lastShiftPressed_ = FcitxKey_None; diff --git a/src/lotus-utils.cpp b/src/lotus-utils.cpp index 7bbcb7fd..6e30f717 100644 --- a/src/lotus-utils.cpp +++ b/src/lotus-utils.cpp @@ -19,7 +19,6 @@ std::atomic needEngineReset{false}; std::atomic g_mouse_clicked{false}; std::atomic is_deleting_{false}; std::atomic stop_flag_monitor{false}; -std::atomic monitor_running{false}; std::atomic uinput_client_fd_{-1}; std::atomic realtextLen{0}; std::atomic mouse_socket_fd{-1}; diff --git a/src/lotus-utils.h b/src/lotus-utils.h index 94be4394..208233dd 100644 --- a/src/lotus-utils.h +++ b/src/lotus-utils.h @@ -67,7 +67,6 @@ extern std::atomic needEngineReset; ///< Flag to trigge extern std::atomic g_mouse_clicked; ///< Mouse click detection flag extern std::atomic is_deleting_; ///< Deletion in progress flag extern std::atomic stop_flag_monitor; ///< Signal to stop monitor threads -extern std::atomic monitor_running; ///< Monitor thread status extern std::atomic uinput_client_fd_; ///< Uinput client file descriptor extern std::atomic realtextLen; ///< Current text length extern std::atomic mouse_socket_fd; ///< Mouse socket file descriptor diff --git a/unikey/CMakeLists.txt b/unikey/CMakeLists.txt index f64bed99..e51ccf54 100644 --- a/unikey/CMakeLists.txt +++ b/unikey/CMakeLists.txt @@ -14,6 +14,12 @@ set(LOTUS_UNIKEY_CORE_SRCS ${_UK_CORE}/unikeyinputcontext.cpp ) add_library(lotus-unikey-core STATIC ${LOTUS_UNIKEY_CORE_SRCS}) +if (NOT TARGET Unikey::Core) + add_library(Unikey::Core INTERFACE IMPORTED GLOBAL) + set_target_properties(Unikey::Core PROPERTIES + INTERFACE_LINK_LIBRARIES lotus-unikey-core + INTERFACE_INCLUDE_DIRECTORIES "${_UK_CORE}") +endif() set_target_properties(lotus-unikey-core PROPERTIES POSITION_INDEPENDENT_CODE ON) target_link_libraries(lotus-unikey-core PUBLIC Fcitx5::Utils) target_include_directories(lotus-unikey-core PUBLIC "${_UK_CORE}") From 6d5dbeb96c759d30e67d20d0aab252593f6c54e1 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 16 May 2026 16:04:28 +0700 Subject: [PATCH 41/42] cau truc lai --- unikey/CMakeLists.txt | 50 +++++++-------- unikey/LotusUnikeyEngine.cpp | 80 ------------------------ unikey/LotusUnikeyEngine.hpp | 62 ------------------ unikey/{core => }/byteio.cpp | 0 unikey/{core => }/byteio.h | 0 unikey/{core => }/charset.cpp | 0 unikey/{core => }/charset.h | 0 unikey/{core => }/convert.cpp | 0 unikey/{core => }/data.cpp | 0 unikey/{core => }/data.h | 0 unikey/{core => }/inputproc.cpp | 0 unikey/{core => }/inputproc.h | 0 unikey/{core => }/keycons.h | 0 unikey/{core => }/mactab.cpp | 0 unikey/{core => }/mactab.h | 0 unikey/{core => }/pattern.cpp | 0 unikey/{core => }/pattern.h | 0 unikey/{core => }/ukengine.cpp | 0 unikey/{core => }/ukengine.h | 0 unikey/{core => }/unikeyinputcontext.cpp | 0 unikey/{core => }/unikeyinputcontext.h | 0 unikey/{core => }/usrkeymap.cpp | 0 unikey/{core => }/usrkeymap.h | 0 unikey/{core => }/vnconv.h | 0 unikey/{core => }/vnlexi.h | 0 25 files changed, 22 insertions(+), 170 deletions(-) delete mode 100644 unikey/LotusUnikeyEngine.cpp delete mode 100644 unikey/LotusUnikeyEngine.hpp rename unikey/{core => }/byteio.cpp (100%) rename unikey/{core => }/byteio.h (100%) rename unikey/{core => }/charset.cpp (100%) rename unikey/{core => }/charset.h (100%) rename unikey/{core => }/convert.cpp (100%) rename unikey/{core => }/data.cpp (100%) rename unikey/{core => }/data.h (100%) rename unikey/{core => }/inputproc.cpp (100%) rename unikey/{core => }/inputproc.h (100%) rename unikey/{core => }/keycons.h (100%) rename unikey/{core => }/mactab.cpp (100%) rename unikey/{core => }/mactab.h (100%) rename unikey/{core => }/pattern.cpp (100%) rename unikey/{core => }/pattern.h (100%) rename unikey/{core => }/ukengine.cpp (100%) rename unikey/{core => }/ukengine.h (100%) rename unikey/{core => }/unikeyinputcontext.cpp (100%) rename unikey/{core => }/unikeyinputcontext.h (100%) rename unikey/{core => }/usrkeymap.cpp (100%) rename unikey/{core => }/usrkeymap.h (100%) rename unikey/{core => }/vnconv.h (100%) rename unikey/{core => }/vnlexi.h (100%) diff --git a/unikey/CMakeLists.txt b/unikey/CMakeLists.txt index e51ccf54..8ee8b6d2 100644 --- a/unikey/CMakeLists.txt +++ b/unikey/CMakeLists.txt @@ -1,33 +1,27 @@ -# Vendored Unikey engine (core/) + Lotus wrapper -# SPDX-FileCopyrightText: Unikey authors (LGPL/GPL); Lotus wrapper GPL-3.0-or-later -set(_UK_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") -set(LOTUS_UNIKEY_CORE_SRCS - ${_UK_CORE}/byteio.cpp - ${_UK_CORE}/charset.cpp - ${_UK_CORE}/convert.cpp - ${_UK_CORE}/data.cpp - ${_UK_CORE}/inputproc.cpp - ${_UK_CORE}/mactab.cpp - ${_UK_CORE}/pattern.cpp - ${_UK_CORE}/ukengine.cpp - ${_UK_CORE}/usrkeymap.cpp - ${_UK_CORE}/unikeyinputcontext.cpp +# Vendored Unikey engine. +# SPDX-FileCopyrightText: Unikey authors (LGPL/GPL) + +set(UNIKEY_SRCS + byteio.cpp + charset.cpp + convert.cpp + data.cpp + inputproc.cpp + mactab.cpp + pattern.cpp + ukengine.cpp + usrkeymap.cpp + unikeyinputcontext.cpp ) -add_library(lotus-unikey-core STATIC ${LOTUS_UNIKEY_CORE_SRCS}) + +add_library(unikey-lib STATIC ${UNIKEY_SRCS}) +target_link_libraries(unikey-lib PUBLIC Fcitx5::Utils) +set_target_properties(unikey-lib PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(unikey-lib PUBLIC "$") + if (NOT TARGET Unikey::Core) add_library(Unikey::Core INTERFACE IMPORTED GLOBAL) set_target_properties(Unikey::Core PROPERTIES - INTERFACE_LINK_LIBRARIES lotus-unikey-core - INTERFACE_INCLUDE_DIRECTORIES "${_UK_CORE}") + INTERFACE_LINK_LIBRARIES unikey-lib + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}") endif() -set_target_properties(lotus-unikey-core PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_link_libraries(lotus-unikey-core PUBLIC Fcitx5::Utils) -target_include_directories(lotus-unikey-core PUBLIC "${_UK_CORE}") -add_library(lotus-unikey-bridge STATIC - "${CMAKE_CURRENT_SOURCE_DIR}/LotusUnikeyEngine.cpp" -) -target_link_libraries(lotus-unikey-bridge PUBLIC lotus-unikey-core) -target_include_directories(lotus-unikey-bridge PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}" - "${_UK_CORE}" -) diff --git a/unikey/LotusUnikeyEngine.cpp b/unikey/LotusUnikeyEngine.cpp deleted file mode 100644 index 09d615a1..00000000 --- a/unikey/LotusUnikeyEngine.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#include "LotusUnikeyEngine.hpp" -#include "unikeyinputcontext.h" - -namespace fcitx::lotus { - - LotusUnikeyEngine::LotusUnikeyEngine() : im_(std::make_unique()), uic_(std::make_unique(im_.get())) {} - - LotusUnikeyEngine::~LotusUnikeyEngine() = default; - - void LotusUnikeyEngine::setInputMethod(UkInputMethod im) { - im_->setInputMethod(im); - } - - void LotusUnikeyEngine::setOutputCharset(int charsetId) { - im_->setOutputCharset(charsetId); - } - - void LotusUnikeyEngine::setOptions(UnikeyOptions* opt) { - im_->setOptions(opt); - } - - void LotusUnikeyEngine::resetBuf() { - uic_->resetBuf(); - } - - void LotusUnikeyEngine::setCapsState(int shiftPressed, int capsLockOn) { - uic_->setCapsState(shiftPressed, capsLockOn); - } - - void LotusUnikeyEngine::filter(std::uint32_t unikeyKeyCode) { - uic_->filter(unikeyKeyCode); - } - - void LotusUnikeyEngine::putChar(std::uint32_t ch) { - uic_->putChar(ch); - } - - void LotusUnikeyEngine::rebuildChar(VnLexiName ch) { - uic_->rebuildChar(ch); - } - - void LotusUnikeyEngine::backspacePress() { - uic_->backspacePress(); - } - - void LotusUnikeyEngine::restoreKeyStrokes() { - uic_->restoreKeyStrokes(); - } - - bool LotusUnikeyEngine::isAtWordBeginning() const { - return uic_->isAtWordBeginning(); - } - - int LotusUnikeyEngine::backspaces() const { - return uic_->backspaces(); - } - - int LotusUnikeyEngine::bufChars() const { - return uic_->bufChars(); - } - - const unsigned char* LotusUnikeyEngine::buf() const { - return uic_->buf(); - } - - UnikeyInputMethod* LotusUnikeyEngine::inputMethod() { - return im_.get(); - } - - UnikeyInputContext* LotusUnikeyEngine::context() { - return uic_.get(); - } - -} // namespace fcitx::lotus diff --git a/unikey/LotusUnikeyEngine.hpp b/unikey/LotusUnikeyEngine.hpp deleted file mode 100644 index f70d917e..00000000 --- a/unikey/LotusUnikeyEngine.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 fcitx5-lotus contributors - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - * Thin wrapper around fcitx5-unikey's UkEngine stack - * LOTUS_USE_UNIKEY is wired through LotusState. - */ -#ifndef FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP -#define FCITX5_LOTUS_LOTUS_UNIKEY_ENGINE_HPP - -#include "keycons.h" -#include "vnlexi.h" -#include -#include -#include -#include - -class UnikeyInputMethod; -class UnikeyInputContext; - -namespace fcitx::lotus { - - class LotusUnikeyEngine { - public: - LotusUnikeyEngine(); - ~LotusUnikeyEngine(); - - LotusUnikeyEngine(const LotusUnikeyEngine&) = delete; - LotusUnikeyEngine& operator=(const LotusUnikeyEngine&) = delete; - LotusUnikeyEngine(LotusUnikeyEngine&&) = delete; - LotusUnikeyEngine& operator=(LotusUnikeyEngine&&) = delete; - - void setInputMethod(UkInputMethod im); - void setOutputCharset(int charsetId); - void setOptions(UnikeyOptions* opt); - - void resetBuf(); - void setCapsState(int shiftPressed, int capsLockOn); - void filter(std::uint32_t unikeyKeyCode); - void putChar(std::uint32_t ch); - void rebuildChar(VnLexiName ch); - void backspacePress(); - void restoreKeyStrokes(); - - bool isAtWordBeginning() const; - - int backspaces() const; - int bufChars() const; - const unsigned char* buf() const; - - UnikeyInputMethod* inputMethod(); - UnikeyInputContext* context(); - - private: - std::unique_ptr im_; - std::unique_ptr uic_; - }; - -} // namespace fcitx::lotus - -#endif diff --git a/unikey/core/byteio.cpp b/unikey/byteio.cpp similarity index 100% rename from unikey/core/byteio.cpp rename to unikey/byteio.cpp diff --git a/unikey/core/byteio.h b/unikey/byteio.h similarity index 100% rename from unikey/core/byteio.h rename to unikey/byteio.h diff --git a/unikey/core/charset.cpp b/unikey/charset.cpp similarity index 100% rename from unikey/core/charset.cpp rename to unikey/charset.cpp diff --git a/unikey/core/charset.h b/unikey/charset.h similarity index 100% rename from unikey/core/charset.h rename to unikey/charset.h diff --git a/unikey/core/convert.cpp b/unikey/convert.cpp similarity index 100% rename from unikey/core/convert.cpp rename to unikey/convert.cpp diff --git a/unikey/core/data.cpp b/unikey/data.cpp similarity index 100% rename from unikey/core/data.cpp rename to unikey/data.cpp diff --git a/unikey/core/data.h b/unikey/data.h similarity index 100% rename from unikey/core/data.h rename to unikey/data.h diff --git a/unikey/core/inputproc.cpp b/unikey/inputproc.cpp similarity index 100% rename from unikey/core/inputproc.cpp rename to unikey/inputproc.cpp diff --git a/unikey/core/inputproc.h b/unikey/inputproc.h similarity index 100% rename from unikey/core/inputproc.h rename to unikey/inputproc.h diff --git a/unikey/core/keycons.h b/unikey/keycons.h similarity index 100% rename from unikey/core/keycons.h rename to unikey/keycons.h diff --git a/unikey/core/mactab.cpp b/unikey/mactab.cpp similarity index 100% rename from unikey/core/mactab.cpp rename to unikey/mactab.cpp diff --git a/unikey/core/mactab.h b/unikey/mactab.h similarity index 100% rename from unikey/core/mactab.h rename to unikey/mactab.h diff --git a/unikey/core/pattern.cpp b/unikey/pattern.cpp similarity index 100% rename from unikey/core/pattern.cpp rename to unikey/pattern.cpp diff --git a/unikey/core/pattern.h b/unikey/pattern.h similarity index 100% rename from unikey/core/pattern.h rename to unikey/pattern.h diff --git a/unikey/core/ukengine.cpp b/unikey/ukengine.cpp similarity index 100% rename from unikey/core/ukengine.cpp rename to unikey/ukengine.cpp diff --git a/unikey/core/ukengine.h b/unikey/ukengine.h similarity index 100% rename from unikey/core/ukengine.h rename to unikey/ukengine.h diff --git a/unikey/core/unikeyinputcontext.cpp b/unikey/unikeyinputcontext.cpp similarity index 100% rename from unikey/core/unikeyinputcontext.cpp rename to unikey/unikeyinputcontext.cpp diff --git a/unikey/core/unikeyinputcontext.h b/unikey/unikeyinputcontext.h similarity index 100% rename from unikey/core/unikeyinputcontext.h rename to unikey/unikeyinputcontext.h diff --git a/unikey/core/usrkeymap.cpp b/unikey/usrkeymap.cpp similarity index 100% rename from unikey/core/usrkeymap.cpp rename to unikey/usrkeymap.cpp diff --git a/unikey/core/usrkeymap.h b/unikey/usrkeymap.h similarity index 100% rename from unikey/core/usrkeymap.h rename to unikey/usrkeymap.h diff --git a/unikey/core/vnconv.h b/unikey/vnconv.h similarity index 100% rename from unikey/core/vnconv.h rename to unikey/vnconv.h diff --git a/unikey/core/vnlexi.h b/unikey/vnlexi.h similarity index 100% rename from unikey/core/vnlexi.h rename to unikey/vnlexi.h From 53ae96ec7c8d8eeb73df441a3a36ca85352eb159 Mon Sep 17 00:00:00 2001 From: Zebra2711 Date: Sat, 16 May 2026 16:20:58 +0700 Subject: [PATCH 42/42] add macro,.. fcitx5-unikey feat --- CMakeLists.txt | 8 + keymap-editor/CMakeLists.txt | 24 +++ keymap-editor/actions.cpp | 115 ++++++++++++++ keymap-editor/actions.h | 24 +++ keymap-editor/editor.cpp | 221 ++++++++++++++++++++++++++ keymap-editor/editor.h | 73 +++++++++ keymap-editor/editor.ui | 237 ++++++++++++++++++++++++++++ keymap-editor/keymap-editor.json | 4 + keymap-editor/main.cpp | 25 +++ keymap-editor/main.h | 25 +++ keymap-editor/model.cpp | 263 +++++++++++++++++++++++++++++++ keymap-editor/model.h | 55 +++++++ macro-editor/CMakeLists.txt | 24 +++ macro-editor/dialog.cpp | 19 +++ macro-editor/dialog.h | 26 +++ macro-editor/dialog.ui | 93 +++++++++++ macro-editor/editor.cpp | 181 +++++++++++++++++++++ macro-editor/editor.h | 49 ++++++ macro-editor/editor.ui | 114 ++++++++++++++ macro-editor/macro-editor.json | 4 + macro-editor/main.cpp | 29 ++++ macro-editor/main.h | 27 ++++ macro-editor/model.cpp | 127 +++++++++++++++ macro-editor/model.h | 51 ++++++ src/CMakeLists.txt | 2 +- src/lotus-config.h | 3 +- src/lotus-engine.cpp | 4 +- src/lotus-input-backend.cpp | 19 +++ 28 files changed, 1843 insertions(+), 3 deletions(-) create mode 100644 keymap-editor/CMakeLists.txt create mode 100644 keymap-editor/actions.cpp create mode 100644 keymap-editor/actions.h create mode 100644 keymap-editor/editor.cpp create mode 100644 keymap-editor/editor.h create mode 100644 keymap-editor/editor.ui create mode 100644 keymap-editor/keymap-editor.json create mode 100644 keymap-editor/main.cpp create mode 100644 keymap-editor/main.h create mode 100644 keymap-editor/model.cpp create mode 100644 keymap-editor/model.h create mode 100644 macro-editor/CMakeLists.txt create mode 100644 macro-editor/dialog.cpp create mode 100644 macro-editor/dialog.h create mode 100644 macro-editor/dialog.ui create mode 100644 macro-editor/editor.cpp create mode 100644 macro-editor/editor.h create mode 100644 macro-editor/editor.ui create mode 100644 macro-editor/macro-editor.json create mode 100644 macro-editor/main.cpp create mode 100644 macro-editor/main.h create mode 100644 macro-editor/model.cpp create mode 100644 macro-editor/model.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 52b7e0fa..83bf931a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ find_package(X11 REQUIRED) include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake") add_definitions(-DFCITX_GETTEXT_DOMAIN=\"fcitx5-lotus\") +add_definitions(-DQT_NO_KEYWORDS) find_package(Fcitx5Utils REQUIRED) if (Fcitx5Utils_VERSION VERSION_GREATER_EQUAL "5.1.13") @@ -39,6 +40,13 @@ endif() fcitx5_add_i18n_definition() add_subdirectory(unikey) +if (ENABLE_QT) + set(QT_MAJOR_VERSION 6) + find_package(Qt${QT_MAJOR_VERSION} REQUIRED COMPONENTS Core Gui Widgets) + find_package(Fcitx5Qt${QT_MAJOR_VERSION}WidgetsAddons 5.0.12 REQUIRED) + add_subdirectory(macro-editor) + add_subdirectory(keymap-editor) +endif() add_subdirectory(po) add_subdirectory(src) add_subdirectory(data) diff --git a/keymap-editor/CMakeLists.txt b/keymap-editor/CMakeLists.txt new file mode 100644 index 00000000..3c4f507e --- /dev/null +++ b/keymap-editor/CMakeLists.txt @@ -0,0 +1,24 @@ + + +set(KEYMAP_EDITOR_SRCS + main.cpp + editor.cpp + model.cpp + actions.cpp + ) + +add_library(fcitx5-lotus-keymap-editor + MODULE ${KEYMAP_EDITOR_SRCS}) +set_target_properties(fcitx5-lotus-keymap-editor PROPERTIES + AUTOMOC TRUE + AUTOUIC TRUE + AUTOUIC_OPTIONS "-tr=fcitx::tr2fcitx;--include=fcitxqti18nhelper.h" +) +target_link_libraries(fcitx5-lotus-keymap-editor + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Widgets + Fcitx5Qt${QT_MAJOR_VERSION}::WidgetsAddons + unikey-lib + ) + +install(TARGETS fcitx5-lotus-keymap-editor DESTINATION ${CMAKE_INSTALL_LIBDIR}/fcitx5/qt${QT_MAJOR_VERSION}) diff --git a/keymap-editor/actions.cpp b/keymap-editor/actions.cpp new file mode 100644 index 00000000..f812ae3c --- /dev/null +++ b/keymap-editor/actions.cpp @@ -0,0 +1,115 @@ +#include "actions.h" +#include "inputproc.h" +#include +#include +#include + +namespace fcitx::unikey { + +const std::vector> &actionNames() { + static const auto names = []() { + std::vector> result; + const std::tuple UkEvNameList[] = { + {N_("Remove existing tone"), vneTone0, AC_Tone}, + {N_("Tone ' (acute)"), vneTone1, AC_Tone}, + {N_("Tone ` (grave)"), vneTone2, AC_Tone}, + {N_("Tone ◌̉ (hook above)"), vneTone3, AC_Tone}, + {N_("Tone ~ (tilde)"), vneTone4, AC_Tone}, + {N_("Tone . (dot below)"), vneTone5, AC_Tone}, + {N_("Escape key"), vneEscChar, AC_Tone}, + {N_("Circumflex for all applicable characters"), vneRoofAll, + AC_ChrComp}, + {N_("Circumflex: A becomes A^"), vneRoof_a, AC_ChrComp}, + {N_("Circumflex: E becomes E^"), vneRoof_e, AC_ChrComp}, + {N_("Circumflex: O becomes O^"), vneRoof_o, AC_ChrComp}, + {N_("Horn-Breve: U, O, A, become U+, O+, A("), vneHookAll, + AC_ChrComp}, + {N_("Horn: U, O become U+, O+"), vneHook_uo, AC_ChrComp}, + {N_("Horn: U becomes U+"), vneHook_u, AC_ChrComp}, + {N_("Horn: O becomes O+"), vneHook_o, AC_ChrComp}, + {N_("Breve: A becomes A("), vneBowl, AC_ChrComp}, + {N_("Stroke: D becomes -D"), vneDd, AC_ChrComp}, + {N_("Horn-Breve: U, O, A, become U+, O+, A(, or create U+"), + vne_telex_w, AC_ChrComp}, + {N_("D with stroke [-D]"), vneCount + vnl_DD, AC_Viet}, + {N_("d with stroke [-d]"), vneCount + vnl_dd, AC_Viet}, + {N_("A with circumflex [A^]"), vneCount + vnl_Ar, AC_Viet}, + {N_("a with circumflex [a^]"), vneCount + vnl_ar, AC_Viet}, + {N_("A with breve [A(]"), vneCount + vnl_Ab, AC_Viet}, + {N_("a with breve [a(]"), vneCount + vnl_ab, AC_Viet}, + {N_("E with circumflex [E^]"), vneCount + vnl_Er, AC_Viet}, + {N_("e with circumflex [e^]"), vneCount + vnl_er, AC_Viet}, + {N_("O with circumflex [O^]"), vneCount + vnl_Or, AC_Viet}, + {N_("o with circumflex [o^]"), vneCount + vnl_or, AC_Viet}, + {N_("O with horn [O+]"), vneCount + vnl_Oh, AC_Viet}, + {N_("o with horn [o+]"), vneCount + vnl_oh, AC_Viet}, + {N_("U with horn [U+]"), vneCount + vnl_Uh, AC_Viet}, + {N_("u with horn [u+]"), vneCount + vnl_uh, AC_Viet}}; + result.reserve(FCITX_ARRAY_SIZE(UkEvNameList)); + for (const auto &item : UkEvNameList) { + result.push_back(item); + } + return result; + }(); + return names; +} + +static const std::string emptyString; +const std::string &actionName(int action) { + static const auto actionToNameMap = []() { + std::unordered_map result; + for (const auto &[name, action, _] : actionNames()) { + result[action] = name; + } + return result; + }(); + + if (auto iter = actionToNameMap.find(action); + iter != actionToNameMap.end()) { + return iter->second; + } + return emptyString; +} + +int actionCategory(int action) { + switch (action) { + case vneTone0: + case vneTone1: + case vneTone2: + case vneTone3: + case vneTone4: + case vneTone5: + case vneEscChar: + return AC_Tone; + case vneRoofAll: + case vneRoof_a: + case vneRoof_e: + case vneRoof_o: + case vneHookAll: + case vneHook_uo: + case vneHook_u: + case vneHook_o: + case vneBowl: + case vneDd: + case vne_telex_w: + return AC_ChrComp; + case vneCount + vnl_DD: + case vneCount + vnl_dd: + case vneCount + vnl_Ar: + case vneCount + vnl_ar: + case vneCount + vnl_Ab: + case vneCount + vnl_ab: + case vneCount + vnl_Er: + case vneCount + vnl_er: + case vneCount + vnl_Or: + case vneCount + vnl_or: + case vneCount + vnl_Oh: + case vneCount + vnl_oh: + case vneCount + vnl_Uh: + case vneCount + vnl_uh: + return AC_Viet; + } + return -1; +} + +} // namespace fcitx::unikey diff --git a/keymap-editor/actions.h b/keymap-editor/actions.h new file mode 100644 index 00000000..2d3f2066 --- /dev/null +++ b/keymap-editor/actions.h @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _KEYMAP_EDITOR_ACTIONS_H_ +#define _KEYMAP_EDITOR_ACTIONS_H_ + +#include +#include +#include + +namespace fcitx::unikey { + +enum ActionCategory { AC_Tone, AC_ChrComp, AC_Viet }; + +const std::vector> &actionNames(); +const std::string &actionName(int action); +int actionCategory(int action); + +} // namespace fcitx::unikey + +#endif diff --git a/keymap-editor/editor.cpp b/keymap-editor/editor.cpp new file mode 100644 index 00000000..e39ffc75 --- /dev/null +++ b/keymap-editor/editor.cpp @@ -0,0 +1,221 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include "editor.h" +#include "actions.h" +#include "keycons.h" +#include "model.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx::unikey { + +KeymapEditor::KeymapEditor(QWidget *parent) : FcitxQtConfigUIWidget(parent) { + setupUi(this); + + keySequenceEdit->setKeycodeAllowed(false); + keySequenceEdit->setModifierAllowed(false); + keySequenceEdit->setModifierlessAllowed(true); + + connect(addButton, &QPushButton::clicked, this, &KeymapEditor::addKeymap); + connect(moveUpButton, &QPushButton::clicked, this, [this]() { + if (auto index = keymapView->currentIndex(); index.isValid()) { + model_->moveUp(index.row()); + } + }); + connect(moveDownButton, &QPushButton::clicked, this, [this]() { + if (auto index = keymapView->currentIndex(); index.isValid()) { + model_->moveDown(index.row()); + } + }); + connect(deleteButton, &QPushButton::clicked, this, + &KeymapEditor::deleteKeymap); + connect(clearButton, &QPushButton::clicked, this, + &KeymapEditor::deleteAllKeymap); + connect(importButton, &QPushButton::clicked, this, + &KeymapEditor::importKeymap); + connect(exportButton, &QPushButton::clicked, this, + &KeymapEditor::exportKeymap); + + inputMethodBox->addItem(_("Telex"), UkTelex); + inputMethodBox->addItem(_("VNI"), UkVni); + inputMethodBox->addItem(_("VIQR"), UkViqr); + inputMethodBox->addItem(_("Microsoft Vietnamese"), UkMsVi); + inputMethodBox->addItem(_("Simple Telex"), UkSimpleTelex); + inputMethodBox->addItem(_("Simple Telex2"), UkSimpleTelex2); + + categoryBox->addItem(_("Tone marks")); + categoryBox->addItem(_("Character complements")); + categoryBox->addItem(_("Vietnamese characters")); + + for (const auto &[text, action, category] : actionNames()) { + QStandardItem *item = + new QStandardItem(QString::fromStdString(_(text))); + item->setData(action, Qt::UserRole); + item->setData(category, Qt::UserRole + 1); + actionModel_.insertRow(actionModel_.rowCount(), item); + } + + filteredActionModel_.setSourceModel(&actionModel_); + + actionBox->setModel(&filteredActionModel_); + connect(categoryBox, qOverload(&QComboBox::currentIndexChanged), + &filteredActionModel_, &ActionFilterModel::setCategory); + connect(categoryBox, qOverload(&QComboBox::currentIndexChanged), this, + [this]() { actionBox->setCurrentIndex(0); }); + categoryBox->setCurrentIndex(0); + + model_ = new KeymapModel(this); + keymapView->horizontalHeader()->setStretchLastSection(true); + keymapView->verticalHeader()->setVisible(false); + keymapView->setModel(model_); + connect(keymapView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &KeymapEditor::itemFocusChanged); + connect(model_, &QAbstractItemModel::rowsMoved, this, + &KeymapEditor::itemFocusChanged); + connect(model_, &KeymapModel::needSaveChanged, this, + &KeymapEditor::changed); + connect(keySequenceEdit, &FcitxQtKeySequenceWidget::keySequenceChanged, + this, [this]() { addButton->setEnabled(keySequenceValid()); }); + + load(); + itemFocusChanged(); + addButton->setEnabled(keySequenceValid()); + + connect(loadButton, &QPushButton::clicked, this, + [this]() { model_->load(inputMethodBox->currentData().toInt()); }); +} + +KeymapEditor::~KeymapEditor() {} + +QString KeymapEditor::icon() { return "fcitx-lotus"; } + +QString KeymapEditor::title() { return _("Lotus Keymap Editor"); } + +void KeymapEditor::itemFocusChanged() { + bool hasSelection = keymapView->currentIndex().isValid(); + deleteButton->setEnabled(hasSelection); + moveUpButton->setEnabled(hasSelection && + keymapView->currentIndex().row() > 0); + moveDownButton->setEnabled(hasSelection && + keymapView->currentIndex().row() + 1 < + model_->rowCount()); + if (hasSelection) { + auto chr = model_->index(keymapView->currentIndex().row(), 0) + .data(Qt::UserRole) + .toChar(); + keySequenceEdit->setKeySequence( + QList() << Key(KeySym(chr.unicode()), KeyStates(), 0)); + auto action = model_->index(keymapView->currentIndex().row(), 1) + .data(Qt::UserRole) + .toInt(); + auto category = actionCategory(action); + if (category >= 0) { + categoryBox->setCurrentIndex(category); + for (int i = 0; i < filteredActionModel_.rowCount(); i++) { + if (auto index = filteredActionModel_.index(i, 0); + index.data(Qt::UserRole) == action) { + actionBox->setCurrentIndex(i); + } + } + } + } +} + +bool KeymapEditor::keySequenceValid() const { + if (keySequenceEdit->keySequence().empty()) { + return false; + } + auto key = keySequenceEdit->keySequence()[0]; + return key.isValid() && key.isSimple(); +} + +void KeymapEditor::deleteKeymap() { + if (!keymapView->currentIndex().isValid()) { + return; + } + int row = keymapView->currentIndex().row(); + model_->deleteItem(row); +} + +void KeymapEditor::deleteAllKeymap() { model_->deleteAllItem(); } + +void KeymapEditor::addKeymap() { + if (!keySequenceValid()) { + return; + } + auto action = actionBox->currentData(Qt::UserRole); + if (!action.isValid()) { + return; + } + auto key = keySequenceEdit->keySequence()[0]; + unsigned char chr = key.sym() & 0xff; + + auto index = model_->addItem(chr, action.toInt()); + keymapView->setCurrentIndex(index); +} + +void KeymapEditor::load() { model_->load(); } + +void KeymapEditor::save() { model_->save(); } + +void KeymapEditor::importKeymap() { + QFileDialog *dialog = new QFileDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + dialog->setFileMode(QFileDialog::ExistingFile); + dialog->setAcceptMode(QFileDialog::AcceptOpen); + dialog->open(); + connect(dialog, &QFileDialog::accepted, this, + &KeymapEditor::importFileSelected); +} + +void KeymapEditor::importFileSelected() { + const QFileDialog *dialog = + qobject_cast(QObject::sender()); + if (dialog->selectedFiles().isEmpty()) { + return; + } + QString file = dialog->selectedFiles()[0]; + model_->load(file); +} + +void KeymapEditor::exportKeymap() { + QFileDialog *dialog = new QFileDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + dialog->setAcceptMode(QFileDialog::AcceptSave); + dialog->open(); + connect(dialog, &QFileDialog::accepted, this, + &KeymapEditor::exportFileSelected); +} + +void KeymapEditor::exportFileSelected() { + const QFileDialog *dialog = + qobject_cast(QObject::sender()); + if (dialog->selectedFiles().length() <= 0) { + return; + } + QString file = dialog->selectedFiles()[0]; + model_->save(file); +} + +} // namespace fcitx::unikey diff --git a/keymap-editor/editor.h b/keymap-editor/editor.h new file mode 100644 index 00000000..047ddcfe --- /dev/null +++ b/keymap-editor/editor.h @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2022-2022 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _KEYMAP_EDITOR_EDITOR_H_ +#define _KEYMAP_EDITOR_EDITOR_H_ + +#include "ui_editor.h" +#include +#include +#include +#include + +class CKeymapTable; + +namespace fcitx { +namespace unikey { + +class ActionFilterModel : public QSortFilterProxyModel { + Q_OBJECT +public Q_SLOTS: + void setCategory(int category) { + category_ = category; + invalidate(); + } + +protected: + bool filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const override { + const QModelIndex index = + sourceModel()->index(sourceRow, 0, sourceParent); + return index.data(Qt::UserRole + 1) == category_; + } + +private: + int category_ = 0; +}; + +class KeymapModel; +class KeymapEditor : public FcitxQtConfigUIWidget, public Ui::Editor { + Q_OBJECT +public: + explicit KeymapEditor(QWidget *parent = 0); + virtual ~KeymapEditor(); + void load() override; + void save() override; + QString title() override; + QString icon() override; + + static QString getData(CKeymapTable *table, int i, bool iskey); +private Q_SLOTS: + void addKeymap(); + void deleteKeymap(); + void deleteAllKeymap(); + void itemFocusChanged(); + bool keySequenceValid() const; + void importKeymap(); + void exportKeymap(); + void importFileSelected(); + void exportFileSelected(); + +private: + CKeymapTable *table_; + KeymapModel *model_; + QStandardItemModel actionModel_; + ActionFilterModel filteredActionModel_; +}; +} // namespace unikey +} // namespace fcitx + +#endif // _MACRO_EDITOR_EDITOR_H_ diff --git a/keymap-editor/editor.ui b/keymap-editor/editor.ui new file mode 100644 index 00000000..fec4f37f --- /dev/null +++ b/keymap-editor/editor.ui @@ -0,0 +1,237 @@ + + + Editor + + + + 0 + 0 + 645 + 555 + + + + Lotus Keymap Editor + + + + .. + + + + + + Built-in Input Methods + + + + + + Input Method: + + + + + + + + 0 + 0 + + + + + + + + &Load + + + + + + + + + + + + + + Key definition + + + + + + Category: + + + + + + + + 0 + 0 + + + + + + + + Action: + + + + + + + + 0 + 0 + + + + + + + + Key + + + + + + + + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + + &Add / Update + + + + .. + + + + + + + Move &Up + + + + .. + + + + + + + Move &Down + + + + .. + + + + + + + &Delete + + + + .. + + + + + + + De&lete All + + + + .. + + + + + + + Qt::Horizontal + + + + + + + &Import + + + + .. + + + + + + + &Export + + + + .. + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + fcitx::FcitxQtKeySequenceWidget + QWidget +
fcitxqtkeysequencewidget.h
+
+
+ + +
diff --git a/keymap-editor/keymap-editor.json b/keymap-editor/keymap-editor.json new file mode 100644 index 00000000..f22bc9ed --- /dev/null +++ b/keymap-editor/keymap-editor.json @@ -0,0 +1,4 @@ +{ + "addon": "lotus", + "files": ["keymap.txt"] +} diff --git a/keymap-editor/main.cpp b/keymap-editor/main.cpp new file mode 100644 index 00000000..1dcd4a8d --- /dev/null +++ b/keymap-editor/main.cpp @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2022-2022 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include "main.h" +#include "editor.h" +#include +#include +#include + +namespace fcitx { + +KeymapEditorPlugin::KeymapEditorPlugin(QObject *parent) + : FcitxQtConfigUIPlugin(parent) { + registerDomain("fcitx5-lotus", FCITX_INSTALL_LOCALEDIR); +} + +FcitxQtConfigUIWidget *KeymapEditorPlugin::create(const QString &key) { + Q_UNUSED(key); + return new fcitx::unikey::KeymapEditor; +} + +} // namespace fcitx diff --git a/keymap-editor/main.h b/keymap-editor/main.h new file mode 100644 index 00000000..c2dd9520 --- /dev/null +++ b/keymap-editor/main.h @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _KEYMAP_EDITOR_MAIN_H_ +#define _KEYMAP_EDITOR_MAIN_H_ + +#include + +namespace fcitx { + +class KeymapEditorPlugin : public FcitxQtConfigUIPlugin { + Q_OBJECT +public: + Q_PLUGIN_METADATA(IID FcitxQtConfigUIFactoryInterface_iid FILE + "keymap-editor.json") + explicit KeymapEditorPlugin(QObject *parent = 0); + FcitxQtConfigUIWidget *create(const QString &key) override; +}; + +} // namespace fcitx + +#endif // _KEYMAP_EDITOR_MAIN_H_ diff --git a/keymap-editor/model.cpp b/keymap-editor/model.cpp new file mode 100644 index 00000000..8748a197 --- /dev/null +++ b/keymap-editor/model.cpp @@ -0,0 +1,263 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include + +#include "actions.h" +#include "editor.h" +#include "inputproc.h" +#include "keycons.h" +#include "model.h" +#include "usrkeymap.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx::unikey { + +using ItemType = std::pair; + +KeymapModel::KeymapModel(QObject *parent) + : QAbstractTableModel(parent), needSave_(false) {} + +KeymapModel::~KeymapModel() {} + +QVariant KeymapModel::headerData(int section, Qt::Orientation orientation, + int role) const { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + if (section == 0) { + return _("Keymap"); + } + if (section == 1) { + return _("Word"); + } + } + return {}; +} + +int KeymapModel::rowCount(const QModelIndex & /*parent*/) const { + return list_.size(); +} + +int KeymapModel::columnCount(const QModelIndex & /*parent*/) const { return 2; } + +QVariant KeymapModel::data(const QModelIndex &index, int role) const { + if (index.row() >= static_cast(list_.size()) || index.row() < 0) { + return {}; + } + + if (role == Qt::DisplayRole) { + if (index.column() == 0) { + return QString(QChar(list_[index.row()].key)); + } + if (index.column() == 1) { + return QString::fromStdString( + _(actionName(list_[index.row()].action))); + } + } else if (role == Qt::UserRole) { + if (index.column() == 0) { + return QChar(list_[index.row()].key); + } + if (index.column() == 1) { + return list_[index.row()].action; + } + } + return QVariant(); +} + +QModelIndex KeymapModel::addItem(unsigned char key, int action) { + beginResetModel(); + bool checkBoth = false; + if (action < vneCount) { + key = charutils::toupper(key); + checkBoth = true; + } + const unsigned char lower = charutils::tolower(key); + bool updated = false; + auto match = [key, checkBoth, lower](const UkKeyMapping &item) { + if (item.action < vneCount && + charutils::toupper(item.key) == charutils::toupper(key)) { + return true; + } + return item.key == key || (checkBoth && item.key == lower); + }; + + auto iter = list_.begin(); + for (; iter != list_.end(); iter++) { + if (match(*iter)) { + *iter = UkKeyMapping{.key = key, .action = action}; + updated = true; + break; + } + } + + int selectRow = 0; + if (updated) { + selectRow = std::distance(list_.begin(), iter); + list_.erase(std::remove_if(std::next(iter), list_.end(), match), + list_.end()); + } else { + selectRow = list_.size(); + list_.push_back(UkKeyMapping{key, action}); + } + endResetModel(); + setNeedSave(true); + return index(selectRow, 0); +} + +void KeymapModel::moveUp(int row) { + if (row >= static_cast(list_.size()) || row <= 0) { + return; + } + if (!beginMoveRows(QModelIndex(), row, row, QModelIndex(), row - 1)) { + return; + } + std::swap(list_[row - 1], list_[row]); + endMoveRows(); + setNeedSave(true); +} + +void KeymapModel::moveDown(int row) { + if (row + 1 >= static_cast(list_.size()) || row < 0) { + return; + } + if (!beginMoveRows(QModelIndex(), row, row, QModelIndex(), row + 2)) { + return; + } + std::swap(list_[row], list_[row + 1]); + endMoveRows(); + setNeedSave(true); +} + +void KeymapModel::deleteItem(int row) { + if (row >= static_cast(list_.size())) { + return; + } + beginRemoveRows(QModelIndex(), row, row); + list_.erase(list_.begin() + row); + endRemoveRows(); + setNeedSave(true); +} + +void KeymapModel::deleteAllItem() { + if (!list_.empty()) { + setNeedSave(true); + } + beginResetModel(); + list_.clear(); + endResetModel(); +} + +void KeymapModel::setNeedSave(bool needSave) { + if (needSave_ != needSave) { + needSave_ = needSave; + Q_EMIT needSaveChanged(needSave_); + } +} + +bool KeymapModel::needSave() const { return needSave_; } + +void KeymapModel::load() { + beginResetModel(); + auto keymapFile = StandardPaths::global().open(StandardPathsType::PkgConfig, + "lotus/keymap.txt"); + if (keymapFile.isValid()) { + list_ = UkLoadKeyOrderMap(keymapFile.fd()); + } else { + list_.clear(); + } + endResetModel(); +} + +void KeymapModel::save() { + StandardPaths::global().safeSave(StandardPathsType::PkgConfig, + "lotus/keymap.txt", + [this](int fd) { return saveToFd(fd); }); + setNeedSave(false); +} + +void KeymapModel::load(const QString &file) { + UnixFD fd = UnixFD::own(open(file.toLocal8Bit().constData(), O_RDONLY)); + + if (!fd.isValid()) { + return; + } + + beginResetModel(); + list_ = UkLoadKeyOrderMap(fd.fd()); + endResetModel(); + setNeedSave(true); +} + +void KeymapModel::save(const QString &file) { + if (!file.startsWith("/")) { + return; + } + StandardPaths::global().safeSave(StandardPathsType::PkgConfig, + file.toLocal8Bit().constData(), + [this](int fd) { return saveToFd(fd); }); + setNeedSave(false); +} + +bool KeymapModel::saveToFd(int fd) { + UnixFD unixFD(fd); + auto fp = fs::openFD(unixFD, "wb"); + if (!fp) { + return false; + } + UkStoreKeyOrderMap(fp.get(), list_); + return true; +} + +void KeymapModel::load(int profile) { + const UkKeyMapping *mapping = nullptr; + switch (profile) { + case UkTelex: + mapping = TelexMethodMapping; + break; + case UkSimpleTelex: + mapping = SimpleTelexMethodMapping; + break; + case UkSimpleTelex2: + mapping = SimpleTelex2MethodMapping; + break; + case UkVni: + mapping = VniMethodMapping; + break; + case UkViqr: + mapping = VIQRMethodMapping; + break; + case UkMsVi: + mapping = MsViMethodMapping; + break; + default: + break; + } + if (!mapping) { + return; + } + + beginResetModel(); + list_.clear(); + for (size_t i = 0; mapping[i].key != 0; i++) { + list_.push_back(mapping[i]); + } + endResetModel(); + setNeedSave(true); +} + +} // namespace fcitx::unikey diff --git a/keymap-editor/model.h b/keymap-editor/model.h new file mode 100644 index 00000000..3db540a0 --- /dev/null +++ b/keymap-editor/model.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _KEYMAP_EDITOR_MODEL_H_ +#define _KEYMAP_EDITOR_MODEL_H_ + +#include "inputproc.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx::unikey { +class KeymapModel : public QAbstractTableModel { + Q_OBJECT +public: + explicit KeymapModel(QObject *parent = 0); + virtual ~KeymapModel(); + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, + int role = Qt::DisplayRole) const override; + void load(); + QModelIndex addItem(unsigned char key, int action); + void moveUp(int row); + void moveDown(int row); + void deleteItem(int row); + void deleteAllItem(); + void save(); + bool needSave() const; + void load(const QString &fileName); + void save(const QString &fileName); + void load(int profile); + +Q_SIGNALS: + void needSaveChanged(bool); + +private: + void setNeedSave(bool needSave); + bool saveToFd(int fd); + bool needSave_; + std::vector list_; +}; +} // namespace fcitx::unikey + +#endif // _MACRO_EDITOR_MODEL_H_ diff --git a/macro-editor/CMakeLists.txt b/macro-editor/CMakeLists.txt new file mode 100644 index 00000000..e14b8c7b --- /dev/null +++ b/macro-editor/CMakeLists.txt @@ -0,0 +1,24 @@ + + +set(MACRO_EDITOR_SRCS + model.cpp + main.cpp + editor.cpp + dialog.cpp + ) + +add_library(fcitx5-lotus-macro-editor + MODULE ${MACRO_EDITOR_SRCS}) +set_target_properties(fcitx5-lotus-macro-editor PROPERTIES + AUTOMOC TRUE + AUTOUIC TRUE + AUTOUIC_OPTIONS "-tr=fcitx::tr2fcitx;--include=fcitxqti18nhelper.h" +) +target_link_libraries(fcitx5-lotus-macro-editor + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Widgets + Fcitx5Qt${QT_MAJOR_VERSION}::WidgetsAddons + unikey-lib + ) + +install(TARGETS fcitx5-lotus-macro-editor DESTINATION ${CMAKE_INSTALL_LIBDIR}/fcitx5/qt${QT_MAJOR_VERSION}) diff --git a/macro-editor/dialog.cpp b/macro-editor/dialog.cpp new file mode 100644 index 00000000..46e749e9 --- /dev/null +++ b/macro-editor/dialog.cpp @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include "dialog.h" +#include +#include +#include + +namespace fcitx::unikey { +MacroDialog::MacroDialog(QWidget *parent) : QDialog(parent) { setupUi(this); } + +QString MacroDialog::macro() const { return macroLineEdit->text(); } + +QString MacroDialog::word() const { return wordLineEdit->text(); } + +} // namespace fcitx::unikey diff --git a/macro-editor/dialog.h b/macro-editor/dialog.h new file mode 100644 index 00000000..9adc6708 --- /dev/null +++ b/macro-editor/dialog.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _MACRO_EDITOR_DIALOG_H_ +#define _MACRO_EDITOR_DIALOG_H_ + +#include "ui_dialog.h" +#include +#include +#include + +namespace fcitx::unikey { + +class MacroDialog : public QDialog, private Ui::Dialog { + Q_OBJECT +public: + explicit MacroDialog(QWidget *parent = nullptr); + QString macro() const; + QString word() const; +}; +} // namespace fcitx::unikey + +#endif // _MACRO_EDITOR_DIALOG_H_ diff --git a/macro-editor/dialog.ui b/macro-editor/dialog.ui new file mode 100644 index 00000000..729f6a26 --- /dev/null +++ b/macro-editor/dialog.ui @@ -0,0 +1,93 @@ + + + Dialog + + + + 0 + 0 + 334 + 91 + + + + Dialog + + + + + + + + + + + Word: + + + + + + + Macro: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + macroLineEdit + wordLineEdit + buttonBox + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/macro-editor/editor.cpp b/macro-editor/editor.cpp new file mode 100644 index 00000000..ebf4017c --- /dev/null +++ b/macro-editor/editor.cpp @@ -0,0 +1,181 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include "editor.h" +#include "charset.h" +#include "dialog.h" +#include "keycons.h" +#include "mactab.h" +#include "model.h" +#include "vnconv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx::unikey { + +MacroEditor::MacroEditor(QWidget *parent) + : FcitxQtConfigUIWidget(parent), table_(std::make_unique()), + model_(new MacroModel(this)) { + setupUi(this); + + connect(addButton, &QPushButton::clicked, this, &MacroEditor::addWord); + connect(deleteButton, &QPushButton::clicked, this, + &MacroEditor::deleteWord); + connect(clearButton, &QPushButton::clicked, this, + &MacroEditor::deleteAllWord); + connect(importButton, &QPushButton::clicked, this, + &MacroEditor::importMacro); + connect(exportButton, &QPushButton::clicked, this, + &MacroEditor::exportMacro); + table_->init(); + macroTableView->horizontalHeader()->setStretchLastSection(true); + macroTableView->verticalHeader()->setVisible(false); + macroTableView->setModel(model_); + connect(macroTableView->selectionModel(), + &QItemSelectionModel::selectionChanged, this, + &MacroEditor::itemFocusChanged); + connect(model_, &MacroModel::needSaveChanged, this, &MacroEditor::changed); + load(); + itemFocusChanged(); +} + +MacroEditor::~MacroEditor() {} + +QString MacroEditor::icon() { return "fcitx-lotus"; } + +QString MacroEditor::title() { return _("Lotus Macro Editor"); } + +void MacroEditor::itemFocusChanged() { + deleteButton->setEnabled(macroTableView->currentIndex().isValid()); +} + +void MacroEditor::deleteWord() { + if (!macroTableView->currentIndex().isValid()) { + return; + } + int row = macroTableView->currentIndex().row(); + model_->deleteItem(row); +} + +void MacroEditor::deleteAllWord() { model_->deleteAllItem(); } + +void MacroEditor::addWord() { + auto *dialog = new MacroDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + dialog->open(); + connect(dialog, &QDialog::accepted, this, &MacroEditor::addWordAccepted); +} + +QString MacroEditor::getData(CMacroTable *table, int i, bool iskey) { + + char key[MAX_MACRO_KEY_LEN * 3]; + char value[MAX_MACRO_TEXT_LEN * 3]; + do { + if (i < table->getCount()) { + const StdVnChar *p = nullptr; + int maxOutLen = 0; + const char *result = nullptr; + if (iskey) { + p = table->getKey(i); + maxOutLen = sizeof(key); + result = key; + } else { + p = table->getText(i); + maxOutLen = sizeof(value); + result = value; + } + + if (!p) { + break; + } + int inLen = -1; + int ret = + VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_XUTF8, + (UKBYTE *)p, (UKBYTE *)result, &inLen, &maxOutLen); + if (ret != 0) { + break; + } + return QString::fromUtf8(result); + } + } while (0); + return QString(); +} + +void MacroEditor::addWordAccepted() { + const auto *dialog = qobject_cast(QObject::sender()); + + model_->addItem(dialog->macro(), dialog->word()); +} + +void MacroEditor::load() { + auto path = StandardPaths::global().locate(StandardPathsType::PkgConfig, + "lotus/macro"); + table_->loadFromFile(path.string().c_str()); + model_->load(table_.get()); +} + +void MacroEditor::save() { + model_->save(table_.get()); + StandardPaths::global().safeSave(StandardPathsType::PkgConfig, + "lotus/macro", [this](int fd) -> bool { + UnixFD unixFD(fd); + auto f = fs::openFD(unixFD, "wb"); + return table_->writeToFp(f.release()); + }); +} + +void MacroEditor::importMacro() { + auto *dialog = new QFileDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + dialog->setFileMode(QFileDialog::ExistingFile); + dialog->setAcceptMode(QFileDialog::AcceptOpen); + dialog->open(); + connect(dialog, &QFileDialog::accepted, this, + &MacroEditor::importFileSelected); +} + +void MacroEditor::importFileSelected() { + const auto *dialog = qobject_cast(QObject::sender()); + if (dialog->selectedFiles().length() <= 0) { + return; + } + QString file = dialog->selectedFiles()[0]; + table_->loadFromFile(file.toUtf8().constData()); +} + +void MacroEditor::exportMacro() { + auto *dialog = new QFileDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + dialog->setDirectory("macro"); + dialog->setAcceptMode(QFileDialog::AcceptSave); + dialog->open(); + connect(dialog, &QFileDialog::accepted, this, + &MacroEditor::exportFileSelected); +} + +void MacroEditor::exportFileSelected() { + const auto *dialog = qobject_cast(QObject::sender()); + if (dialog->selectedFiles().length() <= 0) { + return; + } + QString file = dialog->selectedFiles()[0]; + table_->writeToFile(file.toUtf8().constData()); +} + +} // namespace fcitx::unikey diff --git a/macro-editor/editor.h b/macro-editor/editor.h new file mode 100644 index 00000000..42f311b3 --- /dev/null +++ b/macro-editor/editor.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _MACRO_EDITOR_EDITOR_H_ +#define _MACRO_EDITOR_EDITOR_H_ + +#include "ui_editor.h" +#include +#include +#include +#include + +class CMacroTable; + +namespace fcitx::unikey { + +class MacroModel; +class MacroEditor : public FcitxQtConfigUIWidget, public Ui::Editor { + Q_OBJECT +public: + explicit MacroEditor(QWidget *parent = 0); + virtual ~MacroEditor(); + void load() override; + void save() override; + QString title() override; + QString icon() override; + + static QString getData(CMacroTable *table, int i, bool iskey); +private Q_SLOTS: + void addWord(); + void deleteWord(); + void deleteAllWord(); + void itemFocusChanged(); + void addWordAccepted(); + void importMacro(); + void exportMacro(); + void importFileSelected(); + void exportFileSelected(); + +private: + std::unique_ptr table_; + MacroModel *model_; +}; +} // namespace fcitx::unikey + +#endif // _MACRO_EDITOR_EDITOR_H_ diff --git a/macro-editor/editor.ui b/macro-editor/editor.ui new file mode 100644 index 00000000..fd9998c9 --- /dev/null +++ b/macro-editor/editor.ui @@ -0,0 +1,114 @@ + + + Editor + + + + 0 + 0 + 375 + 366 + + + + Lotus Macro Editor + + + + .. + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + + + + + + &Add + + + + .. + + + + + + + &Delete + + + + .. + + + + + + + De&lete All + + + + .. + + + + + + + Qt::Horizontal + + + + + + + &Import + + + + .. + + + + + + + &Export + + + + .. + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/macro-editor/macro-editor.json b/macro-editor/macro-editor.json new file mode 100644 index 00000000..49165d4f --- /dev/null +++ b/macro-editor/macro-editor.json @@ -0,0 +1,4 @@ +{ + "addon": "lotus", + "files": ["macro"] +} diff --git a/macro-editor/main.cpp b/macro-editor/main.cpp new file mode 100644 index 00000000..c349acf2 --- /dev/null +++ b/macro-editor/main.cpp @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include "main.h" +#include "editor.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx { + +MacroEditorPlugin::MacroEditorPlugin(QObject *parent) + : FcitxQtConfigUIPlugin(parent) { + registerDomain("fcitx5-lotus", FCITX_INSTALL_LOCALEDIR); +} + +FcitxQtConfigUIWidget *MacroEditorPlugin::create(const QString &key) { + FCITX_UNUSED(key); + return new fcitx::unikey::MacroEditor; +} + +} // namespace fcitx diff --git a/macro-editor/main.h b/macro-editor/main.h new file mode 100644 index 00000000..40af0c75 --- /dev/null +++ b/macro-editor/main.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _MACRO_EDITOR_MAIN_H_ +#define _MACRO_EDITOR_MAIN_H_ + +#include +#include +#include + +namespace fcitx { + +class MacroEditorPlugin : public FcitxQtConfigUIPlugin { + Q_OBJECT +public: + Q_PLUGIN_METADATA(IID FcitxQtConfigUIFactoryInterface_iid FILE + "macro-editor.json") + explicit MacroEditorPlugin(QObject *parent = 0); + FcitxQtConfigUIWidget *create(const QString &key) override; +}; + +} // namespace fcitx + +#endif // _MACRO_EDITOR_MAIN_H_ diff --git a/macro-editor/model.cpp b/macro-editor/model.cpp new file mode 100644 index 00000000..e6f664ec --- /dev/null +++ b/macro-editor/model.cpp @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#include + +#include "editor.h" +#include "model.h" +#include "vnconv.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx::unikey { + +using ItemType = std::pair; + +MacroModel::MacroModel(QObject *parent) + : QAbstractTableModel(parent), needSave_(false) {} + +MacroModel::~MacroModel() {} + +QVariant MacroModel::headerData(int section, Qt::Orientation orientation, + int role) const { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + if (section == 0) { + return _("Macro"); + } + if (section == 1) { + return _("Word"); + } + } + return {}; +} + +int MacroModel::rowCount(const QModelIndex & /*parent*/) const { + return list_.count(); +} + +int MacroModel::columnCount(const QModelIndex & /*parent*/) const { return 2; } + +QVariant MacroModel::data(const QModelIndex &index, int role) const { + do { + if (role == Qt::DisplayRole && index.row() < list_.count()) { + if (index.column() == 0) { + return list_[index.row()].first; + } + if (index.column() == 1) { + return list_[index.row()].second; + } + } + } while (0); + return QVariant(); +} + +void MacroModel::addItem(const QString ¯o, const QString &word) { + if (keyset_.contains(macro)) { + return; + } + beginInsertRows(QModelIndex(), list_.size(), list_.size()); + list_.append(std::pair(macro, word)); + keyset_.insert(macro); + endInsertRows(); + setNeedSave(true); +} + +void MacroModel::deleteItem(int row) { + if (row >= list_.count()) { + return; + } + std::pair item = list_.at(row); + QString key = item.first; + beginRemoveRows(QModelIndex(), row, row); + list_.removeAt(row); + keyset_.remove(key); + endRemoveRows(); + setNeedSave(true); +} + +void MacroModel::deleteAllItem() { + if (list_.count()) { + setNeedSave(true); + } + beginResetModel(); + list_.clear(); + keyset_.clear(); + endResetModel(); +} + +void MacroModel::setNeedSave(bool needSave) { + if (needSave_ != needSave) { + needSave_ = needSave; + Q_EMIT needSaveChanged(needSave_); + } +} + +bool MacroModel::needSave() const { return needSave_; } + +void MacroModel::load(CMacroTable *table) { + beginResetModel(); + list_.clear(); + keyset_.clear(); + for (int i = 0; i < table->getCount(); i++) { + QString key = MacroEditor::getData(table, i, true); + QString value = MacroEditor::getData(table, i, false); + list_.append(std::pair(key, value)); + keyset_.insert(key); + } + endResetModel(); +} + +void MacroModel::save(CMacroTable *table) { + table->resetContent(); + for (const ItemType &item : list_) { + table->addItem(item.first.toUtf8().data(), item.second.toUtf8().data(), + CONV_CHARSET_XUTF8); + } + setNeedSave(false); +} + +} // namespace fcitx::unikey diff --git a/macro-editor/model.h b/macro-editor/model.h new file mode 100644 index 00000000..04ffda06 --- /dev/null +++ b/macro-editor/model.h @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2012-2018 CSSlayer + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef _MACRO_EDITOR_MODEL_H_ +#define _MACRO_EDITOR_MODEL_H_ + +#include "mactab.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx::unikey { +class MacroModel : public QAbstractTableModel { + Q_OBJECT +public: + explicit MacroModel(QObject *parent = 0); + virtual ~MacroModel(); + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, + int role = Qt::DisplayRole) const override; + void load(CMacroTable *table); + void addItem(const QString ¯o, const QString &word); + void deleteItem(int row); + void deleteAllItem(); + void save(CMacroTable *table); + bool needSave() const; + +Q_SIGNALS: + void needSaveChanged(bool); + +private: + void setNeedSave(bool needSave); + bool needSave_; + QSet keyset_; + QList> list_; +}; + +} // namespace fcitx::unikey + +#endif // _MACRO_EDITOR_MODEL_H_ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a8f520d..da0d1f81 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,7 @@ if (LOTUS_ENABLE_LOG) target_compile_definitions(lotus PRIVATE LOTUS_ENABLE_LOG=1) endif() target_compile_definitions(lotus PRIVATE LOTUS_ENGINE_UNIKEY=1) -target_include_directories(lotus PRIVATE "${PROJECT_SOURCE_DIR}/unikey/core") +target_include_directories(lotus PRIVATE "${PROJECT_SOURCE_DIR}/unikey") target_include_directories(lotus PRIVATE ${PROJECT_BINARY_DIR} diff --git a/src/lotus-config.h b/src/lotus-config.h index f91cc691..d654cb54 100644 --- a/src/lotus-config.h +++ b/src/lotus-config.h @@ -233,7 +233,8 @@ namespace fcitx { Option enableCustomKeymap{this, "EnableCustomKeymap", _("Enable Custom Keymap"), false}; OptionWithAnnotation timeFormat{this, "TimeFormat", _("Time Format ($TIME in macro)"), "%H:%M", {}, {}, TimeFormatAnnotation()}; OptionWithAnnotation dateFormat{this, "DateFormat", _("Date Format ($DATE in macro)"), "%d/%m/%Y", {}, {}, DateFormatAnnotation()}; - SubConfigOption macroEditor{this, "MacroEditor", _("Macro"), "fcitx://config/addon/lotus/lotus-macro"}; + ExternalOption macroEditor{this, "MacroEditor", _("Macro Editor"), "fcitx://config/addon/lotus/macro"}; + ExternalOption keymapEditor{this, "KeymapEditor", _("Keymap Editor"), "fcitx://config/addon/lotus/keymap.txt"}; SubConfigOption customKeymap{this, "CustomKeymap", _("Custom Keymap"), "fcitx://config/addon/lotus/custom_keymap"}; SubConfigOption appRules{this, "AppRules", _("App Rules"), "fcitx://config/addon/lotus/app_rules"}; KeyListOption modeMenuKey{ this, "ModeMenuKey", _("Mode Menu Hotkey"), {Key("grave")}, KeyListConstrain({KeyConstrainFlag::AllowModifierLess, KeyConstrainFlag::AllowModifierOnly})};); diff --git a/src/lotus-engine.cpp b/src/lotus-engine.cpp index 4cee0a21..3ba41670 100644 --- a/src/lotus-engine.cpp +++ b/src/lotus-engine.cpp @@ -60,7 +60,7 @@ namespace fcitx { const char* desktop = std::getenv("XDG_CURRENT_DESKTOP"); isGnome_ = (desktop != nullptr) && std::string(desktop).find("GNOME") != std::string::npos; // emptyCustomKeymap_.customKeymap is implicitly initialized to empty by fcitx::Option default value macro. - imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "VIQR", "Microsoft"}; + imNames_ = {"Telex", "VNI", "Telex 2", "Telex + VNI", "VIQR", "Microsoft", "UserIM"}; config_.inputMethod.annotation().setList(imNames_); auto& uiManager = instance_->userInterfaceManager(); initToggleAction(spellCheckAction_, config_.spellCheck, "lotus-spellcheck", "tools-check-spelling", _("Enable Spell Check"), _("Spell Check"), uiManager); @@ -154,6 +154,8 @@ namespace fcitx { customKeymap_.load(config, true); safeSaveAsIni(customKeymap_, CustomKeymapFile); refreshEngine(); + } else if (path == "macro" || path == "keymap.txt") { + refreshOption(); } else if (path == "app_rules") { appRulesTables_.load(config, true); { diff --git a/src/lotus-input-backend.cpp b/src/lotus-input-backend.cpp index ed95cb23..9cbbb79e 100644 --- a/src/lotus-input-backend.cpp +++ b/src/lotus-input-backend.cpp @@ -11,10 +11,12 @@ #include "lotus-config.h" #include "lotus-engine.h" #include "unikeyinputcontext.h" +#include "usrkeymap.h" #include #include #include #include +#include #include #include namespace fcitx { @@ -34,6 +36,8 @@ namespace fcitx { return UkViqr; if (name.find("Microsoft") != std::string::npos || name.find("Ms") != std::string::npos) return UkMsVi; + if (name.find("User") != std::string::npos || name.find("Custom") != std::string::npos) + return UkUsrIM; if (name.find("Telex") != std::string::npos) return UkSimpleTelex; if (name.find("Telex + VNI") != std::string::npos) @@ -101,6 +105,8 @@ namespace fcitx { private: void applyFromConfig(LotusEngine* engine) { if (!im_) return; + reloadKeymap(); + reloadMacroTable(); UkInputMethod currentIM_ = mapLotusIm(engine->config().inputMethod.value()); im_->setInputMethod(currentIM_); im_->setOutputCharset(CONV_CHARSET_XUTF8); @@ -116,6 +122,19 @@ namespace fcitx { opt.autoNonVnRestore = *engine->config().autoNonVnRestore ? 1 : 0; im_->setOptions(&opt); } + void reloadKeymap() { + auto keymapFile = StandardPaths::global().open(StandardPathsType::PkgConfig, "lotus/keymap.txt"); + if (keymapFile.isValid()) { + UkLoadKeyMap(keymapFile.fd(), im_->sharedMem()->usrKeyMap); + im_->sharedMem()->usrKeyMapLoaded = true; + } else { + im_->sharedMem()->usrKeyMapLoaded = false; + } + } + void reloadMacroTable() { + auto path = StandardPaths::global().locate(StandardPathsType::PkgConfig, "lotus/macro"); + if (!path.empty()) im_->loadMacroTable(path.string().c_str()); + } void eraseChars(int num_chars) { int i; int k = num_chars;