feat(tray): add tray icon control
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
#include "Defines.h"
|
#include "Defines.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <shellapi.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -18,6 +19,224 @@
|
|||||||
|
|
||||||
std::ofstream GLogFile;
|
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<HBRUSH>(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<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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
void PrintLog(const std::string& Text)
|
||||||
{
|
{
|
||||||
const auto Now = std::chrono::system_clock::now();
|
const auto Now = std::chrono::system_clock::now();
|
||||||
@ -44,64 +263,5 @@ void PrintLog(const std::string& Text)
|
|||||||
GLogFile << Buffer << Text << std::endl;
|
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 CppDeprecatedEntity
|
||||||
// ReSharper restore CppClangTidyCertErr33C
|
// ReSharper restore CppClangTidyCertErr33C
|
||||||
|
Reference in New Issue
Block a user