Compare commits
2 Commits
fa81c1d418
...
7e6be53e8b
Author | SHA1 | Date | |
---|---|---|---|
7e6be53e8b | |||
9a83aea6a2 |
@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user