Add drive detection and states

This commit is contained in:
Grayson Riffe 2026-06-04 23:54:32 -05:00
parent 8d070ca5f4
commit 081eb00a46
Signed by: grayson
SSH Key Fingerprint: SHA256:23HJg9tnL2m6u0uUb26QIrOTFymFZ+xgSH/2UPEBB/I
6 changed files with 80 additions and 17 deletions

View File

@ -54,7 +54,7 @@ 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 "Time Remaining on Tester",IDC_STATICUPPER,7,4,286,22,SS_CENTERIMAGE,WS_EX_TRANSPARENT CTEXT "Lower",IDC_STATICLOWER,7,85,286,22,SS_CENTERIMAGE,WS_EX_TRANSPARENT
END END

View File

@ -11,19 +11,21 @@
#define WM_UPDATE WM_USER + 1 // Update window message #define WM_UPDATE WM_USER + 1 // Update window message
namespace watchfuleye { namespace watchfuleye {
Application::Application(const char* appName, const char* appVersion, unsigned int maximumMinutes) 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_maxMinutes(maximumMinutes)
, m_drive(driveToDetect)
, m_startTime() , m_startTime()
{ {
std::cout << std::format("{} {}\n", m_appName, m_appVersion); std::cout << std::format("{} {}\n", m_appName, m_appVersion);
} }
void Application::run() { void Application::run() {
m_startTime = Clock::now();
// Create dialog // Create dialog
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
@ -32,19 +34,29 @@ 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 HBRUSH backgroundBrush = CreateSolidBrush(RGB(255, 150, 150));
static HFONT timeFont = nullptr; 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);
@ -55,7 +67,7 @@ namespace watchfuleye {
// 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");
@ -64,7 +76,7 @@ namespace watchfuleye {
strcpy(lFont.lfFaceName, "Consolas"); strcpy(lFont.lfFaceName, "Consolas");
lFont.lfHeight = 45; lFont.lfHeight = 45;
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 = 200;
lFont.lfWeight = FW_BOLD; lFont.lfWeight = FW_BOLD;
@ -77,7 +89,8 @@ namespace watchfuleye {
} }
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 = {};
@ -106,6 +119,18 @@ namespace watchfuleye {
RECT clientRect = {}; RECT clientRect = {};
GetClientRect(dlg, &clientRect); GetClientRect(dlg, &clientRect);
HBRUSH backgroundBrush = nullptr;
if (!app->m_active)
backgroundBrush = greenBackgroundBrush;
else if (app->m_overTime)
backgroundBrush = redBackgroundBrush;
else
backgroundBrush = yellowBackgroundBrush;
FillRect(hDC, &clientRect, backgroundBrush); FillRect(hDC, &clientRect, backgroundBrush);
return TRUE; return TRUE;
@ -141,17 +166,29 @@ namespace watchfuleye {
RECT timeRect = {}; RECT timeRect = {};
GetClientRect(dlg, &timeRect); GetClientRect(dlg, &timeRect);
timeRect.top += 15, timeRect.bottom += 15; timeRect.top -= 25, timeRect.bottom -= 25;
SetTextColor(hDC, RGB(0, 0, 0)); SetTextColor(hDC, RGB(0, 0, 0));
SetBkMode(hDC, TRANSPARENT); SetBkMode(hDC, TRANSPARENT);
SelectObject(hDC, timeFont); SelectObject(hDC, timeFont);
Seconds elapsed = Duration(Clock::now() - app->m_startTime).count(); Seconds elapsed = Duration(Clock::now() - app->m_startTime).count();
if (!app->m_active)
elapsed = 0;
Seconds remaining = app->m_maxMinutes * 60 - elapsed; Seconds remaining = app->m_maxMinutes * 60 - elapsed;
unsigned int minutes = remaining % (3600) / 60, seconds = remaining % 60; 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);
}
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); EndPaint(dlg, &ps);
return TRUE; return TRUE;
@ -186,7 +223,23 @@ namespace watchfuleye {
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));
}
} }
} }

View File

@ -8,20 +8,26 @@ namespace watchfuleye {
using Seconds = int; using Seconds = int;
public: public:
Application(const char* appName, const char* appVersion, unsigned int maximumMinutes); 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; unsigned int m_maxMinutes;
const char* m_drive;
TimePoint m_startTime; TimePoint m_startTime;
}; };
} }

View File

@ -11,9 +11,12 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define APPNAME "Watchful Eye" #define APPNAME "Watchful Eye"
#define MAX_MINUTES 1
#define DRIVE_TO_DETECT "E:"
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
{ {
watchfuleye::Application app(APPNAME, APPVERSION, 15); watchfuleye::Application app(APPNAME, APPVERSION, MAX_MINUTES, DRIVE_TO_DETECT);
app.run(); app.run();
} }

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

View File

@ -3,7 +3,7 @@
// 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_STATICUPPER 1000 #define IDC_STATICLOWER 1000
// Next default values for new objects // Next default values for new objects
// //