9 Commits
Author SHA1 Message Date
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
grayson cf310dc71c Setup UI fonts 2026-06-04 20:20:51 -05:00
grayson 2d6a972e85 Change background color 2026-06-04 20:11:49 -05:00
grayson 35b0d02af5 Add tray icon 2026-06-04 19:41:31 -05:00
grayson 1d3e58587d Add base dialog 2026-06-04 18:08:26 -05:00
grayson fdbe1a07bb Add precompiled header 2026-06-04 11:22:42 -05:00
grayson 3977d85fee Add version system 2026-06-04 11:16:13 -05:00
grayson 97727d05d4 Add CMake project 2026-06-04 11:06:16 -05:00
11 changed files with 513 additions and 0 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
.vs/
build/
*.aps
+6
View File
@@ -0,0 +1,6 @@
# Watchful Eye project CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(watchful-eye)
add_subdirectory(WatchfulEye)
+49
View File
@@ -0,0 +1,49 @@
// Watchful Eye CMake presets
{
"version": 3,
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_C_COMPILER": "cl.exe",
"CMAKE_CXX_COMPILER": "cl.exe"
}
},
{
"name": "Debug",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "Release",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
],
"buildPresets": [
{
"name": "Debug",
"configurePreset": "Debug"
},
{
"name": "Release",
"configurePreset": "Release"
}
]
}
+17
View File
@@ -0,0 +1,17 @@
# Watchful Eye app CMakeLists.txt
add_executable(WatchfulEye WIN32 "src/main.cpp" "src/pch.h" "src/Application.h" "src/Application.cpp" "resource.rc" "src/resource.h")
set_property(TARGET WatchfulEye PROPERTY CXX_STANDARD 20)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /entry:mainCRTStartup")
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
set_property(TARGET WatchfulEye PROPERTY WIN32_EXECUTABLE FALSE)
endif()
target_precompile_headers(WatchfulEye PUBLIC "src/pch.h")
find_package(Git)
execute_process(COMMAND ${GIT_EXECUTABLE} describe OUTPUT_VARIABLE APPVERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
configure_file(src/version.h.in version.h)
target_include_directories(WatchfulEye PUBLIC "${PROJECT_BINARY_DIR}/WatchfulEye")
+104
View File
@@ -0,0 +1,104 @@
// Microsoft Visual C++ generated resource script.
//
#include "src/resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include <winres.h>
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"src/resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include <winres.h>\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_DIALOGMAIN DIALOGEX 0, 0, 300, 114
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CAPTION
EXSTYLE WS_EX_TOPMOST | WS_EX_TOOLWINDOW
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CTEXT "Lower",IDC_STATICLOWER,7,85,286,22,SS_CENTERIMAGE,WS_EX_TRANSPARENT
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_DIALOGMAIN, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 293
TOPMARGIN, 7
BOTTOMMARGIN, 107
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//
IDD_DIALOGMAIN AFX_DIALOG_LAYOUT
BEGIN
0
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
+245
View File
@@ -0,0 +1,245 @@
// 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, const char* driveToDetect)
: m_appName(appName)
, m_appVersion(appVersion)
, 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);
}
void Application::run() {
// Create dialog
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
m_mainDlg = CreateDialogParam(nullptr, MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast<DLGPROC>(mainDlgProc), reinterpret_cast<LPARAM>(this));
// Create tray icon
createTrayIcon();
// Create drive query thread
std::thread queryThread(&Application::queryThreadFunction, this);
ShowWindow(m_mainDlg, SW_SHOW);
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
IsDialogMessage(m_mainDlg, &msg);
m_running = false;
queryThread.join();
}
BOOL CALLBACK Application::mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static Application* app = nullptr;
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) {
case WM_INITDIALOG: {
app = reinterpret_cast<Application*>(lParam);
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)
int cornerX = GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN);
int cornerY = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN);
SetWindowPos(dlg, nullptr, cornerX - 550, cornerY - 540, 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_STATICLOWER), WM_SETFONT, reinterpret_cast<WPARAM>(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_LBUTTONDOWN ||
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<HDC>(wParam);
RECT 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);
return TRUE;
}
case WM_CTLCOLORSTATIC: {
if (GetWindowLongPtr(reinterpret_cast<HWND>(lParam), GWL_EXSTYLE) & WS_EX_TRANSPARENT) {
SetBkMode(reinterpret_cast<HDC>(wParam), TRANSPARENT);
return reinterpret_cast<INT_PTR>(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 -= 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: {
// 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);
}
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));
}
}
}
+33
View File
@@ -0,0 +1,33 @@
// Application class header
namespace watchfuleye {
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:
Application(const char* appName, const char* appVersion, unsigned int maximumMinutes, const char* driveToDetect);
void run();
private:
static BOOL CALLBACK mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
void createTrayIcon();
void queryThreadFunction();
std::string m_appName, m_appVersion;
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;
};
}
+24
View File
@@ -0,0 +1,24 @@
// Watchful Eye main file
#include "pch.h"
#include "version.h"
#include "Application.h"
// Enable visual styles
#pragma comment(linker, "\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define APPNAME "Watchful Eye"
#define MAX_MINUTES 1
#define DRIVE_TO_DETECT "E:"
int main(int argc, char* argv[]) {
{
watchfuleye::Application app(APPNAME, APPVERSION, MAX_MINUTES, DRIVE_TO_DETECT);
app.run();
}
return EXIT_SUCCESS;
}
+13
View File
@@ -0,0 +1,13 @@
// Watchful Eye precompiled header
// STD
#include <iostream>
#include <string>
#include <format>
#include <chrono>
#include <thread>
// Windows
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shellapi.h>
+17
View File
@@ -0,0 +1,17 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by C:\Users\Grayson\Documents\Visual Studio 18\Solutions\watchful-eye\WatchfulEye\resource.rc
//
#define IDD_DIALOGMAIN 101
#define IDC_STATICLOWER 1000
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
+2
View File
@@ -0,0 +1,2 @@
// Version configured header file
#define APPVERSION "@APPVERSION@"