Compare commits

..

No commits in common. "stable" and "v0.0.1" have entirely different histories.

12 changed files with 0 additions and 651 deletions

3
.gitignore vendored
View File

@ -1,3 +0,0 @@
.vs/
build/
*.aps

View File

@ -1,6 +0,0 @@
# Watchful Eye project CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(watchful-eye)
add_subdirectory(WatchfulEye)

View File

@ -1,49 +0,0 @@
// 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"
}
]
}

View File

@ -1,17 +0,0 @@
# 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")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

View File

@ -1,106 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#include "src/resource.h"
#include "version.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
/////////////////////////////////////////////////////////////////////////////
//
// 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
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -1,369 +0,0 @@
// Application class implementation
#include "pch.h"
#include "Application.h"
#include "resource.h"
#define WINDOW_WIDTH 450
#define WINDOW_HEIGHT 185
#define WINDOW_STYLE WS_POPUP
#define EX_WINDOW_STYLE WS_EX_TOOLWINDOW
#define WM_TRAYICON WM_USER // Tray icon window message
#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
// Enable visual styles
#pragma comment(linker, "\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
namespace watchfuleye {
Application::Application(const char* appName, const char* appVersion, unsigned int maximumMinutes, const char* driveToDetect)
: m_appName(appName)
, m_appVersion(appVersion)
, m_window(nullptr)
, m_running(true)
, m_active(false)
, m_overTime(false)
, m_maxMinutes(maximumMinutes)
, m_drive(driveToDetect)
, m_paintCounter(0)
, m_startTime()
, m_taskbarCreatedMessage(-1)
, m_region(nullptr)
, m_timeFont(nullptr)
, m_lowerFont(nullptr)
, m_greenBrush(nullptr)
, m_yellowBrush(nullptr)
, m_redBrush(nullptr)
, m_outlinePen(nullptr)
, m_offscreenDC(nullptr)
, m_offscreenBitmap(nullptr)
{
std::cout << std::format("{} {}\n", m_appName, m_appVersion);
}
void Application::run() {
// Create window
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); // Don't mess with the window size
WNDCLASS wc = {};
std::string windowClassName = "WatchfulEyeWindow";
wc.lpszClassName = windowClassName.c_str();
wc.hCursor = LoadCursor(NULL, IDC_SIZEALL);
wc.lpfnWndProc = wndProc;
RegisterClass(&wc);
m_window = CreateWindowEx(EX_WINDOW_STYLE, windowClassName.c_str(), m_appName.c_str(), WINDOW_STYLE, 0, 0, 0, 0, nullptr, nullptr, nullptr, this);
// Create tray icon
createTrayIcon();
// Create drive query thread
std::thread queryThread(&Application::queryThreadFunction, this);
ShowWindow(m_window, SW_SHOW);
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
m_running = false;
queryThread.join();
}
LRESULT CALLBACK Application::wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static Application* app = nullptr;
switch (msg) {
case WM_CREATE: {
app = reinterpret_cast<Application*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
app->sizeWindow(hWnd);
// Round the window's corners
app->m_region = CreateRoundRectRgn(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 20, 20);
SetWindowRgn(hWnd, app->m_region, TRUE);
// 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);
// And set topmost flag
SetWindowPos(hWnd, HWND_TOPMOST, cornerX - 500, cornerY - 400, 0, 0, SWP_NOSIZE);
app->m_taskbarCreatedMessage = RegisterWindowMessage("TaskbarCreated");
// Set up UI
LOGFONT lFont = {};
strcpy(lFont.lfFaceName, "Consolas");
lFont.lfHeight = 35;
app->m_lowerFont = CreateFontIndirect(&lFont);
lFont.lfHeight = 180;
lFont.lfWeight = FW_BOLD;
app->m_timeFont = CreateFontIndirect(&lFont);
app->m_greenBrush = CreateSolidBrush(RGB(150, 255, 150));
app->m_yellowBrush = CreateSolidBrush(RGB(255, 255, 150));
app->m_redBrush = CreateSolidBrush(RGB(255, 150, 150));
app->m_outlinePen = CreatePen(PS_SOLID, 5, RGB(0, 0, 0));
// App will update (repaint) at 5 FPS
SendMessage(hWnd, WM_UPDATE, NULL, NULL);
SetTimer(hWnd, IDT_UPDATE, 200, nullptr);
return 0;
}
case WM_DPICHANGED: { // Prevents the window from resizing because of DPI changes
RECT* suggestedRECT = reinterpret_cast<RECT*>(lParam);
app->sizeWindow(hWnd, suggestedRECT->left, suggestedRECT->top);
return 0;
}
case WM_NCHITTEST: { // Allows the window to be moved by dragging from anywhere within it
LRESULT result = DefWindowProc(hWnd, msg, wParam, lParam);
if (result == HTCLIENT)
return HTCAPTION;
return result;
}
case WM_SETCURSOR: { // Allows the cursor to change with the above WM_NCHITTEST code
if (LOWORD(lParam) == HTCAPTION)
lParam = MAKELPARAM(HTCLIENT, HIWORD(lParam));
break;
}
case WM_TRAYICON: {
if (LOWORD(lParam) == WM_LBUTTONDOWN ||
LOWORD(lParam) == WM_RBUTTONDOWN) {
SetForegroundWindow(hWnd);
POINT cursor = {};
GetCursorPos(&cursor);
HMENU popupMenu = CreatePopupMenu();
AppendMenu(popupMenu, MF_STRING, IDM_ABOUT, "About");
AppendMenu(popupMenu, MF_STRING, IDM_EXIT, "Exit");
TrackPopupMenu(popupMenu, NULL, cursor.x, cursor.y, NULL, hWnd, nullptr);
DestroyMenu(popupMenu);
return 0;
}
break;
}
case WM_COMMAND: {
if (HIWORD(wParam) == 0 && LOWORD(wParam) == IDM_ABOUT) {
MessageBox(hWnd, 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"
"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);
return 0;
}
else if (HIWORD(wParam) == 0 && LOWORD(wParam) == IDM_EXIT) {
DestroyWindow(hWnd);
return 0;
}
break;
}
case WM_TIMER: {
if (wParam == IDT_UPDATE) {
SendMessage(hWnd, WM_UPDATE, NULL, NULL);
return 0;
}
break;
}
case WM_UPDATE: {
InvalidateRect(hWnd, nullptr, TRUE);
UpdateWindow(hWnd);
return 0;
}
case WM_ERASEBKGND: { // Don't erase the background, it is already done in WM_PAINT
return 1;
}
case WM_PAINT: {
PAINTSTRUCT ps = {};
HDC hDC = BeginPaint(hWnd, &ps);
RECT clientRect = {};
GetClientRect(hWnd, &clientRect);
// Create offscreen buffer if not already created
if (!app->m_offscreenDC) {
app->m_offscreenDC = CreateCompatibleDC(hDC);
app->m_offscreenBitmap = CreateCompatibleBitmap(hDC, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
}
// Setup RECTs
RECT timeRect = clientRect;
timeRect.top -= 25, timeRect.bottom -= 25;
RECT lowerRect = { .top = WINDOW_HEIGHT - 65, .right = WINDOW_WIDTH, .bottom = WINDOW_HEIGHT };
HANDLE prevBitmap = SelectObject(app->m_offscreenDC, app->m_offscreenBitmap);
// Fill background
HBRUSH backgroundBrush = nullptr;
if (!app->m_active)
backgroundBrush = app->m_greenBrush;
else if (app->m_overTime)
backgroundBrush = app->m_redBrush;
else
backgroundBrush = app->m_yellowBrush;
FillRect(app->m_offscreenDC, &clientRect, backgroundBrush);
// Draw outline
SelectObject(app->m_offscreenDC, GetStockObject(NULL_BRUSH));
SelectObject(app->m_offscreenDC, app->m_outlinePen);
RoundRect(app->m_offscreenDC, 1, 1, WINDOW_WIDTH - 2, WINDOW_HEIGHT - 2, 20, 20);
// Setup text options
SetTextColor(app->m_offscreenDC, RGB(0, 0, 0));
SetBkMode(app->m_offscreenDC, TRANSPARENT);
// Calculate time
Seconds elapsed = Duration(Clock::now() - app->m_startTime).count();
Seconds remaining = app->m_maxMinutes * 60;
if (app->m_active)
remaining -= elapsed;
if (!app->m_active || (app->m_active && !app->m_overTime)) { // Draw time
SelectObject(app->m_offscreenDC, app->m_timeFont);
unsigned int minutes = remaining % (3600) / 60, seconds = remaining % 60;
DrawText(app->m_offscreenDC, std::format("{:02}:{:02}", minutes, seconds).c_str(), -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
if (!app->m_active) { // Ready (green)
SelectObject(app->m_offscreenDC, app->m_lowerFont);
DrawText(app->m_offscreenDC, "Ready", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
else if (!app->m_overTime) { // Counting (yellow)
SelectObject(app->m_offscreenDC, app->m_lowerFont);
DrawText(app->m_offscreenDC, "Time Remaining on Tester", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
else { // Over time (red)
SelectObject(app->m_offscreenDC, app->m_lowerFont);
DrawText(app->m_offscreenDC, "Please Eject Your USB", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
if (app->m_paintCounter % 5 > 1) {
SelectObject(app->m_offscreenDC, app->m_timeFont);
DrawText(app->m_offscreenDC, "STOP", -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
}
if (remaining <= 0)
app->m_overTime = true;
BitBlt(hDC, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, app->m_offscreenDC, 0, 0, SRCCOPY);
SelectObject(app->m_offscreenDC, prevBitmap);
EndPaint(hWnd, &ps);
app->m_paintCounter++;
return 0;
}
case WM_CLOSE: {
// Do nothing
return 0;
}
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
}
if (app && msg == app->m_taskbarCreatedMessage) {
app->createTrayIcon();
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
void Application::sizeWindow(const HWND& hWnd, int newXPos, int newYPos) {
RECT clientRect = { .right = WINDOW_WIDTH, .bottom = WINDOW_HEIGHT };
AdjustWindowRectExForDpi(&clientRect, WINDOW_STYLE, FALSE, EX_WINDOW_STYLE, GetDpiForWindow(hWnd));
SetWindowPos(hWnd, nullptr, newXPos, newYPos, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, SWP_NOZORDER);
}
void Application::createTrayIcon() {
NOTIFYICONDATA iconData = {};
iconData.cbSize = sizeof(iconData);
iconData.uVersion = NOTIFYICON_VERSION_4;
iconData.hWnd = m_window;
iconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
iconData.uCallbackMessage = WM_TRAYICON;
iconData.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN));
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));
}
}
Application::~Application() {
DeleteObject(m_region);
DeleteObject(m_timeFont);
DeleteObject(m_lowerFont);
DeleteObject(m_greenBrush);
DeleteObject(m_yellowBrush);
DeleteObject(m_redBrush);
DeleteObject(m_offscreenBitmap);
DeleteDC(m_offscreenDC);
}
}

View File

@ -1,48 +0,0 @@
// 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();
~Application();
private:
static LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void sizeWindow(const HWND& hWnd, int newXPos = 0, int newYPos = 0);
void createTrayIcon();
void queryThreadFunction();
std::string m_appName, m_appVersion;
HWND m_window;
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;
uint8_t m_paintCounter;
TimePoint m_startTime;
UINT m_taskbarCreatedMessage;
HRGN m_region;
HFONT m_timeFont, m_lowerFont;
HBRUSH m_greenBrush, m_yellowBrush, m_redBrush;
HPEN m_outlinePen;
HDC m_offscreenDC;
HBITMAP m_offscreenBitmap;
};
}

View File

@ -1,22 +0,0 @@
// Watchful Eye main file
#include "pch.h"
#include "version.h"
#include "Application.h"
#define APPNAME "Watchful Eye"
// ======== Configuration ========
#define MAX_MINUTES 15
#define DRIVE_TO_DETECT "D:"
// ===============================
int main(int argc, char* argv[]) {
{
watchfuleye::Application app(APPNAME, APPVERSION, MAX_MINUTES, DRIVE_TO_DETECT);
app.run();
}
return EXIT_SUCCESS;
}

View File

@ -1,13 +0,0 @@
// 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>

View File

@ -1,16 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by C:\Users\Grayson\Documents\Visual Studio 18\Solutions\watchful-eye\WatchfulEye\resource.rc
//
#define IDI_ICONMAIN 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -1,2 +0,0 @@
// Version configured header file
#define APPVERSION "@APPVERSION@"