4 Commits
Author SHA1 Message Date
grayson 4eae4af911 Finishing touches 2026-06-05 02:26:19 -05:00
grayson 89c76d0a9c Add About dialog and version info 2026-06-05 00:22:45 -05:00
grayson 081eb00a46 Add drive detection and states 2026-06-04 23:54:32 -05:00
grayson 8d070ca5f4 Add countdown and fix flicker 2026-06-04 22:39:08 -05:00
7 changed files with 199 additions and 31 deletions

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

+50 -4
View File
@@ -1,6 +1,7 @@
// Microsoft Visual C++ generated resource script. // Microsoft Visual C++ generated resource script.
// //
#include "src/resource.h" #include "src/resource.h"
#include "version.h"
#define APSTUDIO_READONLY_SYMBOLS #define APSTUDIO_READONLY_SYMBOLS
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@@ -49,13 +50,12 @@ END
// //
IDD_DIALOGMAIN DIALOGEX 0, 0, 300, 114 IDD_DIALOGMAIN DIALOGEX 0, 0, 300, 114
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_FIXEDSYS | WS_CAPTION
EXSTYLE WS_EX_TOPMOST EXSTYLE WS_EX_TOPMOST | WS_EX_TOOLWINDOW
CAPTION "Dialog" CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1 FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN BEGIN
CTEXT "00:00",IDC_STATICTIME,17,35,266,60,SS_CENTERIMAGE,WS_EX_TRANSPARENT CTEXT "Lower",IDC_STATICLOWER,7,85,286,22,SS_CENTERIMAGE,WS_EX_TRANSPARENT
CTEXT "Time left on tester:",IDC_STATICUPPER,7,4,286,22,SS_CENTERIMAGE,WS_EX_TRANSPARENT
END END
@@ -88,6 +88,52 @@ BEGIN
0 0
END END
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x0L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Grayson Riffe"
VALUE "FileDescription", "Watchful Eye"
VALUE "InternalName", "WatchfulEye"
VALUE "LegalCopyright", "Copyright (C) 2026"
VALUE "OriginalFilename", "WatchfulEye.exe"
VALUE "ProductName", "Watchful Eye"
VALUE "ProductVersion", APPVERSION
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICONMAIN ICON "res\\watchful-eye.ico"
#endif // English (United States) resources #endif // English (United States) resources
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
+122 -20
View File
@@ -5,14 +5,23 @@
#include "resource.h" #include "resource.h"
#define WM_TRAYICON WM_USER #define WM_TRAYICON WM_USER // Tray icon window message
#define IDM_EXIT 1001 #define IDM_ABOUT 1001 // "About" menu identifier
#define IDM_EXIT 1002 // "Exit" menu identifier
#define IDT_UPDATE 1 // Update timer identifier
#define WM_UPDATE WM_USER + 1 // Update window message
namespace watchfuleye { namespace watchfuleye {
Application::Application(const char* appName, const char* appVersion) Application::Application(const char* appName, const char* appVersion, unsigned int maximumMinutes, const char* driveToDetect)
: m_appName(appName) : m_appName(appName)
, m_appVersion(appVersion) , m_appVersion(appVersion)
, m_mainDlg(nullptr) , m_mainDlg(nullptr)
, m_running(true)
, m_active(false)
, m_overTime(false)
, m_maxMinutes(maximumMinutes)
, m_drive(driveToDetect)
, m_startTime()
{ {
std::cout << std::format("{} {}\n", m_appName, m_appVersion); std::cout << std::format("{} {}\n", m_appName, m_appVersion);
} }
@@ -26,54 +35,69 @@ namespace watchfuleye {
// Create tray icon // Create tray icon
createTrayIcon(); createTrayIcon();
// Create drive query thread
std::thread queryThread(&Application::queryThreadFunction, this);
ShowWindow(m_mainDlg, SW_SHOW); ShowWindow(m_mainDlg, SW_SHOW);
MSG msg = {}; MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0)) while (GetMessage(&msg, nullptr, 0, 0))
IsDialogMessage(m_mainDlg, &msg); IsDialogMessage(m_mainDlg, &msg);
m_running = false;
queryThread.join();
} }
BOOL CALLBACK Application::mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) { BOOL CALLBACK Application::mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static Application* app = nullptr; static Application* app = nullptr;
static UINT taskbarCreatedMessage = 0; static UINT taskbarCreatedMessage = 0;
static HFONT timeFont = nullptr;
static HBRUSH greenBackgroundBrush = CreateSolidBrush(RGB(150, 255, 150));
static HBRUSH yellowBackgroundBrush = CreateSolidBrush(RGB(255, 255, 150));
static HBRUSH redBackgroundBrush = CreateSolidBrush(RGB(255, 150, 150));
switch (msg) { switch (msg) {
case WM_INITDIALOG: { case WM_INITDIALOG: {
app = reinterpret_cast<Application*>(lParam); app = reinterpret_cast<Application*>(lParam);
SetWindowText(dlg, app->m_appName.c_str()); SetWindowText(dlg, app->m_appName.c_str());
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION))));
// Spawn in the lower right corner of the virtual screen (might be problematic in some cases) // 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 cornerX = GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN);
int cornerY = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN); int cornerY = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN);
SetWindowPos(dlg, nullptr, cornerX - 550, cornerY - 340, NULL, NULL, SWP_NOZORDER | SWP_NOSIZE); SetWindowPos(dlg, nullptr, cornerX - 550, cornerY - 540, NULL, NULL, SWP_NOZORDER | SWP_NOSIZE);
taskbarCreatedMessage = RegisterWindowMessage("TaskbarCreated"); taskbarCreatedMessage = RegisterWindowMessage("TaskbarCreated");
// Set up UI // Set up UI
LOGFONT lFont = {}; LOGFONT lFont = {};
strcpy(lFont.lfFaceName, "Consolas"); strcpy(lFont.lfFaceName, "Consolas");
lFont.lfHeight = 50; lFont.lfHeight = 35;
HFONT upperFont = CreateFontIndirect(&lFont); HFONT upperFont = CreateFontIndirect(&lFont);
SendMessage(GetDlgItem(dlg, IDC_STATICUPPER), WM_SETFONT, reinterpret_cast<WPARAM>(upperFont), NULL); SendMessage(GetDlgItem(dlg, IDC_STATICLOWER), WM_SETFONT, reinterpret_cast<WPARAM>(upperFont), NULL);
lFont.lfHeight = 200; lFont.lfHeight = 180;
HFONT timeFont = CreateFontIndirect(&lFont); lFont.lfWeight = FW_BOLD;
SendMessage(GetDlgItem(dlg, IDC_STATICTIME), WM_SETFONT, reinterpret_cast<WPARAM>(timeFont), NULL); timeFont = CreateFontIndirect(&lFont);
SendMessage(dlg, WM_UPDATE, NULL, NULL);
SetTimer(dlg, IDT_UPDATE, 1000, nullptr);
return TRUE; return TRUE;
} }
case WM_TRAYICON: { case WM_TRAYICON: {
if (LOWORD(lParam) == WM_RBUTTONDOWN) { if (LOWORD(lParam) == WM_LBUTTONDOWN ||
LOWORD(lParam) == WM_RBUTTONDOWN) {
SetForegroundWindow(dlg); SetForegroundWindow(dlg);
POINT cursor = {}; POINT cursor = {};
GetCursorPos(&cursor); GetCursorPos(&cursor);
HMENU popupMenu = CreatePopupMenu(); HMENU popupMenu = CreatePopupMenu();
AppendMenu(popupMenu, MF_STRING, IDM_ABOUT, "About");
AppendMenu(popupMenu, MF_STRING, IDM_EXIT, "Exit"); AppendMenu(popupMenu, MF_STRING, IDM_EXIT, "Exit");
TrackPopupMenu(popupMenu, NULL, cursor.x, cursor.y, NULL, dlg, nullptr); // Will block here. Might be problematic. TrackPopupMenu(popupMenu, NULL, cursor.x, cursor.y, NULL, dlg, nullptr); // Will block here. Might be problematic.
@@ -85,7 +109,14 @@ namespace watchfuleye {
} }
case WM_COMMAND: { case WM_COMMAND: {
if (HIWORD(wParam) == 0 && LOWORD(wParam) == IDM_EXIT) if (HIWORD(wParam) == 0 && LOWORD(wParam) == IDM_ABOUT)
MessageBox(dlg, std::format("{} {}\nCopyright Grayson Riffe 2026\ngraysonriffe.com\n\n"
"\t{} is a simple countdown application\n"
"specifically designed for this electronics testing labratory.\n\n"
"Hopefully, it's working!", app->m_appName, app->m_appVersion,
app->m_appName).c_str(), std::format("About {}", app->m_appName).c_str(), MB_OK);
else if (HIWORD(wParam) == 0 && LOWORD(wParam) == IDM_EXIT)
DestroyWindow(dlg); DestroyWindow(dlg);
return TRUE; return TRUE;
@@ -94,28 +125,83 @@ namespace watchfuleye {
case WM_ERASEBKGND: { case WM_ERASEBKGND: {
HDC hDC = reinterpret_cast<HDC>(wParam); HDC hDC = reinterpret_cast<HDC>(wParam);
HBRUSH backgroundBrush = CreateSolidBrush(RGB(255, 150, 150));
RECT clientRect = {}; RECT clientRect = {};
GetClientRect(dlg, &clientRect); GetClientRect(dlg, &clientRect);
FillRect(hDC, &clientRect, backgroundBrush);
DeleteObject(backgroundBrush); HBRUSH backgroundBrush = nullptr;
if (!app->m_active)
backgroundBrush = greenBackgroundBrush;
else if (app->m_overTime)
backgroundBrush = redBackgroundBrush;
else
backgroundBrush = yellowBackgroundBrush;
FillRect(hDC, &clientRect, backgroundBrush);
return TRUE; return TRUE;
} }
case WM_CTLCOLORSTATIC: { case WM_CTLCOLORSTATIC: {
if (GetWindowLongPtr(reinterpret_cast<HWND>(lParam), GWL_EXSTYLE) & WS_EX_TRANSPARENT) { if (GetWindowLongPtr(reinterpret_cast<HWND>(lParam), GWL_EXSTYLE) & WS_EX_TRANSPARENT) {
std::cout << "Test\n";
SetBkMode(reinterpret_cast<HDC>(wParam), TRANSPARENT); SetBkMode(reinterpret_cast<HDC>(wParam), TRANSPARENT);
return reinterpret_cast<INT_PTR>(GetStockObject(NULL_BRUSH)); return reinterpret_cast<INT_PTR>(GetStockObject(NULL_BRUSH));
} }
return FALSE; 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 -= 25, timeRect.bottom -= 25;
SetTextColor(hDC, RGB(0, 0, 0));
SetBkMode(hDC, TRANSPARENT);
SelectObject(hDC, timeFont);
Seconds elapsed = Duration(Clock::now() - app->m_startTime).count();
if (!app->m_active)
elapsed = 0;
Seconds remaining = app->m_maxMinutes * 60 - elapsed;
if (app->m_overTime) {
SetDlgItemText(dlg, IDC_STATICLOWER, "Please Eject Your USB");
DrawText(hDC, "STOP", -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
else {
SetDlgItemText(dlg, IDC_STATICLOWER, "Time Remaining on Tester");
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);
}
if (remaining <= 0)
app->m_overTime = true;
EndPaint(dlg, &ps);
return TRUE;
}
case WM_CLOSE: { case WM_CLOSE: {
// Do nothing // Do nothing
return TRUE; return TRUE;
@@ -140,12 +226,28 @@ namespace watchfuleye {
iconData.hWnd = m_mainDlg; iconData.hWnd = m_mainDlg;
iconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; iconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
iconData.uCallbackMessage = WM_TRAYICON; iconData.uCallbackMessage = WM_TRAYICON;
iconData.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION)); iconData.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN));
strcpy(iconData.szTip, m_appName.c_str()); strcpy(iconData.szTip, m_appName.c_str());
Shell_NotifyIcon(NIM_ADD, &iconData); Shell_NotifyIcon(NIM_ADD, &iconData);
} }
Application::~Application() { void Application::queryThreadFunction() {
char buff[100] = {};
while (m_running) {
bool driveDetected = QueryDosDevice(m_drive, buff, 100) > 0;
if (driveDetected && !m_active) {
m_active = true;
m_startTime = Clock::now();
}
else if (!driveDetected && m_active) {
m_active = false;
m_overTime = false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
} }
} }
+17 -3
View File
@@ -2,18 +2,32 @@
namespace watchfuleye { namespace watchfuleye {
class Application { class Application {
using Clock = std::chrono::high_resolution_clock;
using TimePoint = std::chrono::time_point<Clock>;
using Duration = std::chrono::duration<double, std::ratio<1, 1>>;
using Seconds = int;
public: public:
Application(const char* appName, const char* appVersion); Application(const char* appName, const char* appVersion, unsigned int maximumMinutes, const char* driveToDetect);
void run(); void run();
~Application();
private: private:
static BOOL CALLBACK mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam); static BOOL CALLBACK mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
void createTrayIcon(); void createTrayIcon();
void queryThreadFunction();
std::string m_appName, m_appVersion; std::string m_appName, m_appVersion;
HWND m_mainDlg; HWND m_mainDlg;
bool m_running; // Is the application running?
bool m_active; // Is the timer ticking?
bool m_overTime; // Are we out of time?
unsigned int m_maxMinutes;
const char* m_drive;
TimePoint m_startTime;
}; };
} }
+6 -1
View File
@@ -11,9 +11,14 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define APPNAME "Watchful Eye" #define APPNAME "Watchful Eye"
// ======== Configuration ========
#define MAX_MINUTES 15
#define DRIVE_TO_DETECT "D:"
// ===============================
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
{ {
watchfuleye::Application app(APPNAME, APPVERSION); watchfuleye::Application app(APPNAME, APPVERSION, MAX_MINUTES, DRIVE_TO_DETECT);
app.run(); app.run();
} }
+1
View File
@@ -5,6 +5,7 @@
#include <string> #include <string>
#include <format> #include <format>
#include <chrono> #include <chrono>
#include <thread>
// Windows // Windows
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
+3 -3
View File
@@ -3,14 +3,14 @@
// Used by C:\Users\Grayson\Documents\Visual Studio 18\Solutions\watchful-eye\WatchfulEye\resource.rc // Used by C:\Users\Grayson\Documents\Visual Studio 18\Solutions\watchful-eye\WatchfulEye\resource.rc
// //
#define IDD_DIALOGMAIN 101 #define IDD_DIALOGMAIN 101
#define IDC_STATICTIME 1000 #define IDI_ICONMAIN 103
#define IDC_STATICUPPER 1001 #define IDC_STATICLOWER 1000
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_RESOURCE_VALUE 104
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101