diff --git a/Redcraft.FocusIME/Source/Private/Main.cpp b/Redcraft.FocusIME/Source/Private/Main.cpp index 13b189c..8a595a0 100644 --- a/Redcraft.FocusIME/Source/Private/Main.cpp +++ b/Redcraft.FocusIME/Source/Private/Main.cpp @@ -21,9 +21,17 @@ // ReSharper disable CppDeprecatedEntity // ReSharper disable CppClangTidyCertErr33C -const std::string LogFilename = "FocusIME.log"; +using namespace std::chrono_literals; -const std::string CachedModeFilename = "FocusIME.json"; +constexpr bool bEnableCallback = true; +constexpr bool bEnablePolling = true; +constexpr auto TickInterval = 250ms; +constexpr auto IMEMessageDelay = 50ms; + +const std::string LogFilename = "FocusIME.log"; +const std::string IMEModeFilename = "FocusIME.json"; + +static_assert(bEnableCallback || bEnablePolling); std::ofstream GLogStream; @@ -33,8 +41,6 @@ void PrintLog(const std::string& Text); void Tick() { - using namespace std::chrono_literals; - // ReSharper disable CppInconsistentNaming // ReSharper disable CppClangTidyPerformanceEnumSize @@ -52,15 +58,17 @@ void Tick() Chinese, }; - static std::map CachedIMEMode = [] + using FIMEMode = std::map; + + auto LoadIMEMode = []() -> FIMEMode { std::map Result; - std::ifstream File(CachedModeFilename); + std::ifstream File(IMEModeFilename); if (!File.is_open()) { - PrintLog("Cached conversion mode configuration file not found"); + PrintLog("Conversion mode configuration file not found"); return Result; } @@ -137,7 +145,7 @@ void Tick() while (!View.empty() && std::isspace(View.front())) View.remove_prefix(1); - if (View.empty()) return true; + if (View.empty()) break; if (!View.starts_with(',')) return false; @@ -157,25 +165,24 @@ void Tick() if (!bSuccessful) break; - PrintLog("Successfully loaded " + std::to_string(Result.size()) + " cached conversion mode items"); + PrintLog("Successfully loaded " + std::to_string(Result.size()) + " conversion mode items"); return Result; } while (false); - PrintLog("Invalid format detected in cached conversion mode configuration file"); + PrintLog("Invalid format detected in conversion mode configuration file"); return Result; + }; - } (); - - auto SaveCachedIMEMode = [] + auto SaveIMEMode = [](const FIMEMode& IMEMode) { - std::ofstream File(CachedModeFilename); + std::ofstream File(IMEModeFilename); if (!File.is_open()) { - PrintLog("Error: Failed to save cached conversion mode configuration"); + PrintLog("Error: Failed to save conversion mode configuration"); return; } @@ -184,7 +191,7 @@ void Tick() bool bFirstItem = true; - for (const auto& [Name, Mode] : CachedIMEMode) + for (const auto& [Name, Mode] : IMEMode) { if (!bFirstItem) File << ","; @@ -200,6 +207,8 @@ void Tick() File.close(); }; + static FIMEMode IMEMode = LoadIMEMode(); + static HWND LastWindow = nullptr; static std::string CachedProcess; @@ -208,7 +217,7 @@ void Tick() { do { - if (CachedIMEMode.contains(CachedProcess) && CachedIMEMode[CachedProcess] == EIMEConversionMode::Default) break; + if (IMEMode.contains(CachedProcess) && IMEMode[CachedProcess] == EIMEConversionMode::Default) break; HKL KeyboardLayout = GetKeyboardLayout(GetWindowThreadProcessId(LastWindow, nullptr)); @@ -229,15 +238,15 @@ void Tick() if (CurrentMode == EIMEConversionMode::Default) break; - if (!CachedIMEMode.contains(CachedProcess) || CachedIMEMode[CachedProcess] != CurrentMode) + if (!IMEMode.contains(CachedProcess) || IMEMode[CachedProcess] != CurrentMode) { - CachedIMEMode[CachedProcess] = CurrentMode; + IMEMode[CachedProcess] = CurrentMode; - PrintLog("Updated cached conversion mode for process '" + CachedProcess + "' to " + + PrintLog("Updated conversion mode for process '" + CachedProcess + "' to " + (CurrentMode == EIMEConversionMode::English ? "English" : CurrentMode == EIMEConversionMode::Chinese ? "Chinese" : "Default")); - SaveCachedIMEMode(); + SaveIMEMode(IMEMode); } } } @@ -280,9 +289,10 @@ void Tick() CachedProcess = Buffer; - if (!CachedIMEMode.contains(CachedProcess) || CachedIMEMode[CachedProcess] == EIMEConversionMode::Default) break; + if (!IMEMode.contains(CachedProcess) || IMEMode[CachedProcess] == EIMEConversionMode::Default) break; - std::this_thread::sleep_for(50ms); + // Sometimes the message will miss if we don't sleep for a little while. + std::this_thread::sleep_for(IMEMessageDelay); if (const HWND IMEWindow = ImmGetDefaultIMEWnd(LastWindow)) { @@ -291,7 +301,7 @@ void Tick() if (!OpenStatus) break; - LPARAM TargetMode = CachedIMEMode[CachedProcess] == EIMEConversionMode::Chinese ? 0x0401 : 0x0000; + LPARAM TargetMode = IMEMode[CachedProcess] == EIMEConversionMode::Chinese ? 0x0401 : 0x0000; if (ConversionMode != TargetMode) { @@ -369,22 +379,27 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) ShowWindow(MainWindow, SW_HIDE); - const HWINEVENTHOOK FocusEventHook = SetWinEventHook( - EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, - nullptr, - FocusEventHookProc, - 0, 0, - WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); + HWINEVENTHOOK FocusEventHook; - if (FocusEventHook == nullptr) + if constexpr (bEnableCallback) { - MessageBox(nullptr, "Failed to set focus event hook.", "FocusIME", MB_OK | MB_ICONWARNING); + FocusEventHook = SetWinEventHook( + EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, + nullptr, + FocusEventHookProc, + 0, 0, + WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); - DestroyWindow(MainWindow); + if (FocusEventHook == nullptr) + { + MessageBox(nullptr, "Failed to set focus event hook.", "FocusIME", MB_OK | MB_ICONWARNING); - CloseHandle(Mutex); + DestroyWindow(MainWindow); - return 1; + CloseHandle(Mutex); + + return 1; + } } # if BUILD_DEBUG || BUILD_DEVELOPMENT @@ -432,41 +447,63 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) MSG Message = { }; - while (!GShouldExit) + if constexpr (bEnablePolling) { - const BOOL bResult = GetMessage(&Message, nullptr, 0, 0); - - if (bResult > 0) + while (!GShouldExit) { - TranslateMessage(&Message); - DispatchMessage (&Message); - } + if (PeekMessage(&Message, nullptr, 0, 0, PM_REMOVE)) { - else if (bResult == 0) break; + if (Message.message == WM_QUIT) break; - else - { - DWORD ErrorCode = GetLastError(); + TranslateMessage(&Message); + DispatchMessage (&Message); - LPSTR MessageBuffer = nullptr; - - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&MessageBuffer), 0, nullptr); - - std::string ErrorMessage = "Message loop failed with error code " + std::to_string(ErrorCode); - - if (MessageBuffer) - { - ErrorMessage += ": " + std::string(MessageBuffer); - - LocalFree(MessageBuffer); + continue; } - PrintLog(ErrorMessage); + Tick(); - break; + std::this_thread::sleep_for(TickInterval); + } + } + else + { + while (!GShouldExit) + { + const BOOL bResult = GetMessage(&Message, nullptr, 0, 0); + + if (bResult > 0) + { + TranslateMessage(&Message); + DispatchMessage (&Message); + } + + else if (bResult == 0) break; + + else + { + DWORD ErrorCode = GetLastError(); + + LPSTR MessageBuffer = nullptr; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&MessageBuffer), 0, nullptr); + + std::string ErrorMessage = "Message loop failed with error code " + std::to_string(ErrorCode); + + if (MessageBuffer) + { + ErrorMessage += ": " + std::string(MessageBuffer); + + LocalFree(MessageBuffer); + } + + PrintLog(ErrorMessage); + + break; + } } } @@ -480,7 +517,10 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) } # endif - UnhookWinEvent(FocusEventHook); + if constexpr (bEnableCallback) + { + UnhookWinEvent(FocusEventHook); + } DestroyWindow(MainWindow);