From 9f280af39a5fce9ca0bf1a8e26f7af7baf2fefce Mon Sep 17 00:00:00 2001 From: SimplestStudio Date: Fri, 15 Nov 2024 11:42:30 +0200 Subject: [PATCH] [linux] detect keyboard layout --- ChromiumBasedEditors/lib/ascdocumentscore.pri | 12 + .../lib/src/applicationmanager.cpp | 12 +- .../lib/src/keyboardlayout.cpp | 208 ++++++++++++++++++ ChromiumBasedEditors/lib/src/keyboardlayout.h | 21 ++ 4 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 ChromiumBasedEditors/lib/src/keyboardlayout.cpp create mode 100644 ChromiumBasedEditors/lib/src/keyboardlayout.h diff --git a/ChromiumBasedEditors/lib/ascdocumentscore.pri b/ChromiumBasedEditors/lib/ascdocumentscore.pri index 748b5b72..28b80b71 100644 --- a/ChromiumBasedEditors/lib/ascdocumentscore.pri +++ b/ChromiumBasedEditors/lib/ascdocumentscore.pri @@ -163,6 +163,18 @@ core_linux { QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN\'" QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN/converter\'" QMAKE_LFLAGS += -Wl,--disable-new-dtags + + ICU_PATH = $$CORE_ROOT_DIR/Common/3dParty/icu/icu/cross_build_install + INCLUDEPATH += $$ICU_PATH/include + + HEADERS += \ + $$PWD/src/keyboardlayout.h + + SOURCES += \ + $$PWD/src/keyboardlayout.cpp + + LIBS += -lX11 -lX11-xcb -lxkbcommon-x11 -lxkbcommon + LIBS += -L"$$ICU_PATH/lib/" -licuuc } ADD_DEPENDENCY(graphics, kernel, UnicodeConverter, kernel_network, PdfFile, XpsFile, DjVuFile, HtmlRenderer, hunspell, ooxmlsignature) diff --git a/ChromiumBasedEditors/lib/src/applicationmanager.cpp b/ChromiumBasedEditors/lib/src/applicationmanager.cpp index 47c6824f..e5f18938 100644 --- a/ChromiumBasedEditors/lib/src/applicationmanager.cpp +++ b/ChromiumBasedEditors/lib/src/applicationmanager.cpp @@ -36,6 +36,8 @@ #include "./cefwrapper/monitor_info.h" #ifdef LINUX +# include "keyboardlayout.h" + CApplicationCEF* CLinuxData::app_cef = NULL; CAscApplicationManager* CLinuxData::app_manager = NULL; @@ -908,20 +910,26 @@ void CAscApplicationManager::EndSaveDialog(const std::wstring& sPath, unsigned i bool CAscApplicationManager::IsPlatformKeyboardSupport() { -#ifdef WIN32 +#if defined(WIN32) return true; +#elif defined(LINUX) + KeyboardLayout kl; + return kl.IsKeyboardSupport(); #endif return false; } int CAscApplicationManager::GetPlatformKeyboardLayout() { -#ifdef WIN32 +#if defined(WIN32) HWND wFocus = GetFocus(); DWORD dwThread = GetWindowThreadProcessId(wFocus, 0); HKL hkl = GetKeyboardLayout(dwThread); int nLang = LOWORD(hkl); return nLang; +#elif defined(LINUX) + KeyboardLayout kl; + return kl.GetKeyboardLayout(); #endif return -1; } diff --git a/ChromiumBasedEditors/lib/src/keyboardlayout.cpp b/ChromiumBasedEditors/lib/src/keyboardlayout.cpp new file mode 100644 index 00000000..72fc3af1 --- /dev/null +++ b/ChromiumBasedEditors/lib/src/keyboardlayout.cpp @@ -0,0 +1,208 @@ +#include "keyboardlayout.h" +//#include +#include +#include +#include +#include +#include +#include +#include "unicode/locid.h" + +#define LANGIDFROMLCID(lcid) ((uint16_t)(lcid)) + + +static std::string trim(const std::string &str) +{ + size_t first = str.find_first_not_of(' '); + if (first != std::string::npos) { + size_t last = str.find_last_not_of(' '); + return str.substr(first, last - first + 1); + } + return ""; +} + +static uint16_t layoutNameToLangId(const std::string &layoutName) +{ + if (!layoutName.empty()) { + std::string languageDisplayName, language, script, region; + size_t bkt1 = layoutName.find_first_of('('); + languageDisplayName = (bkt1 != std::string::npos) ? trim(layoutName.substr(0, bkt1)) : layoutName; + std::list parts; + size_t bkt2 = layoutName.find_last_of(')'); + if (bkt2 != std::string::npos && bkt1 < bkt2) { + std::stringstream strs(layoutName.substr(bkt1 + 1, bkt2 - bkt1 - 1)); + std::string part; + while (std::getline(strs, part, ',')) + parts.push_back(trim(part)); + } + + if (parts.size() > 0) { + std::string part1(parts.front()); + if (part1.length() == 2 && std::isupper(part1[0]) && std::isupper(part1[1])) { + region = std::move(part1); + parts.pop_front(); + } + } + + int32_t count; + icu::UnicodeString uname; + const icu::Locale *availableLocales = icu::Locale::getAvailableLocales(count); + for (int32_t i = 0; i < count; ++i) { + std::string name; + if (language.empty()) { + availableLocales[i].getDisplayLanguage(uname); + uname.toUTF8String(name); + if (name.find(languageDisplayName) != std::string::npos) + language = availableLocales[i].getLanguage(); + name.clear(); + } + + if (script.empty()) { + availableLocales[i].getDisplayScript(uname); + uname.toUTF8String(name); + if (!name.empty()) { + for (auto it = parts.cbegin(); it != parts.cend(); it++) { + if (name.find(*it) != std::string::npos) { + script = availableLocales[i].getScript(); + parts.erase(it); + break; + } + } + } + name.clear(); + } + + if (region.empty()) { + availableLocales[i].getDisplayCountry(uname); + uname.toUTF8String(name); + if (!name.empty()) { + for (auto it = parts.cbegin(); it != parts.cend(); it++) { + if (name.find(*it) != std::string::npos) { + region = availableLocales[i].getCountry(); + parts.erase(it); + break; + } + } + } + } + + if (!language.empty() && !region.empty() && !script.empty()) + break; + } + + if (!language.empty()) { + if (!script.empty()) + language.append("_" + script); + else + if (region.empty()) { + // try prevent neutral language + UErrorCode status = U_ZERO_ERROR; + char fullName[ULOC_FULLNAME_CAPACITY]; + uloc_addLikelySubtags(language.c_str(), fullName, ULOC_FULLNAME_CAPACITY, &status); + if (U_SUCCESS(status)) { + icu::Locale loc(fullName); + region = loc.getCountry(); + } + } + + if (!region.empty()) + language.append("_" + region); + + // fprintf(stderr, "Canonical name: %s\n", language.c_str()); + icu::Locale loc(language.c_str()); + return LANGIDFROMLCID(loc.getLCID()); + } + } + return 0; +} + +class KeyboardLayoutPrivate +{ +public: + KeyboardLayoutPrivate(); + ~KeyboardLayoutPrivate(); + + xcb_connection_t *connection; + xkb_context *context; + int32_t device_id; + +private: + Display *display; +}; + +KeyboardLayoutPrivate::KeyboardLayoutPrivate() : + connection(nullptr), + context(nullptr), + device_id(-1), + display(nullptr) +{ + if ((display = XOpenDisplay(NULL)) != nullptr) { + if ((context = xkb_context_new(XKB_CONTEXT_NO_FLAGS)) != nullptr) { + connection = XGetXCBConnection(display); + if ((device_id = xkb_x11_get_core_keyboard_device_id(connection)) == -1) { + // fprintf(stderr, "Cannot get device id\n"); + xkb_context_unref(context); context = nullptr; + XCloseDisplay(display); display = nullptr; + } + } else { + // fprintf(stderr, "Cannot create context xkb\n"); + XCloseDisplay(display); display = nullptr; + } + } else { + // fprintf(stderr, "Cannot open display\n"); + } +} + +KeyboardLayoutPrivate::~KeyboardLayoutPrivate() +{ + if (context) + xkb_context_unref(context); + if (display) + XCloseDisplay(display); +} + + +KeyboardLayout::KeyboardLayout() : + pimpl(new KeyboardLayoutPrivate) +{ + +} + +KeyboardLayout::~KeyboardLayout() +{ + delete pimpl, pimpl = nullptr; +} + +bool KeyboardLayout::IsKeyboardSupport() const +{ + return pimpl->device_id != -1; +} + +uint16_t KeyboardLayout::GetKeyboardLayout() const +{ + uint16_t layout = 0; + if (pimpl->device_id != -1) { + if (xkb_keymap *keymap = xkb_x11_keymap_new_from_device(pimpl->context, pimpl->connection, pimpl->device_id, XKB_KEYMAP_COMPILE_NO_FLAGS)) { + if (xkb_state *state = xkb_x11_state_new_from_device(keymap, pimpl->connection, pimpl->device_id)) { + xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap); + + for (xkb_layout_index_t i = 0; i < num_layouts; ++i) { + if (xkb_state_layout_index_is_active(state, i, XKB_STATE_LAYOUT_EFFECTIVE) == 1) { + if (const char *layout_name = xkb_keymap_layout_get_name(keymap, i)) { + // fprintf(stderr, "\nCurrent keyboard layout: %s\n", layout_name); + layout = layoutNameToLangId(layout_name); + } + break; + } + } + xkb_state_unref(state); + } else { + // fprintf(stderr, "Cannot create keyboard state\n"); + } + xkb_keymap_unref(keymap); + } else { + // fprintf(stderr, "Cannot get keymap\n"); + } + } + return layout; +} diff --git a/ChromiumBasedEditors/lib/src/keyboardlayout.h b/ChromiumBasedEditors/lib/src/keyboardlayout.h new file mode 100644 index 00000000..2499560e --- /dev/null +++ b/ChromiumBasedEditors/lib/src/keyboardlayout.h @@ -0,0 +1,21 @@ +#ifndef KEYBOARDLAYOUT_H +#define KEYBOARDLAYOUT_H + +#include + + +class KeyboardLayoutPrivate; +class KeyboardLayout +{ +public: + KeyboardLayout(); + ~KeyboardLayout(); + + bool IsKeyboardSupport() const; + uint16_t GetKeyboardLayout() const; + +private: + KeyboardLayoutPrivate *pimpl; +}; + +#endif // KEYBOARDLAYOUT_H