feat(master): add polling tick mode

This commit is contained in:
2025-06-22 18:42:55 +08:00
parent fa81c1d418
commit 9a83aea6a2

View File

@ -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<std::string, EIMEConversionMode> CachedIMEMode = []
using FIMEMode = std::map<std::string, EIMEConversionMode>;
auto LoadIMEMode = []() -> FIMEMode
{
std::map<std::string, EIMEConversionMode> 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<LPSTR>(&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<LPSTR>(&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);