feat(master): add polling tick mode
This commit is contained in:
@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user