/
Dengit-man
/
CustomLocaleKeyShortcut
Обзор
Документация
Войти
/
Dengit-man
/
CustomLocaleKeyShortcut
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
7
CI/CD
Аналитика
Безопасность
AddMacOS
Classes/C_MainContext.cpp
542 строки
20 KB
Dengit-man
Фикс мелочей - 1.5.1
27 май 2026, 14:23
Верифицирован
27 май 2026, 14:23
28ae8d3
Код
Авторство
О чём код?
// // Created by Dengit-man on 08.02.2026. // Правообладатель (c) 2026 Пользователь GitVerse "Dengit-man" // #include <algorithm> #include <iostream> #include <utility> #include <vector> #include "C_MainContext.h" #include "C_Exceptions.h" #include "G_Globals.h" #include "S_Utilities.h" // #include <imm.h> // For ImmGetContext, ImmGetDescription, etc. //#pragma comment(lib, "imm32.lib") // Link with IMM32 std::atomic_bool C_MainContext::initialized = ATOMIC_VAR_INIT(0); std::atomic_bool C_MainContext::initializing = ATOMIC_VAR_INIT(0); std::atomic< std::shared_ptr<C_MainContext> > C_MainContext::_instance{nullptr}; std::mutex C_MainContext::init_mutex; C_MainContext::C_MainContext(std::string a_iniPath) : f_iniFilePath(std::move(a_iniPath)), f_Reader(f_iniFilePath.string()), f_logDirPath(std::filesystem::current_path()/ "Log"), f_logFileName("File_{DATE}.log"), f_sendMessageMode(__defaults__::sendMessageMode), f_noInput(false), g_doOnSignal(nullptr) { static bool initialized = false; if (!initialized) // проверка на случай, ошибки при синхронизации ассинхронных вызовов instance() { this->Reload(false); // INI_File открывается при инициализации f_Reader, повторно открывать не надо. initialized = true; // if (f_Reader.ParseError() < 0) // { // LogWarn(std::format("Can't load '{0}'.", f_iniFilePath), true); // this->f_layoutSwitchShortcut = "Shift+CapsLock"; // this->f_currentSysLayout.clear(); // } // else // { // std::cout << "Config loaded from '" << this->f_iniFilePath << "': version=" // << f_Reader.GetInteger("protocol", "version", -1) << ",\n name=" // << f_Reader.Get("user", "name", "UNKNOWN") << ",\n email=" // << f_Reader.Get("user", "email", "UNKNOWN") << ",\n pi=" // << f_Reader.GetReal("user", "pi", -1) << ",\n active=" // << f_Reader.GetBoolean("user", "active", true) << "\n"; // this->Reload(); // } } else { throw EObjectIsAlreadyInitialized("[-] Instance already initialized. You can't construct new object. " \ "If you need to add tags. You should use \"addTag(QString tag)\"."); // static_assert(false, "Can't add tags. You should use \"obj->addTag(QString tag)\"."); } } std::shared_ptr<C_MainContext> C_MainContext::instance() { LogDebug("C_MainContext::instance() called", false); auto ptr = C_MainContext::_instance.load(std::memory_order_acquire); if (ptr != nullptr) { return ptr; } std::lock_guard const lock(C_MainContext::init_mutex); LogDebug("Double-Checked Locking: C_MainContext::init_mutex become locked.", false); ptr = C_MainContext::_instance.load(std::memory_order_acquire); if (ptr == nullptr) { try { auto fresh = std::shared_ptr<C_MainContext>(new C_MainContext(__defaults__::configFileName)); LogDebug("C_MainContext instance created", false); C_MainContext::_instance.store(fresh, std::memory_order_release); LogDebug("C_MainContext instance stored successfully.", false); } catch (...) { LogCritical("[!] There was an error creating shared_ptr with new C_MainContext.", false); throw; } } // else // { // LogWarn("C_MainContext instance already initialized."); // } ptr = C_MainContext::_instance.load(std::memory_order_acquire); return ptr; } /// :reopenINI_File: default: true; флаг определеяющий переоткрытие INI файла и, соотв., пересоздание INIReader. void C_MainContext::Reload(bool reopenINI_File) { this->LoadFromIni(reopenINI_File); this->RefreshLayoutsFromOS(); this->ParseShortcut(this->GetLayoutSwitchShortcut_string()); } template<typename... T> requires(_METHOD_IS_FORBIDDEN__::__disabled__It_cannot_be_used_because_of_static_reference_in_LowLevelKeyboardProc) bool C_MainContext::freeInstance() //disabled { LogCritical("[!] Disabled", false); throw EInvalidObjectState("Disabled method"); LogDebug("C_MainContext::freeInstance() called", false); std::lock_guard const lock(C_MainContext::init_mutex); auto last_chance = C_MainContext::_instance.exchange(nullptr, std::memory_order_acq_rel); if (last_chance != nullptr) { last_chance.reset(); LogDebug("C_MainContext instance released", false); return true; } else { LogDebug("C_MainContext instance already nullptr", false); return false; } } void C_MainContext::RefreshLayoutsFromOS() { LogDebug("C_MainContext::RefreshLayoutsFromOS() called", false); this->f_availableLayouts.clear(); // Шаг 1: Получаем массив всех доступных раскладок const int bufferSize = 256; HKL hklList[bufferSize]; UINT count = GetKeyboardLayoutList(bufferSize, hklList); if (count == 0) { LogCritical("[!] GetKeyboardLayoutList returned 0 layouts. System error.", false); return; } LogInfo(std::format("[+] Found {} keyboard layouts in OS.", count), false); for (UINT i = 0; i < count; ++i) { HKL hkl = hklList[i]; DWORD hklDword = reinterpret_cast<DWORD_PTR>(hkl); std::wstring w_hklStr = std::format(L"{:08X}", hklDword); // например, "00000409" std::string hklStr = std::format("{:08X}", hklDword); // например, "00000409" // Извлекаем имя раскладки (язык + страна) char layoutName[256] = {0}; GetLocaleInfoA( LOWORD(hklDword), // локаль (LCID) LOCALE_SISO639LANGNAME, // например, "en" layoutName, sizeof(layoutName) ); std::string lang = layoutName; std::ranges::transform(lang, lang.begin(), ::toupper); LogInfo(std::format("Layout [ID: {}] -> '{}'", hklStr, lang), false); this->f_availableLayouts[w_hklStr] = std::make_pair(hkl, lang); // сохраняем: "00000409" -> "EN" // Является ли эта раскладка текущей if (hkl == GetKeyboardLayout(0)) // 0 — текущий поток/процесс { this->f_currentSysLayout = std::make_pair(hkl, lang); } } // Если не удалось определить текущую — попробуем запасной путь if (this->f_currentSysLayout.first == nullptr || this->f_currentSysLayout.second.empty()) { HKL currentHkl = GetKeyboardLayout(0); // DWORD currentHklDword = reinterpret_cast<DWORD_PTR>(currentHkl); const std::wstring currentHklStr = std::format(L"{:08X}", reinterpret_cast<DWORD_PTR>(currentHkl)); auto it = this->f_availableLayouts.find(currentHklStr); if (it != this->f_availableLayouts.end()) { this->f_currentSysLayout = it->second; } else { this->f_currentSysLayout = std::make_pair(currentHkl, std::format("{:08X}", reinterpret_cast<DWORD_PTR>(currentHkl))); // fallback: просто хеш } } LogInfo(std::format("Current system layout: {}", this->f_currentSysLayout.second), false); } void C_MainContext::LoadFromIni(bool reopenINI_File) { LogDebug("C_MainContext::LoadFromIni() called", false); this->f_iniDict.clear(); if (reopenINI_File) { LogDebug("C_MainContext::LoadFromIni() INI file is reopening", false); this->f_Reader = INIReader(this->f_iniFilePath.string()); } if (this->f_Reader.ParseError() < 0) { LogWarn(std::format("[-] Can't load '{0}'.", f_iniFilePath.string()), false); this->f_layoutSwitchShortcut = __defaults__::shortcut; this->f_currentSysLayout = std::make_pair(HKL(), ""); } else { LogDebug("C_MainContext::LoadFromIni() successfully loaded. Reading all data.", false); for (const auto& section : this->f_Reader.Sections()) { LogDebug(std::format("INI Section: [{}]", section), false); for (const auto& key : this->f_Reader.Keys(section)) { auto value = this->f_Reader.Get(section, key, {}); LogDebug(std::format("\"{}\": \"{}\"",key, value), false); this->f_iniDict[section][key] = value; // for (const auto& value: this->f_Reader.Get(section, key, {})) // { // LogDebug(std::format("\"{}\": \"{}\"",key, value), false); // this->f_iniDict[section][key] = value; // } } } this->ParseKnownFields(); } } void C_MainContext::ParseKnownFields() { LogDebug("C_MainContext::ParseKnownFields() called", false); bool f_layoutSwitchShortcut_check = false; bool f_sendMessageMode_check = false; bool f_logDirPath_check = false; bool f_logFileName_check = false; for (const auto& [section, items] : this->f_iniDict) { if (section == "main" || section == "Main" || section == "MAIN") { for (const auto& item : items) { if (item.first == "shortcut" || item.first == "ShortCut" || item.first == "Shortcut" || item.first == "SHORTCUT") { std::string shrtct = item.second; if (!S_Utilities::less_or_one_char_in_string(shrtct, '+')) { shrtct = S_Utilities::take_before_second(shrtct, '+'); LogWarn(std::format("[-] Only two keys available. Got only: {}", shrtct), false); std::this_thread::sleep_for(std::chrono::milliseconds(500)); LogWarn(std::format("Second Warning! Only two keys available. Got only: {}", shrtct), false); std::this_thread::sleep_for(std::chrono::milliseconds(1500)); LogWarn(std::format("Third Warning! Only two keys available. Got only: {}", shrtct), false); std::this_thread::sleep_for(std::chrono::seconds(2)); } this->f_layoutSwitchShortcut = shrtct; f_layoutSwitchShortcut_check = true; } else if (item.first == "switch_layout_send_mode") { const auto for_error_msg = std::format("[-] Wrong value for 'switch_layout_send_mode'. '{}'", item.second); this->f_sendMessageMode = (item.second == "Broadcast" ? SendMessageMode::Broadcast : ( item.second == "Foreground" || item.second == "ForegroundOnly" ? SendMessageMode::ForegroundOnly : ( LogCritical(for_error_msg, false), throw EInvalidString(for_error_msg), static_cast< SendMessageMode >(-1)) ) ); f_sendMessageMode_check = true; } } } else if (section == "log" || section == "Log" || section == "LOG") { for (const auto& item : items) { if (item.first == "Dir" || item.first == "dir") { this->f_logDirPath = item.second; f_logDirPath_check = true; } else if (item.first == "Filename" || item.first == "filename") { this->f_logFileName = item.second; f_logFileName_check = true; } } } } if (!f_layoutSwitchShortcut_check) { this->f_layoutSwitchShortcut = __defaults__::shortcut; } if (!f_sendMessageMode_check) { this->f_sendMessageMode = __defaults__::sendMessageMode; } if (!f_logDirPath_check) { this->f_logDirPath = __defaults__::logDirPath; } if (!f_logFileName_check) { this->f_logFileName = __defaults__::logFileName; } LogDebug(std::format("Selected Layout Switch \"shortcut\" set to {}", this->f_layoutSwitchShortcut), false); LogDebug(std::format("Selected \"switch_layout_send_mode\" set to {}", S_Utilities::SendMessageMode_Str(this->f_sendMessageMode)), false); C_Logger::instance()->setLogFileName((this->GetLogDirPath() / this->GetLogFileName()).string()); } void C_MainContext::UpdateIniDictFromKnownFields() { LogDebug("C_MainContext::UpdateIniDictFromKnownFields() called", false); this->f_iniDict["Main"]["shortcut"] = this->f_layoutSwitchShortcut; this->f_iniDict["Log"]["Dir"] = this->f_logDirPath.string(); this->f_iniDict["Log"]["Filename"] = this->f_logFileName.string(); } C_MainContext::HotKey C_MainContext::getHotKeyFromString(const auto key) { // auto key = S_Utilities::as_string_view(a_key); UINT mod = 0; UINT vk = 0; if (S_Utilities::equals_ignore_case_ascii( key, "shift")) { mod = VK_SHIFT; } else if (S_Utilities::equals_ignore_case_ascii(key, "ctrl")) { mod = VK_CONTROL; } else if (S_Utilities::equals_ignore_case_ascii(key, "alt")) { vk = VK_MENU; } else if (S_Utilities::equals_ignore_case_ascii(key, "capslock")) { vk = VK_CAPITAL; } else if (S_Utilities::equals_ignore_case_ascii(key, "tab")) { vk = VK_TAB; } else if (S_Utilities::equals_ignore_case_ascii(key, "return") || S_Utilities::equals_ignore_case_ascii( key, "enter")) { vk = VK_RETURN; } else { LogWarn(std::format("[-] C_MainContext::HotKey getHotKeyFromString() unknown or unusual key string: \"{}\"", key), false); vk = key[0]; } return {.modifiers=mod, .vk=vk}; } void C_MainContext::ParseShortcut(const std::string &a_shortcut) { LogDebug("C_MainContext::ParseShortcut() called", false); const std::string shortcut = a_shortcut.empty() ? this->GetLayoutSwitchShortcut_string() : a_shortcut; // std::vector<HotKey> keys; const auto l = shortcut.length() + 1; std::unique_ptr<char[]> word{new char[l]}; for (std::size_t i = 0; i < l; i++) { word.get()[i] = '\0'; } unsigned i = 0; unsigned count = 0; std::array<std::string, 2> words; std::array<HotKey, 2> keys; for (std::size_t j = 0; j < l; j++) { const auto& key = shortcut[j]; // ReSharper disable once CppDFAConstantConditions if (key != '+') { word[i++] = key; } // ReSharper disable once CppDFAConstantConditions if (key == '+' || j == l-1) { if (count < 3 -1) { words[count] = word.get(); keys[count] = this->getHotKeyFromString(word.get()); // keys.push_back(this->getHotKeyFromString(std::move(word))); count++; } else { LogWarn(std::format("[-] Only two keys available. Got only: {}+{}", words[0], words[1]), false); std::this_thread::sleep_for(std::chrono::milliseconds(500)); LogWarn(std::format(" Second Warning! Only two keys available. Got only: {}+{}", words[0], words[1]), false); std::this_thread::sleep_for(std::chrono::milliseconds(1500)); LogWarn(std::format("Third Warning! Only two keys available. Got only: {}+{}", words[0], words[1]), false); std::this_thread::sleep_for(std::chrono::seconds(2)); break; } for (std::size_t i = 0; i < l; i++) { word.get()[i] = '\0'; } i = 0; } } // keys[0].vk = (keys[0].vk == 0 || keys[0].vk == -1) ? ( (keys[1].vk != 0 && keys[1].vk != -1) ? keys[1].vk : 0 ) : (keys[0].vk); keys[0].modifiers = (keys[0].modifiers == 0 || keys[0].modifiers == -1) ? ( (keys[1].modifiers != 0 && keys[1].modifiers != -1) ? keys[1].modifiers : 0 ) : (keys[0].modifiers); if (keys[0].modifiers == 0 && keys[1].vk == 0) { LogCritical("[!] Shortcut is invalid. Both (key and modifier) are zero.", false); throw EInvalidShortcut("Bad shortcut. Both (key and modifier) are zero."); } LogDebug(std::format("Key codes: {} + {}", keys[0].vk, keys[0].modifiers), false); this->f_key = keys[0]; // this->f_hotkeys = std::move(keys); // return keys; } const std::filesystem::path & C_MainContext::GetIniPath() const noexcept { return this->f_iniFilePath; } const std::string & C_MainContext::GetLayoutSwitchShortcut_string() const noexcept { return this->f_layoutSwitchShortcut; } const std::vector<C_MainContext::HotKey> C_MainContext::GetLayoutSwitchShortcut() const noexcept { return this->f_hotkeys; } // const C_MainContext::HotKey C_MainContext::GetLayoutSwitchKey() const noexcept // { // return this->f_key; // } const auto C_MainContext::GetCurrentSysLayout() const noexcept { return this->f_currentSysLayout; } const std::filesystem::path & C_MainContext::GetLogDirPath() const noexcept { return this->f_logDirPath; } const std::filesystem::path & C_MainContext::GetLogFileName() const noexcept { return this->f_logFileName; } const std::unordered_map<std::wstring, std::pair<HKL, std::string>> C_MainContext::GetAvailableLayouts() const noexcept { return this->f_availableLayouts; } SendMessageMode C_MainContext::GetSendMessageMode() const noexcept { return this->f_sendMessageMode; } std::optional<std::string> C_MainContext::GetValue(const std::string §ion, const std::string &key) const noexcept { LogDebug("C_MainContext::GetValue(const std::string §ion, const std::string &key) called", false); auto result = std::nullopt; LogCritical("[!] C_MainContext::GetValue is not implemented yet", false); throw ENotImplemented("C_MainContext::GetValue is not implemented yet"); return result; } const bool C_MainContext::IsInitialized() const noexcept { auto ptr = C_MainContext::_instance.load(std::memory_order_acquire); if (ptr != nullptr && C_MainContext::initialized) { return true; } else if (ptr == nullptr && C_MainContext::initialized) { LogCritical("[!] Wrong state of object: _instance pointer (is null) " "and initialized flag (is true). " "Correct: \"_instance pointer\" is not null and \"initialized\" is true", false); throw EInvalidObjectState("[!] Wrong state of object: _instance pointer (is null) " "and initialized flag (is true). " "Correct: \"_instance pointer\" is not null and \"initialized\" is true"); } else if (ptr != nullptr && !C_MainContext::initialized) { LogCritical("[!] Wrong state of object: _instance pointer (is not null) " "and initialized flag (is false). " "Correct: \"_instance pointer\" is not null and \"initialized\" is true.", false); throw EInvalidObjectState("Wrong state of object: _instance pointer (is not null) " "and initialized flag (is false). " "Correct: \"_instance pointer\" is not null and \"initialized\" is true."); } else { return false; } } bool C_MainContext::isInputEnabled() const noexcept { return !this->f_noInput; } void C_MainContext::SetLayoutSwitchShortcut(const std::string &a_layoutSwitchShortcut) { this->f_layoutSwitchShortcut = a_layoutSwitchShortcut; } void C_MainContext::SetValue(const std::string §ion, const std::string &key, const std::string &value) noexcept { this->f_iniDict[section][key] = value; } void C_MainContext::DisableLogs() { C_Logger::instance()->SetLogsOn(false); } void C_MainContext::DisableInput() { this->f_noInput = true; } void C_MainContext::SaveToIni() const { LogDebug("C_MainContext::SaveToIni() called", false); throw ENotImplemented("C_MainContext::GetValue is not implemented yet"); // this-> }