Compare commits

...

2 Commits

Author SHA1 Message Date
7e6be53e8b fix(json): fix IME mode incomplete loading 2025-06-24 21:28:35 +08:00
9a83aea6a2 feat(master): add polling tick mode 2025-06-22 18:42:55 +08:00

View File

@ -21,9 +21,17 @@
// ReSharper disable CppDeprecatedEntity // ReSharper disable CppDeprecatedEntity
// ReSharper disable CppClangTidyCertErr33C // 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; std::ofstream GLogStream;
@ -33,8 +41,6 @@ void PrintLog(const std::string& Text);
void Tick() void Tick()
{ {
using namespace std::chrono_literals;
// ReSharper disable CppInconsistentNaming // ReSharper disable CppInconsistentNaming
// ReSharper disable CppClangTidyPerformanceEnumSize // ReSharper disable CppClangTidyPerformanceEnumSize
@ -52,15 +58,17 @@ void Tick()
Chinese, 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::map<std::string, EIMEConversionMode> Result;
std::ifstream File(CachedModeFilename); std::ifstream File(IMEModeFilename);
if (!File.is_open()) if (!File.is_open())
{ {
PrintLog("Cached conversion mode configuration file not found"); PrintLog("Conversion mode configuration file not found");
return Result; return Result;
} }
@ -137,18 +145,17 @@ void Tick()
while (!View.empty() && std::isspace(View.front())) View.remove_prefix(1); while (!View.empty() && std::isspace(View.front())) View.remove_prefix(1);
if (View.empty()) return true; if (Mode != "English" && Mode != "Chinese" && Mode != "Default") return false;
Result.emplace(Process,
Mode == "English" ? EIMEConversionMode::English :
Mode == "Chinese" ? EIMEConversionMode::Chinese : EIMEConversionMode::Default);
if (View.empty()) break;
if (!View.starts_with(',')) return false; if (!View.starts_with(',')) return false;
View.remove_prefix(1); View.remove_prefix(1);
if (Mode != "English" && Mode != "Chinese" && Mode != "Default") return false;
Result[std::string(Process)] =
Mode == "English" ? EIMEConversionMode::English :
Mode == "Chinese" ? EIMEConversionMode::Chinese : EIMEConversionMode::Default;
} }
return true; return true;
@ -157,25 +164,24 @@ void Tick()
if (!bSuccessful) break; 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; return Result;
} }
while (false); while (false);
PrintLog("Invalid format detected in cached conversion mode configuration file"); PrintLog("Invalid format detected in conversion mode configuration file");
return Result; return Result;
};
} (); auto SaveIMEMode = [](const FIMEMode& IMEMode)
auto SaveCachedIMEMode = []
{ {
std::ofstream File(CachedModeFilename); std::ofstream File(IMEModeFilename);
if (!File.is_open()) if (!File.is_open())
{ {
PrintLog("Error: Failed to save cached conversion mode configuration"); PrintLog("Error: Failed to save conversion mode configuration");
return; return;
} }
@ -184,7 +190,7 @@ void Tick()
bool bFirstItem = true; bool bFirstItem = true;
for (const auto& [Name, Mode] : CachedIMEMode) for (const auto& [Name, Mode] : IMEMode)
{ {
if (!bFirstItem) File << ","; if (!bFirstItem) File << ",";
@ -200,6 +206,8 @@ void Tick()
File.close(); File.close();
}; };
static FIMEMode IMEMode = LoadIMEMode();
static HWND LastWindow = nullptr; static HWND LastWindow = nullptr;
static std::string CachedProcess; static std::string CachedProcess;
@ -208,7 +216,7 @@ void Tick()
{ {
do 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)); HKL KeyboardLayout = GetKeyboardLayout(GetWindowThreadProcessId(LastWindow, nullptr));
@ -229,15 +237,15 @@ void Tick()
if (CurrentMode == EIMEConversionMode::Default) break; 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::English ? "English" :
CurrentMode == EIMEConversionMode::Chinese ? "Chinese" : "Default")); CurrentMode == EIMEConversionMode::Chinese ? "Chinese" : "Default"));
SaveCachedIMEMode(); SaveIMEMode(IMEMode);
} }
} }
} }
@ -280,9 +288,10 @@ void Tick()
CachedProcess = Buffer; 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)) if (const HWND IMEWindow = ImmGetDefaultIMEWnd(LastWindow))
{ {
@ -291,7 +300,7 @@ void Tick()
if (!OpenStatus) break; if (!OpenStatus) break;
LPARAM TargetMode = CachedIMEMode[CachedProcess] == EIMEConversionMode::Chinese ? 0x0401 : 0x0000; LPARAM TargetMode = IMEMode[CachedProcess] == EIMEConversionMode::Chinese ? 0x0401 : 0x0000;
if (ConversionMode != TargetMode) if (ConversionMode != TargetMode)
{ {
@ -369,22 +378,27 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int)
ShowWindow(MainWindow, SW_HIDE); ShowWindow(MainWindow, SW_HIDE);
const HWINEVENTHOOK FocusEventHook = SetWinEventHook( HWINEVENTHOOK FocusEventHook;
EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS,
nullptr,
FocusEventHookProc,
0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
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 # if BUILD_DEBUG || BUILD_DEVELOPMENT
@ -432,41 +446,63 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int)
MSG Message = { }; MSG Message = { };
while (!GShouldExit) if constexpr (bEnablePolling)
{ {
const BOOL bResult = GetMessage(&Message, nullptr, 0, 0); while (!GShouldExit)
if (bResult > 0)
{ {
TranslateMessage(&Message); if (PeekMessage(&Message, nullptr, 0, 0, PM_REMOVE)) {
DispatchMessage (&Message);
}
else if (bResult == 0) break; if (Message.message == WM_QUIT) break;
else TranslateMessage(&Message);
{ DispatchMessage (&Message);
DWORD ErrorCode = GetLastError();
LPSTR MessageBuffer = nullptr; continue;
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); 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 +516,10 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int)
} }
# endif # endif
UnhookWinEvent(FocusEventHook); if constexpr (bEnableCallback)
{
UnhookWinEvent(FocusEventHook);
}
DestroyWindow(MainWindow); DestroyWindow(MainWindow);