diff --git a/Redcraft.FocusIME/Source/Private/Main.cpp b/Redcraft.FocusIME/Source/Private/Main.cpp index f2b6319..84b4274 100644 --- a/Redcraft.FocusIME/Source/Private/Main.cpp +++ b/Redcraft.FocusIME/Source/Private/Main.cpp @@ -2,27 +2,32 @@ #include #include +#include + #include #include -#include #include #include -#include #include -#include #include +#include + +#pragma comment(lib, "psapi.lib") #pragma warning(disable: 4996) // ReSharper disable CppDeprecatedEntity // ReSharper disable CppClangTidyCertErr33C +std::mutex GLogMutex; std::ofstream GLogFile; -bool GShouldExit = false; +std::atomic GShouldExit = false; void PrintLog(const std::string& Text); +void CALLBACK FocusEventHookProc(HWINEVENTHOOK, DWORD Event, HWND Window, LONG, LONG, DWORD, DWORD); + LRESULT CALLBACK MainWindowProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam); int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) @@ -47,8 +52,6 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) return 0; } - HWND MainWindow = nullptr; - WNDCLASS MainWindowClass = { }; MainWindowClass.lpfnWndProc = MainWindowProc; @@ -66,7 +69,7 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) return 1; } - MainWindow = CreateWindow( + const HWND MainWindow = CreateWindow( "FocusIME_MainWindow", "FocusIME", WS_OVERLAPPEDWINDOW, @@ -74,7 +77,7 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, Instance, nullptr); - if (!MainWindow) + if (MainWindow == nullptr) { MessageBox(nullptr, "Failed to create main window.", "FocusIME", MB_OK | MB_ICONERROR); @@ -85,6 +88,24 @@ 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); + + if (FocusEventHook == nullptr) + { + MessageBox(nullptr, "Failed to set focus event hook.", "FocusIME", MB_OK | MB_ICONWARNING); + + DestroyWindow(MainWindow); + + CloseHandle(Mutex); + + return 1; + } + GLogFile.open("Log.txt", std::ios::out | std::ios::app); if (!GLogFile.is_open()) @@ -140,7 +161,13 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) PrintLog("FocusIME is shutting down..."); - GLogFile.close(); + { + std::lock_guard Lock(GLogMutex); + + GLogFile.close(); + } + + UnhookWinEvent(FocusEventHook); DestroyWindow(MainWindow); @@ -237,6 +264,59 @@ LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPA return 0; } +void CALLBACK FocusEventHookProc(HWINEVENTHOOK, const DWORD Event, const HWND Window, LONG, LONG, DWORD, DWORD) +{ + if (Event == EVENT_OBJECT_FOCUS && Window != nullptr) + { + std::string ProcessName; + + do + { + DWORD PID = 0; + + GetWindowThreadProcessId(Window, &PID); + + const HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (ProcessHandle == nullptr) + { + ProcessName = "Unknown"; + + break; + } + + char Buffer[MAX_PATH] = { }; + + if (GetModuleBaseName(ProcessHandle, nullptr, Buffer, sizeof(Buffer)) == 0) + { + CloseHandle(ProcessHandle); + + ProcessName = "Unknown"; + + break; + } + + CloseHandle(ProcessHandle); + + ProcessName = Buffer; + } + while (false); + + char Title[1024] = { }; + + GetWindowText(Window, Title, sizeof(Title)); + + std::string MessageBuffer = "Focus changed to: " + ProcessName; + + if (strlen(Title) > 0) + { + MessageBuffer += " (" + std::string(Title) + ")"; + } + + PrintLog(MessageBuffer); + } +} + void PrintLog(const std::string& Text) { const auto Now = std::chrono::system_clock::now(); @@ -244,7 +324,9 @@ void PrintLog(const std::string& Text) const long long Milliseconds = (std::chrono::duration_cast(Now.time_since_epoch()) % 1000).count(); - static std::mutex Mutex; std::lock_guard Lock(Mutex); + std::lock_guard Lock(GLogMutex); + + if (!GLogFile.is_open()) return; const std::tm* LocalTime = std::localtime(&Time); // NOLINT(concurrency-mt-unsafe) @@ -261,6 +343,8 @@ void PrintLog(const std::string& Text) Buffer[23] = Digits[Milliseconds / 1 % 10]; GLogFile << Buffer << Text << std::endl; + + GLogFile.flush(); } // ReSharper restore CppDeprecatedEntity