diff --git a/Redcraft.FocusIME/Source/Private/Main.cpp b/Redcraft.FocusIME/Source/Private/Main.cpp index b08604e..f2b6319 100644 --- a/Redcraft.FocusIME/Source/Private/Main.cpp +++ b/Redcraft.FocusIME/Source/Private/Main.cpp @@ -1,6 +1,7 @@ #include "Defines.h" #include +#include #include #include #include @@ -18,6 +19,224 @@ std::ofstream GLogFile; +bool GShouldExit = false; + +void PrintLog(const std::string& Text); + +LRESULT CALLBACK MainWindowProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam); + +int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int) +{ + using namespace std::chrono_literals; + + const HANDLE Mutex = CreateMutex(nullptr, TRUE, "FocusIME_SingleInstance_Mutex"); + + if (Mutex == nullptr) + { + MessageBox(nullptr, "Failed to create mutex.", "FocusIME", MB_OK | MB_ICONERROR); + + return 1; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + MessageBox(nullptr, "FocusIME is already running.", "FocusIME", MB_OK | MB_ICONINFORMATION); + + CloseHandle(Mutex); + + return 0; + } + + HWND MainWindow = nullptr; + + WNDCLASS MainWindowClass = { }; + + MainWindowClass.lpfnWndProc = MainWindowProc; + MainWindowClass.hInstance = Instance; + MainWindowClass.lpszClassName = "FocusIME_MainWindow"; + MainWindowClass.hCursor = LoadCursor(nullptr, IDC_ARROW); + MainWindowClass.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); // NOLINT(performance-no-int-to-ptr) + + if (!RegisterClass(&MainWindowClass)) + { + MessageBox(nullptr, "Failed to register main window class.", "FocusIME", MB_OK | MB_ICONERROR); + + CloseHandle(Mutex); + + return 1; + } + + MainWindow = CreateWindow( + "FocusIME_MainWindow", + "FocusIME", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + nullptr, nullptr, Instance, nullptr); + + if (!MainWindow) + { + MessageBox(nullptr, "Failed to create main window.", "FocusIME", MB_OK | MB_ICONERROR); + + CloseHandle(Mutex); + + return 1; + } + + ShowWindow(MainWindow, SW_HIDE); + + GLogFile.open("Log.txt", std::ios::out | std::ios::app); + + if (!GLogFile.is_open()) + { + MessageBox(nullptr, "Failed to open log file.", "FocusIME", MB_OK | MB_ICONERROR); + + CloseHandle(Mutex); + + return 1; + } + + PrintLog("FocusIME started successfully"); + + MSG Message = { }; + + while (!GShouldExit) + { + 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(&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; + } + } + + PrintLog("FocusIME is shutting down..."); + + GLogFile.close(); + + DestroyWindow(MainWindow); + + CloseHandle(Mutex); + + return 0; +} + +LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPARAM WParam, const LPARAM LParam) +{ + static NOTIFYICONDATA NotifyIconData; + + constexpr UINT TrayIconMessage = WM_USER + 1; + + constexpr UINT ExitMessage = 1001; + + switch (Message) + { + case WM_CREATE: + + memset(&NotifyIconData, 0, sizeof(NOTIFYICONDATA)); + + NotifyIconData.cbSize = sizeof(NOTIFYICONDATA); + NotifyIconData.hWnd = Window; + NotifyIconData.uID = 1; + NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + NotifyIconData.uCallbackMessage = TrayIconMessage; + + NotifyIconData.hIcon = LoadIcon(nullptr, IDI_APPLICATION); + + strcpy(NotifyIconData.szTip, "FocusIME"); + + if (!Shell_NotifyIcon(NIM_ADD, &NotifyIconData)) return -1; + + break; + + case TrayIconMessage: + + switch (LParam) + { + case WM_RBUTTONUP: + case WM_CONTEXTMENU: + + POINT Point; + + GetCursorPos(&Point); + + if (const HMENU Menu = CreatePopupMenu()) + { + InsertMenu(Menu, -1, MF_BYPOSITION | MF_STRING, ExitMessage, "Exit(&X)"); + + SetMenuDefaultItem(Menu, ExitMessage, FALSE); + + SetForegroundWindow(Window); + + TrackPopupMenu(Menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, Point.x, Point.y, 0, Window, nullptr); + + PostMessage(Window, WM_NULL, 0, 0); + + DestroyMenu(Menu); + } + + break; + + default: break; + } + + break; + + case WM_COMMAND: + + if (LOWORD(WParam) == ExitMessage) + { + PrintLog("Exit command received from tray menu"); + + GShouldExit = true; + + PostQuitMessage(0); + } + + break; + + case WM_DESTROY: + + Shell_NotifyIcon(NIM_DELETE, &NotifyIconData); + + PostQuitMessage(0); + + break; + + default: return DefWindowProc(Window, Message, WParam, LParam); + } + + return 0; +} + void PrintLog(const std::string& Text) { const auto Now = std::chrono::system_clock::now(); @@ -44,64 +263,5 @@ void PrintLog(const std::string& Text) GLogFile << Buffer << Text << std::endl; } -int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) -{ - using namespace std::chrono_literals; - - const HANDLE Mutex = CreateMutex(nullptr, TRUE, "FocusIME_SingleInstance_Mutex"); - - if (Mutex == nullptr) - { - MessageBox(nullptr, "Failed to create mutex.", "FocusIME", MB_OK | MB_ICONERROR); - - return 1; - } - - if (GetLastError() == ERROR_ALREADY_EXISTS) - { - MessageBox(nullptr, "FocusIME is already running.", "FocusIME", MB_OK | MB_ICONINFORMATION); - - CloseHandle(Mutex); - - return 0; - } - - GLogFile.open("Log.txt", std::ios::out | std::ios::app); - - if (!GLogFile.is_open()) - { - MessageBox(nullptr, "Failed to open log file.", "FocusIME", MB_OK | MB_ICONERROR); - - CloseHandle(Mutex); - - return 1; - } - - PrintLog("FocusIME started successfully"); - - MSG Message = { }; - - while (true) - { - BOOL bResult = GetMessage(&Message, nullptr, 0, 0); - - if (bResult > 0) - { - TranslateMessage(&Message); - DispatchMessage (&Message); - } - - else break; - } - - PrintLog("FocusIME is shutting down..."); - - GLogFile.close(); - - CloseHandle(Mutex); - - return 0; -} - // ReSharper restore CppDeprecatedEntity // ReSharper restore CppClangTidyCertErr33C