// Application class implementation #include "pch.h" #include "Application.h" #include "resource.h" #define WM_TRAYICON WM_USER // Tray icon window message #define IDM_EXIT 1001 // "Exit" menu identifier #define IDT_UPDATE 1 // Update timer identifier #define WM_UPDATE WM_USER + 1 // Update window message namespace watchfuleye { Application::Application(const char* appName, const char* appVersion, unsigned int maximumMinutes) : m_appName(appName) , m_appVersion(appVersion) , m_mainDlg(nullptr) , m_maxMinutes(maximumMinutes) , m_startTime() { std::cout << std::format("{} {}\n", m_appName, m_appVersion); } void Application::run() { m_startTime = Clock::now(); // Create dialog SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); m_mainDlg = CreateDialogParam(nullptr, MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast(mainDlgProc), reinterpret_cast(this)); // Create tray icon createTrayIcon(); ShowWindow(m_mainDlg, SW_SHOW); MSG msg = {}; while (GetMessage(&msg, nullptr, 0, 0)) IsDialogMessage(m_mainDlg, &msg); } BOOL CALLBACK Application::mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) { static Application* app = nullptr; static UINT taskbarCreatedMessage = 0; static HBRUSH backgroundBrush = CreateSolidBrush(RGB(255, 150, 150)); static HFONT timeFont = nullptr; switch (msg) { case WM_INITDIALOG: { app = reinterpret_cast(lParam); SetWindowText(dlg, app->m_appName.c_str()); SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast(LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION)))); // Spawn in the lower right corner of the virtual screen (might be problematic in some cases) int cornerX = GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN); int cornerY = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN); SetWindowPos(dlg, nullptr, cornerX - 550, cornerY - 340, NULL, NULL, SWP_NOZORDER | SWP_NOSIZE); taskbarCreatedMessage = RegisterWindowMessage("TaskbarCreated"); // Set up UI LOGFONT lFont = {}; strcpy(lFont.lfFaceName, "Consolas"); lFont.lfHeight = 45; HFONT upperFont = CreateFontIndirect(&lFont); SendMessage(GetDlgItem(dlg, IDC_STATICUPPER), WM_SETFONT, reinterpret_cast(upperFont), NULL); lFont.lfHeight = 200; lFont.lfWeight = FW_BOLD; timeFont = CreateFontIndirect(&lFont); SendMessage(dlg, WM_UPDATE, NULL, NULL); SetTimer(dlg, IDT_UPDATE, 1000, nullptr); return TRUE; } case WM_TRAYICON: { if (LOWORD(lParam) == WM_RBUTTONDOWN) { SetForegroundWindow(dlg); POINT cursor = {}; GetCursorPos(&cursor); HMENU popupMenu = CreatePopupMenu(); AppendMenu(popupMenu, MF_STRING, IDM_EXIT, "Exit"); TrackPopupMenu(popupMenu, NULL, cursor.x, cursor.y, NULL, dlg, nullptr); // Will block here. Might be problematic. DestroyMenu(popupMenu); } return TRUE; } case WM_COMMAND: { if (HIWORD(wParam) == 0 && LOWORD(wParam) == IDM_EXIT) DestroyWindow(dlg); return TRUE; } case WM_ERASEBKGND: { HDC hDC = reinterpret_cast(wParam); RECT clientRect = {}; GetClientRect(dlg, &clientRect); FillRect(hDC, &clientRect, backgroundBrush); return TRUE; } case WM_CTLCOLORSTATIC: { if (GetWindowLongPtr(reinterpret_cast(lParam), GWL_EXSTYLE) & WS_EX_TRANSPARENT) { SetBkMode(reinterpret_cast(wParam), TRANSPARENT); return reinterpret_cast(GetStockObject(NULL_BRUSH)); } return FALSE; } case WM_TIMER: { if (wParam == IDT_UPDATE) { SendMessage(dlg, WM_UPDATE, NULL, NULL); return TRUE; } return FALSE; } case WM_UPDATE: { InvalidateRect(dlg, nullptr, TRUE); UpdateWindow(dlg); return TRUE; } case WM_PAINT: { PAINTSTRUCT ps = {}; HDC hDC = BeginPaint(dlg, &ps); RECT timeRect = {}; GetClientRect(dlg, &timeRect); timeRect.top += 15, timeRect.bottom += 15; SetTextColor(hDC, RGB(0, 0, 0)); SetBkMode(hDC, TRANSPARENT); SelectObject(hDC, timeFont); Seconds elapsed = Duration(Clock::now() - app->m_startTime).count(); Seconds remaining = app->m_maxMinutes * 60 - elapsed; unsigned int minutes = remaining % (3600) / 60, seconds = remaining % 60; DrawText(hDC, std::format("{:02}:{:02}", minutes, seconds).c_str(), -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER); EndPaint(dlg, &ps); return TRUE; } case WM_CLOSE: { // Do nothing return TRUE; } case WM_DESTROY: { PostQuitMessage(0); return TRUE; } } if (msg == taskbarCreatedMessage) app->createTrayIcon(); return FALSE; } void Application::createTrayIcon() { NOTIFYICONDATA iconData = {}; iconData.cbSize = sizeof(iconData); iconData.uVersion = NOTIFYICON_VERSION_4; iconData.hWnd = m_mainDlg; iconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; iconData.uCallbackMessage = WM_TRAYICON; iconData.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION)); strcpy(iconData.szTip, m_appName.c_str()); Shell_NotifyIcon(NIM_ADD, &iconData); } Application::~Application() { } }