From 329e7111fc54eb91808af74ef7fcefc47171125a Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 04:17:40 -0400
Subject: [PATCH 01/22] Create StayAwakeCore Class. Add SimulateInput function.
---
StayAwake/StayAwake.vcxproj | 3 +-
StayAwake/StayAwake.vcxproj.filters | 9 ++++--
StayAwake/StayAwakeCore.cpp | 49 +++++++++++++++++++++++++++++
StayAwake/StayAwakeCore.h | 37 ++++++++++++++++++++++
StayAwake/StayAwakeDlg.cpp | 44 +-------------------------
StayAwake/StayAwakeDlg.h | 22 +------------
6 files changed, 96 insertions(+), 68 deletions(-)
create mode 100644 StayAwake/StayAwakeCore.cpp
create mode 100644 StayAwake/StayAwakeCore.h
diff --git a/StayAwake/StayAwake.vcxproj b/StayAwake/StayAwake.vcxproj
index c9cac35..23c26c9 100644
--- a/StayAwake/StayAwake.vcxproj
+++ b/StayAwake/StayAwake.vcxproj
@@ -212,8 +212,8 @@
-
+
@@ -224,6 +224,7 @@
+
Create
diff --git a/StayAwake/StayAwake.vcxproj.filters b/StayAwake/StayAwake.vcxproj.filters
index 20383b7..856a3d4 100644
--- a/StayAwake/StayAwake.vcxproj.filters
+++ b/StayAwake/StayAwake.vcxproj.filters
@@ -15,9 +15,6 @@
-
- Header Files
-
Header Files
@@ -42,6 +39,9 @@
Header Files
+
+ Header Files
+
@@ -62,6 +62,9 @@
Source Files
+
+ Source Files
+
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
new file mode 100644
index 0000000..5685489
--- /dev/null
+++ b/StayAwake/StayAwakeCore.cpp
@@ -0,0 +1,49 @@
+#include "pch.h"
+#include "StayAwakeCore.h"
+
+void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
+{
+ switch (inputCode)
+ {
+ case 1:
+ inputName = L"Volume Down && Up";
+ keybd_event(VK_VOLUME_DOWN, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+ keybd_event(VK_VOLUME_DOWN, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ Sleep(10);
+ keybd_event(VK_VOLUME_UP, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+ keybd_event(VK_VOLUME_UP, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ {
+ inputName = L"Unassigned Key #" + to_wstring(inputCode - 1);
+ BYTE keycode{ static_cast(VK_UNASSIGNED_01 + inputCode - 2) };
+ keybd_event(keycode, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+ keybd_event(keycode, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ break;
+ }
+
+ case 11:
+ inputName = L"Unassigned Key #10";
+ keybd_event(VK_UNASSIGNED_10, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+ keybd_event(VK_UNASSIGNED_10, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ break;
+
+ default:
+ inputName = L"Scroll Lock cycling";
+ keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+ keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ Sleep(10);
+ keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+ keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ break;
+ }
+}
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
new file mode 100644
index 0000000..fe4d7d1
--- /dev/null
+++ b/StayAwake/StayAwakeCore.h
@@ -0,0 +1,37 @@
+#pragma once
+#include
+#include
+
+constexpr auto VK_UNASSIGNED_01 = 0x97;
+constexpr auto VK_UNASSIGNED_10 = 0xE8;
+
+constexpr auto PREF_INI_FILE = L"StayAwake.ini";
+constexpr auto PREF_DEFAULTS = L"Defaults";
+constexpr auto PREF_LEGACY_KEYCODE = L"AwakeKeyCode";
+constexpr auto PREF_SELECTED_KEYCODES = L"SelectedKeyCodes";
+constexpr auto PREF_AWAKE_PAUSED = L"AwakePaused";
+constexpr auto PREF_START_MINIMIZED = L"StartMinimized";
+constexpr auto PREF_MULTI_INSTANCE = L"MultipleInstancesAllowed";
+constexpr auto PREF_INTERVAL_LEGACY = L"TimerIntervalInSeconds";
+constexpr auto PREF_INTERVAL_MINIMUM = L"MinimumIntervalInSeconds";
+constexpr auto PREF_INTERVAL_MAXIMUM = L"MaximumIntervalInSeconds";
+
+constexpr auto MIN_PERIOD{ 10 };
+constexpr auto MAX_PERIOD{ 9990 };
+constexpr auto DEF_PERIOD{ 240 };
+
+constexpr auto LEN_ROSTER_KEYCODES{ 12 };
+constexpr auto DEF_SELECTED_KEYCODES = L"001111111111";
+
+
+using std::string;
+using std::wstring;
+using std::to_wstring;
+
+
+class StayAwakeCore
+{
+public:
+ static void SimulateInput(int inputCode, wstring& inputName);
+};
+
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index 55ff507..ad26d92 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -379,49 +379,7 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
UINT nAwakeKeyCode{ m_RosterKeyCodes[rand() % m_RosterLength] };
wstring sAwakeKeyCode{};
- switch (nAwakeKeyCode)
- {
- case 1:
- sAwakeKeyCode = L"Volume Down && Up";
- keybd_event(VK_VOLUME_DOWN, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_VOLUME_DOWN, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- Sleep(10);
- keybd_event(VK_VOLUME_UP, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_VOLUME_UP, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- break;
-
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- case 9:
- case 10:
- {
- sAwakeKeyCode = L"Unassigned Key #" + to_wstring(nAwakeKeyCode - 1);
- BYTE keycode{ static_cast(VK_UNASSIGNED_01 + nAwakeKeyCode - 2) };
- keybd_event(keycode, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(keycode, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- break;
- }
-
- case 11:
- sAwakeKeyCode = L"Unassigned Key #10";
- keybd_event(VK_UNASSIGNED_10, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_UNASSIGNED_10, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- break;
-
- default:
- sAwakeKeyCode = L"Scroll Lock cycling";
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- Sleep(10);
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- break;
- }
+ StayAwakeCore::SimulateInput(nAwakeKeyCode, sAwakeKeyCode);
SYSTEMTIME lastTime{};
GetLocalTime(&lastTime);
diff --git a/StayAwake/StayAwakeDlg.h b/StayAwake/StayAwakeDlg.h
index 7d453d5..d5d83f5 100644
--- a/StayAwake/StayAwakeDlg.h
+++ b/StayAwake/StayAwakeDlg.h
@@ -1,35 +1,15 @@
#pragma once
+#include "StayAwakeCore.h"
#include "Utils.h"
#define WM_POST_OPEN (WM_APP + 1)
#define WM_TRAY_NOTIFY (WM_APP + 2)
#define WM_RESTORE_DIALOG theApp.WM_SHOWFIRSTINSTANCE
-constexpr auto VK_UNASSIGNED_01 = 0x97;
-constexpr auto VK_UNASSIGNED_10 = 0xE8;
-
-constexpr auto PREF_INI_FILE = L"StayAwake.ini";
-constexpr auto PREF_DEFAULTS = L"Defaults";
-constexpr auto PREF_LEGACY_KEYCODE = L"AwakeKeyCode";
-constexpr auto PREF_SELECTED_KEYCODES = L"SelectedKeyCodes";
-constexpr auto PREF_AWAKE_PAUSED = L"AwakePaused";
-constexpr auto PREF_START_MINIMIZED = L"StartMinimized";
-constexpr auto PREF_MULTI_INSTANCE = L"MultipleInstancesAllowed";
-constexpr auto PREF_INTERVAL_LEGACY = L"TimerIntervalInSeconds";
-constexpr auto PREF_INTERVAL_MINIMUM = L"MinimumIntervalInSeconds";
-constexpr auto PREF_INTERVAL_MAXIMUM = L"MaximumIntervalInSeconds";
-
constexpr auto BTN_TEXT_PAUSE = L"&Pause";
constexpr auto BTN_TEXT_RESUME = L"&Resume";
-constexpr auto MIN_PERIOD{ 10 };
-constexpr auto MAX_PERIOD{ 9990 };
-constexpr auto DEF_PERIOD{ 240 };
-
-constexpr auto LEN_ROSTER_KEYCODES{ 12 };
-constexpr auto DEF_SELECTED_KEYCODES = L"001111111111";
-
const wstring MIN_MAX_PERIOD = to_wstring(MIN_PERIOD) + L" and " + to_wstring(MAX_PERIOD);
const wstring INTERVAL_TOOLTIP = L"Number between " + MIN_MAX_PERIOD;
const wstring INTERVAL_WARNING = L"Please enter a value between " + MIN_MAX_PERIOD;
From a7f6fc81174d42439300e538f7e624d471167446 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 05:35:40 -0400
Subject: [PATCH 02/22] Replace keybd_event (deprecated) with SendInput
DRY code with functions PressOneKey and PressTwoKeys
---
StayAwake/StayAwakeCore.cpp | 67 ++++++++++++++++++++++++++++---------
StayAwake/StayAwakeCore.h | 4 +++
2 files changed, 56 insertions(+), 15 deletions(-)
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index 5685489..673b036 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -7,11 +7,7 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
{
case 1:
inputName = L"Volume Down && Up";
- keybd_event(VK_VOLUME_DOWN, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_VOLUME_DOWN, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- Sleep(10);
- keybd_event(VK_VOLUME_UP, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_VOLUME_UP, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ PressTwoKeys(VK_VOLUME_DOWN, VK_VOLUME_UP, true);
break;
case 2:
@@ -25,25 +21,66 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
case 10:
{
inputName = L"Unassigned Key #" + to_wstring(inputCode - 1);
- BYTE keycode{ static_cast(VK_UNASSIGNED_01 + inputCode - 2) };
- keybd_event(keycode, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(keycode, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ PressOneKey(static_cast(VK_UNASSIGNED_01 + inputCode - 2), false);
break;
}
case 11:
inputName = L"Unassigned Key #10";
- keybd_event(VK_UNASSIGNED_10, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_UNASSIGNED_10, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ PressOneKey(VK_UNASSIGNED_10, false);
break;
default:
inputName = L"Scroll Lock cycling";
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
- Sleep(10);
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
- keybd_event(VK_SCROLL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ PressTwoKeys(VK_SCROLL, VK_SCROLL, false);
break;
}
}
+
+void StayAwakeCore::PressOneKey(BYTE vk, bool extended)
+{
+ INPUT input[2] = {};
+
+ auto flags = (extended) ? KEYEVENTF_EXTENDEDKEY : 0;
+
+ input[0].type = INPUT_KEYBOARD;
+ input[0].ki.wVk = vk;
+ input[0].ki.wScan = 0;
+ input[0].ki.dwFlags = flags;
+
+ input[1].type = INPUT_KEYBOARD;
+ input[1].ki.wVk = vk;
+ input[1].ki.wScan = 0;
+ input[1].ki.dwFlags = KEYEVENTF_KEYUP | flags;
+
+ SendInput(2, input, sizeof(INPUT));
+}
+
+void StayAwakeCore::PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended)
+{
+ INPUT input[4] = {};
+
+ auto flags = (extended) ? KEYEVENTF_EXTENDEDKEY : 0;
+
+ input[0].type = INPUT_KEYBOARD;
+ input[0].ki.wVk = vkFirst;
+ input[0].ki.wScan = 0;
+ input[0].ki.dwFlags = flags;
+
+ input[1].type = INPUT_KEYBOARD;
+ input[1].ki.wVk = vkFirst;
+ input[1].ki.wScan = 0;
+ input[1].ki.dwFlags = KEYEVENTF_KEYUP | flags;
+
+ input[2].type = INPUT_KEYBOARD;
+ input[2].ki.wVk = vkSecond;
+ input[2].ki.wScan = 0;
+ input[2].ki.dwFlags = flags;
+
+ input[3].type = INPUT_KEYBOARD;
+ input[3].ki.wVk = vkSecond;
+ input[3].ki.wScan = 0;
+ input[3].ki.dwFlags = KEYEVENTF_KEYUP | flags;
+
+ SendInput(4, input, sizeof(INPUT));
+}
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index fe4d7d1..bd2cd51 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -33,5 +33,9 @@ class StayAwakeCore
{
public:
static void SimulateInput(int inputCode, wstring& inputName);
+
+private:
+ static void PressOneKey(BYTE vk, bool extended = false);
+ static void PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended = false);
};
From 537c56dd77b6a477100f5506d46544665f835148 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 06:00:37 -0400
Subject: [PATCH 03/22] DEBUG_DISPLAY_IDLE_TICKS using
GetIdleTimeMilliseconds()
---
StayAwake/StayAwakeCore.cpp | 11 +++++++++++
StayAwake/StayAwakeCore.h | 1 +
StayAwake/StayAwakeDlg.cpp | 7 +++++++
StayAwake/StayAwakeDlg.h | 2 ++
4 files changed, 21 insertions(+)
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index 673b036..9bbf671 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -37,6 +37,17 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
}
}
+ULONGLONG StayAwakeCore::GetIdleTimeMilliseconds()
+{
+ LASTINPUTINFO lii = {};
+ lii.cbSize = sizeof(LASTINPUTINFO);
+
+ if (!GetLastInputInfo(&lii))
+ return 0; // error case
+
+ return GetTickCount64() - lii.dwTime;
+}
+
void StayAwakeCore::PressOneKey(BYTE vk, bool extended)
{
INPUT input[2] = {};
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index bd2cd51..a535ac0 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -33,6 +33,7 @@ class StayAwakeCore
{
public:
static void SimulateInput(int inputCode, wstring& inputName);
+ static ULONGLONG GetIdleTimeMilliseconds();
private:
static void PressOneKey(BYTE vk, bool extended = false);
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index ad26d92..9d47a46 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -384,7 +384,14 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
SYSTEMTIME lastTime{};
GetLocalTime(&lastTime);
SetDlgItemText(IDC_STAYAWAKE_LAST_EVENT, Utils::formatSystemTime(lastTime, L"Last StayAwake event").c_str());
+
+#ifdef DEBUG_DISPLAY_IDLE_TICKS
+ Sleep((rand() % 5) + 1); // simulate a small delay to get a more accurate idle time
+ ULONGLONG idleTime = StayAwakeCore::GetIdleTimeMilliseconds();
+ SetDlgItemText(IDC_STAYAWAKE_LAST_KEYCODE, (L"[" + sAwakeKeyCode + L":" + to_wstring(idleTime) + L" ms]").c_str());
+#else
SetDlgItemText(IDC_STAYAWAKE_LAST_KEYCODE, (L"[" + sAwakeKeyCode + L"]").c_str());
+#endif
UINT nTimerSeconds{ m_IntervalMinSeconds };
if (m_IntervalMinSeconds != m_IntervalMaxSeconds)
diff --git a/StayAwake/StayAwakeDlg.h b/StayAwake/StayAwakeDlg.h
index d5d83f5..d697a4a 100644
--- a/StayAwake/StayAwakeDlg.h
+++ b/StayAwake/StayAwakeDlg.h
@@ -7,6 +7,8 @@
#define WM_TRAY_NOTIFY (WM_APP + 2)
#define WM_RESTORE_DIALOG theApp.WM_SHOWFIRSTINSTANCE
+#define DEBUG_DISPLAY_IDLE_TICKS
+
constexpr auto BTN_TEXT_PAUSE = L"&Pause";
constexpr auto BTN_TEXT_RESUME = L"&Resume";
From 1ba30aad72e5d67cb4fd8dd7dfbefc6b7c0db3b6 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 06:50:17 -0400
Subject: [PATCH 04/22] Add simulations for F13...F24 keys. Make Scroll Lock
non-default.
---
StayAwake/StayAwakeCore.cpp | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index 9bbf671..d7db4c5 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -5,6 +5,11 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
{
switch (inputCode)
{
+ case 0:
+ inputName = L"Scroll Lock cycling";
+ PressTwoKeys(VK_SCROLL, VK_SCROLL, false);
+ break;
+
case 1:
inputName = L"Volume Down && Up";
PressTwoKeys(VK_VOLUME_DOWN, VK_VOLUME_UP, true);
@@ -30,9 +35,26 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
PressOneKey(VK_UNASSIGNED_10, false);
break;
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ {
+ inputName = L"F" + to_wstring(inputCode);
+ PressOneKey(static_cast(VK_F13 + inputCode - 13), false);
+ break;
+ }
+
default:
- inputName = L"Scroll Lock cycling";
- PressTwoKeys(VK_SCROLL, VK_SCROLL, false);
+ inputName = L"Invalid Awake Key";
break;
}
}
From e706a6822942c6493295f1be96a6c0f70c9f7646 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 07:23:23 -0400
Subject: [PATCH 05/22] Add MouseMove & MouseMoveZero simulations
---
StayAwake/StayAwakeCore.cpp | 38 +++++++++++++++++++++++++++++++++++++
StayAwake/StayAwakeCore.h | 2 ++
StayAwake/StayAwakeDlg.cpp | 2 +-
3 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index d7db4c5..a5c51b0 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -53,6 +53,12 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
break;
}
+ case 25:
+ inputName = L"Invisible Mouse Move";
+ MouseMove();
+ break;
+
+
default:
inputName = L"Invalid Awake Key";
break;
@@ -117,3 +123,35 @@ void StayAwakeCore::PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended)
SendInput(4, input, sizeof(INPUT));
}
+
+void StayAwakeCore::MouseMove()
+{
+ INPUT input[2] = {};
+
+ // Tiny move
+ input[0].type = INPUT_MOUSE;
+ input[0].mi.dx = 1;
+ input[0].mi.dy = 1;
+ input[0].mi.dwFlags = MOUSEEVENTF_MOVE;
+
+ // Move back
+ input[1].type = INPUT_MOUSE;
+ input[1].mi.dx = -1;
+ input[1].mi.dy = -1;
+ input[1].mi.dwFlags = MOUSEEVENTF_MOVE;
+
+ SendInput(2, input, sizeof(INPUT));
+}
+
+void StayAwakeCore::MouseMoveZero()
+{
+ INPUT input[1] = {};
+
+ // Zero move
+ input[0].type = INPUT_MOUSE;
+ input[0].mi.dx = 0;
+ input[0].mi.dy = 0;
+ input[0].mi.dwFlags = MOUSEEVENTF_MOVE;
+
+ SendInput(1, input, sizeof(INPUT));
+}
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index a535ac0..fbf45f5 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -38,5 +38,7 @@ class StayAwakeCore
private:
static void PressOneKey(BYTE vk, bool extended = false);
static void PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended = false);
+ static void MouseMove();
+ static void MouseMoveZero();
};
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index 9d47a46..82d7be6 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -386,7 +386,7 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
SetDlgItemText(IDC_STAYAWAKE_LAST_EVENT, Utils::formatSystemTime(lastTime, L"Last StayAwake event").c_str());
#ifdef DEBUG_DISPLAY_IDLE_TICKS
- Sleep((rand() % 5) + 1); // simulate a small delay to get a more accurate idle time
+ Sleep((rand() % 20) + 1); // simulate a small delay to get a more accurate idle time
ULONGLONG idleTime = StayAwakeCore::GetIdleTimeMilliseconds();
SetDlgItemText(IDC_STAYAWAKE_LAST_KEYCODE, (L"[" + sAwakeKeyCode + L":" + to_wstring(idleTime) + L" ms]").c_str());
#else
From 50f583db16c4f9c3d754b7d400956b7a1bf4131b Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 12:22:03 -0400
Subject: [PATCH 06/22] Refactor StayAwakeCore as an instantiable class
Refactor StayAwakeCore from static utility to instantiable class
- Remove static modifier from member SimulateInput()
- Add explicit default constructor and destructor
- Change calls to StayAwakeCore methods using instance variable
---
StayAwake/StayAwakeCore.cpp | 4 ----
StayAwake/StayAwakeCore.h | 5 ++++-
StayAwake/StayAwakeDlg.cpp | 4 ++--
StayAwake/StayAwakeDlg.h | 1 +
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index a5c51b0..06020ef 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -24,11 +24,9 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
case 8:
case 9:
case 10:
- {
inputName = L"Unassigned Key #" + to_wstring(inputCode - 1);
PressOneKey(static_cast(VK_UNASSIGNED_01 + inputCode - 2), false);
break;
- }
case 11:
inputName = L"Unassigned Key #10";
@@ -47,11 +45,9 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
case 22:
case 23:
case 24:
- {
inputName = L"F" + to_wstring(inputCode);
PressOneKey(static_cast(VK_F13 + inputCode - 13), false);
break;
- }
case 25:
inputName = L"Invisible Mouse Move";
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index fbf45f5..daada64 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -32,7 +32,10 @@ using std::to_wstring;
class StayAwakeCore
{
public:
- static void SimulateInput(int inputCode, wstring& inputName);
+ StayAwakeCore() {};
+ ~StayAwakeCore() {};
+
+ void SimulateInput(int inputCode, wstring& inputName);
static ULONGLONG GetIdleTimeMilliseconds();
private:
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index 82d7be6..b981ff0 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -379,7 +379,7 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
UINT nAwakeKeyCode{ m_RosterKeyCodes[rand() % m_RosterLength] };
wstring sAwakeKeyCode{};
- StayAwakeCore::SimulateInput(nAwakeKeyCode, sAwakeKeyCode);
+ m_AwakeCore.SimulateInput(nAwakeKeyCode, sAwakeKeyCode);
SYSTEMTIME lastTime{};
GetLocalTime(&lastTime);
@@ -387,7 +387,7 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
#ifdef DEBUG_DISPLAY_IDLE_TICKS
Sleep((rand() % 20) + 1); // simulate a small delay to get a more accurate idle time
- ULONGLONG idleTime = StayAwakeCore::GetIdleTimeMilliseconds();
+ ULONGLONG idleTime = m_AwakeCore.GetIdleTimeMilliseconds();
SetDlgItemText(IDC_STAYAWAKE_LAST_KEYCODE, (L"[" + sAwakeKeyCode + L":" + to_wstring(idleTime) + L" ms]").c_str());
#else
SetDlgItemText(IDC_STAYAWAKE_LAST_KEYCODE, (L"[" + sAwakeKeyCode + L"]").c_str());
diff --git a/StayAwake/StayAwakeDlg.h b/StayAwake/StayAwakeDlg.h
index d697a4a..815444a 100644
--- a/StayAwake/StayAwakeDlg.h
+++ b/StayAwake/StayAwakeDlg.h
@@ -53,6 +53,7 @@ class CStayAwakeDlg : public CDialogEx
UINT m_IntervalMinSeconds{};
UINT m_IntervalMaxSeconds{};
+ StayAwakeCore m_AwakeCore;
NOTIFYICONDATA m_TrayData{};
CMenu m_menu;
From 61891427a5c7bee330c4b4ddf01bd15cff673043 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 12:33:46 -0400
Subject: [PATCH 07/22] Preserve Volume Muted state with Volume Down & Up
simulations
---
StayAwake/StayAwakeCore.cpp | 81 ++++++++++++++++++++++++++++++++++++-
StayAwake/StayAwakeCore.h | 10 +++++
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index 06020ef..26ccd5e 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -12,7 +12,10 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
case 1:
inputName = L"Volume Down && Up";
- PressTwoKeys(VK_VOLUME_DOWN, VK_VOLUME_UP, true);
+ if (GetMuteState())
+ PressThreeKeys(VK_VOLUME_DOWN, VK_VOLUME_UP, VK_VOLUME_MUTE, true);
+ else
+ PressTwoKeys(VK_VOLUME_DOWN, VK_VOLUME_UP, true);
break;
case 2:
@@ -120,6 +123,45 @@ void StayAwakeCore::PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended)
SendInput(4, input, sizeof(INPUT));
}
+void StayAwakeCore::PressThreeKeys(BYTE vkFirst, BYTE vkSecond, BYTE vkThird, bool extended)
+{
+ INPUT input[6] = {};
+
+ auto flags = (extended) ? KEYEVENTF_EXTENDEDKEY : 0;
+
+ input[0].type = INPUT_KEYBOARD;
+ input[0].ki.wVk = vkFirst;
+ input[0].ki.wScan = 0;
+ input[0].ki.dwFlags = flags;
+
+ input[1].type = INPUT_KEYBOARD;
+ input[1].ki.wVk = vkFirst;
+ input[1].ki.wScan = 0;
+ input[1].ki.dwFlags = KEYEVENTF_KEYUP | flags;
+
+ input[2].type = INPUT_KEYBOARD;
+ input[2].ki.wVk = vkSecond;
+ input[2].ki.wScan = 0;
+ input[2].ki.dwFlags = flags;
+
+ input[3].type = INPUT_KEYBOARD;
+ input[3].ki.wVk = vkSecond;
+ input[3].ki.wScan = 0;
+ input[3].ki.dwFlags = KEYEVENTF_KEYUP | flags;
+
+ input[4].type = INPUT_KEYBOARD;
+ input[4].ki.wVk = vkThird;
+ input[4].ki.wScan = 0;
+ input[4].ki.dwFlags = flags;
+
+ input[5].type = INPUT_KEYBOARD;
+ input[5].ki.wVk = vkThird;
+ input[5].ki.wScan = 0;
+ input[5].ki.dwFlags = KEYEVENTF_KEYUP | flags;
+
+ SendInput(6, input, sizeof(INPUT));
+}
+
void StayAwakeCore::MouseMove()
{
INPUT input[2] = {};
@@ -151,3 +193,40 @@ void StayAwakeCore::MouseMoveZero()
SendInput(1, input, sizeof(INPUT));
}
+
+bool StayAwakeCore::GetMuteState()
+{
+ HRESULT hr{};
+
+ hr = CoInitialize(NULL);
+ if (FAILED(hr)) return false;
+
+ IMMDeviceEnumerator* enumerator = nullptr;
+ IMMDevice* device = nullptr;
+ IAudioEndpointVolume* endpointVolume = nullptr;
+
+ hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
+ CLSCTX_INPROC_SERVER,
+ __uuidof(IMMDeviceEnumerator),
+ (void**)&enumerator);
+ if (FAILED(hr)) return false;
+
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+ if (FAILED(hr)) { enumerator->Release(); return false; }
+
+ hr = device->Activate(__uuidof(IAudioEndpointVolume),
+ CLSCTX_INPROC_SERVER, NULL,
+ (void**)&endpointVolume);
+ if (FAILED(hr)) {
+ device->Release();
+ enumerator->Release();
+ return false;
+ }
+
+ BOOL bMuted{};
+ hr = endpointVolume->GetMute(&bMuted);
+ if (FAILED(hr)) return false;
+
+ return (bMuted==TRUE);
+}
+
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index daada64..bba1a5f 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -1,7 +1,13 @@
#pragma once
+
+#include
+#include
#include
#include
+#pragma comment(lib, "Ole32.lib")
+
+
constexpr auto VK_UNASSIGNED_01 = 0x97;
constexpr auto VK_UNASSIGNED_10 = 0xE8;
@@ -41,7 +47,11 @@ class StayAwakeCore
private:
static void PressOneKey(BYTE vk, bool extended = false);
static void PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended = false);
+ static void PressThreeKeys(BYTE vkFirst, BYTE vkSecond, BYTE vkThird, bool extended = false);
+
static void MouseMove();
static void MouseMoveZero();
+
+ bool GetMuteState();
};
From cb0a1b270122a3a75dbc3243b2c9dffd15525f73 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 13:39:27 -0400
Subject: [PATCH 08/22] Suppress StayAwake simulations if Windows is in Locked
state
---
StayAwake/StayAwakeCore.h | 3 ++-
StayAwake/StayAwakeDlg.cpp | 26 ++++++++++++++++++++++++++
StayAwake/StayAwakeDlg.h | 3 ++-
3 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index bba1a5f..ef310f9 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -4,8 +4,9 @@
#include
#include
#include
+#include
-#pragma comment(lib, "Ole32.lib")
+#pragma comment(lib, "Wtsapi32.lib")
constexpr auto VK_UNASSIGNED_01 = 0x97;
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index b981ff0..4347b54 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -91,6 +91,7 @@ BEGIN_MESSAGE_MAP(CStayAwakeDlg, CDialogEx)
ON_BN_CLICKED(IDC_ABOUT_BUTTON, &CStayAwakeDlg::OnClickedAboutButton)
ON_BN_CLICKED(IDC_STAYAWAKE_PAUSE_RESUME_BTN, &CStayAwakeDlg::OnPauseResume)
ON_BN_CLICKED(IDC_START_MINIMIZED, &CStayAwakeDlg::OnStartMinimized)
+ ON_WM_WTSSESSION_CHANGE()
END_MESSAGE_MAP()
@@ -160,6 +161,8 @@ void CStayAwakeDlg::OnSysCommand(UINT nID, LPARAM lParam)
afx_msg LRESULT CStayAwakeDlg::OnPostOpen(WPARAM wParam, LPARAM lParam)
{
+ WTSRegisterSessionNotification(m_hWnd, NOTIFY_FOR_THIS_SESSION);
+
if (GetPreference(PREF_MULTI_INSTANCE, L"N") != L"Y" && Utils::getProcessRunCount(L"StayAwake.exe") > 1)
{
::PostMessage(HWND_BROADCAST, theApp.WM_SHOWFIRSTINSTANCE, 0, 0);
@@ -368,6 +371,7 @@ void CStayAwakeDlg::OnDestroy()
if (m_bMinimized) Shell_NotifyIcon(NIM_DELETE, &m_TrayData);
m_menu.DestroyMenu();
+ WTSUnRegisterSessionNotification(m_hWnd);
CDialogEx::OnDestroy();
}
@@ -376,6 +380,12 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
{
if (!m_RosterLength) InitRosterKeyCodes();
+ if (m_bSystemLocked)
+ {
+ SetDlgItemText(IDC_STAYAWAKE_NEXT_EVENT, L"PAUSED since Windows is LOCKED");
+ return;
+ }
+
UINT nAwakeKeyCode{ m_RosterKeyCodes[rand() % m_RosterLength] };
wstring sAwakeKeyCode{};
@@ -478,3 +488,19 @@ void CStayAwakeDlg::OnStartMinimized()
WritePrivateProfileString(PREF_DEFAULTS, PREF_START_MINIMIZED,
(IsDlgButtonChecked(IDC_START_MINIMIZED) == BST_CHECKED) ? L"Y" : L"N", m_IniFilePath);
}
+
+void CStayAwakeDlg::OnSessionChange(UINT nSessionState, UINT nId)
+{
+ switch (nSessionState)
+ {
+ case WTS_SESSION_LOCK:
+ m_bSystemLocked = true;
+ break;
+
+ case WTS_SESSION_UNLOCK:
+ m_bSystemLocked = false;
+ break;
+ }
+
+ CDialogEx::OnSessionChange(nSessionState, nId);
+}
diff --git a/StayAwake/StayAwakeDlg.h b/StayAwake/StayAwakeDlg.h
index 815444a..10a69c2 100644
--- a/StayAwake/StayAwakeDlg.h
+++ b/StayAwake/StayAwakeDlg.h
@@ -44,7 +44,7 @@ class CStayAwakeDlg : public CDialogEx
private:
wchar_t m_IniFilePath[MAX_PATH]{};
- bool m_bMinimized{};
+ bool m_bMinimized{}, m_bSystemLocked{};
UINT_PTR m_TimerID{42};
UINT m_RosterKeyCodes[LEN_ROSTER_KEYCODES]{};
@@ -74,6 +74,7 @@ class CStayAwakeDlg : public CDialogEx
afx_msg void OnExit() { DestroyWindow(); };
afx_msg void OnDestroy();
afx_msg void OnStartMinimized();
+ afx_msg void OnSessionChange(UINT nSessionState, UINT nId);
void InitConfigFilePath();
void InitIntervals();
From 01d4e089a484c90b56d286553ccb11bc4508608d Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 17:43:44 -0400
Subject: [PATCH 09/22] Select Inputs dialog UI: Add controls for FN keys &
mouse move
---
StayAwake/StayAwake.rc | 50 +++++++++++++++++++++++++-------------
StayAwake/resource.h | 55 +++++++++++++++++++++++++++---------------
2 files changed, 69 insertions(+), 36 deletions(-)
diff --git a/StayAwake/StayAwake.rc b/StayAwake/StayAwake.rc
index 7201241..e3dbf19 100644
--- a/StayAwake/StayAwake.rc
+++ b/StayAwake/StayAwake.rc
@@ -89,7 +89,7 @@ EXSTYLE WS_EX_APPWINDOW
CAPTION "StayAwake"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
- PUSHBUTTON "Select multiple &Key Codes for random simulation",IDC_STAYAWAKE_KEYS_ROSTER_BTN,13,12,179,14
+ PUSHBUTTON "Select multiple &options for random simulation",IDC_STAYAWAKE_KEYS_ROSTER_BTN,13,12,179,14
RTEXT "&Minimum Awake Seconds:",IDC_STATIC,1,35,100,8
EDITTEXT IDC_STAYAWAKE_INTERVAL_MIN,102,32,27,13,ES_NUMBER
RTEXT "Ma&ximum Awake Seconds:",IDC_STATIC,1,55,100,8
@@ -106,27 +106,43 @@ BEGIN
PUSHBUTTON "...",IDC_ABOUT_BUTTON,188,126,15,13,BS_BITMAP
END
-IDD_SELECT_KEYCODES_DIALOG DIALOGEX 0, 0, 180, 205
+IDD_SELECT_KEYCODES_DIALOG DIALOGEX 0, 0, 330, 205
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "Select multiple Key Codes"
+CAPTION "Select multiple simulation options"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
- CONTROL "&Scroll Lock cycling",IDC_KEY_SCROLL_LOCK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,15,90,10
+ CONTROL "&Scroll Lock cycling",IDC_KEY_SCROLL_LOCK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,15,90,10
CONTROL "&Volume Down && Up",IDC_KEY_VOLUME_UP_DOWN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,30,90,10
- CONTROL "Unassigned Key #&1",IDC_KEY_UNASSIGNED_1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,45,90,10
- CONTROL "Unassigned Key #&2",IDC_KEY_UNASSIGNED_2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,60,90,10
- CONTROL "Unassigned Key #&3",IDC_KEY_UNASSIGNED_3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,75,90,10
- CONTROL "Unassigned Key #&4",IDC_KEY_UNASSIGNED_4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,90,90,10
- CONTROL "Unassigned Key #&5",IDC_KEY_UNASSIGNED_5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,105,90,10
- CONTROL "Unassigned Key #&6",IDC_KEY_UNASSIGNED_6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,120,90,10
- CONTROL "Unassigned Key #&7",IDC_KEY_UNASSIGNED_7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,135,90,10
- CONTROL "Unassigned Key #&8",IDC_KEY_UNASSIGNED_8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,150,90,10
- CONTROL "Unassigned Key #&9",IDC_KEY_UNASSIGNED_9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,165,90,10
+ CONTROL "Unassigned Key #&1",IDC_KEY_UNASSIGNED_1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,45,90,10
+ CONTROL "Unassigned Key #&2",IDC_KEY_UNASSIGNED_2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,60,90,10
+ CONTROL "Unassigned Key #&3",IDC_KEY_UNASSIGNED_3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,75,90,10
+ CONTROL "Unassigned Key #&4",IDC_KEY_UNASSIGNED_4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,90,90,10
+ CONTROL "Unassigned Key #&5",IDC_KEY_UNASSIGNED_5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,105,90,10
+ CONTROL "Unassigned Key #&6",IDC_KEY_UNASSIGNED_6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,120,90,10
+ CONTROL "Unassigned Key #&7",IDC_KEY_UNASSIGNED_7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,135,90,10
+ CONTROL "Unassigned Key #&8",IDC_KEY_UNASSIGNED_8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,150,90,10
+ CONTROL "Unassigned Key #&9",IDC_KEY_UNASSIGNED_9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,165,90,10
CONTROL "Unassigned Key #1&0",IDC_KEY_UNASSIGNED_10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,180,90,10
- PUSHBUTTON "Select &All",IDC_KEY_SELECT_ALL_BTN,115,15,50,14
- PUSHBUTTON "Select &None",IDC_KEY_SELECT_NONE_BTN,115,35,50,14
- DEFPUSHBUTTON "A&pply",IDOK,115,55,50,14
- PUSHBUTTON "&Cancel",IDCANCEL,115,75,50,14
+ CONTROL "Extended FN Key: F13",IDC_KEY_F13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,15,90,10
+ CONTROL "Extended FN Key: F14",IDC_KEY_F14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,30,90,10
+ CONTROL "Extended FN Key: F15",IDC_KEY_F15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,45,90,10
+ CONTROL "Extended FN Key: F16",IDC_KEY_F16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,60,90,10
+ CONTROL "Extended FN Key: F17",IDC_KEY_F17,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,75,90,10
+ CONTROL "Extended FN Key: F18",IDC_KEY_F18,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,90,90,10
+ CONTROL "Extended FN Key: F19",IDC_KEY_F19,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,105,90,10
+ CONTROL "Extended FN Key: F20",IDC_KEY_F20,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,120,90,10
+ CONTROL "Extended FN Key: F21",IDC_KEY_F21,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,135,90,10
+ CONTROL "Extended FN Key: F22",IDC_KEY_F22,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,150,90,10
+ CONTROL "Extended FN Key: F23",IDC_KEY_F23,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,165,90,10
+ CONTROL "Extended FN Key: F24",IDC_KEY_F24,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,180,90,10
+ CONTROL "Invisible &Mouse Move",IDC_MOUSE_MOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,15,90,10
+ CONTROL "&Zero-pixel move",IDC_MOUSE_MOVE_ZERO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,245,25,70,10
+ PUSHBUTTON "Select &None",IDC_KEY_SELECT_NONE_BTN,225,55,85,14
+ PUSHBUTTON "Select All &Unassigned", IDC_KEY_SELECT_ALL_UNASSGND_BTN,225,80,85,14
+ PUSHBUTTON "Select All Ext. FN Keys", IDC_KEY_SELECT_ALL_EXT_FN_BTN,225,105,85,14
+ PUSHBUTTON "Select &All",IDC_KEY_SELECT_ALL_BTN,225,130,85,14
+ DEFPUSHBUTTON "A&pply",IDOK,225,155,85,14
+ PUSHBUTTON "&Cancel",IDCANCEL,225,180,85,14
END
/////////////////////////////////////////////////////////////////////////////
diff --git a/StayAwake/resource.h b/StayAwake/resource.h
index cd1de7e..5b80db5 100644
--- a/StayAwake/resource.h
+++ b/StayAwake/resource.h
@@ -25,26 +25,43 @@
#define IDC_START_MINIMIZED 1014
#define IDC_ABOUT_BUTTON 1015
-#define IDC_KEY_SCROLL_LOCK 1051
-#define IDC_KEY_VOLUME_UP_DOWN 1052
-#define IDC_KEY_UNASSIGNED_1 1053
-#define IDC_KEY_UNASSIGNED_2 1054
-#define IDC_KEY_UNASSIGNED_3 1055
-#define IDC_KEY_UNASSIGNED_4 1056
-#define IDC_KEY_UNASSIGNED_5 1057
-#define IDC_KEY_UNASSIGNED_6 1058
-#define IDC_KEY_UNASSIGNED_7 1059
-#define IDC_KEY_UNASSIGNED_8 1060
-#define IDC_KEY_UNASSIGNED_9 1061
-#define IDC_KEY_UNASSIGNED_10 1062
-#define IDC_KEY_SELECT_ALL_BTN 1071
-#define IDC_KEY_SELECT_NONE_BTN 1072
+#define IDC_KEY_SCROLL_LOCK 1101
+#define IDC_KEY_VOLUME_UP_DOWN 1102
+#define IDC_KEY_UNASSIGNED_1 1103
+#define IDC_KEY_UNASSIGNED_2 1104
+#define IDC_KEY_UNASSIGNED_3 1105
+#define IDC_KEY_UNASSIGNED_4 1106
+#define IDC_KEY_UNASSIGNED_5 1107
+#define IDC_KEY_UNASSIGNED_6 1108
+#define IDC_KEY_UNASSIGNED_7 1109
+#define IDC_KEY_UNASSIGNED_8 1110
+#define IDC_KEY_UNASSIGNED_9 1111
+#define IDC_KEY_UNASSIGNED_10 1112
+#define IDC_KEY_F13 1113
+#define IDC_KEY_F14 1114
+#define IDC_KEY_F15 1115
+#define IDC_KEY_F16 1116
+#define IDC_KEY_F17 1117
+#define IDC_KEY_F18 1118
+#define IDC_KEY_F19 1119
+#define IDC_KEY_F20 1120
+#define IDC_KEY_F21 1121
+#define IDC_KEY_F22 1122
+#define IDC_KEY_F23 1123
+#define IDC_KEY_F24 1124
+#define IDC_MOUSE_MOVE 1125
+#define IDC_MOUSE_MOVE_ZERO 1126
-#define IDC_ABOUT_NAME 1101
-#define IDC_ABOUT_VERSION 1102
-#define IDC_ABOUT_BUILD_TIME 1103
-#define IDC_ABOUT_ATTRIBUTION 1104
-#define IDC_ABOUT_PROD_URL 1105
+#define IDC_KEY_SELECT_NONE_BTN 1151
+#define IDC_KEY_SELECT_ALL_UNASSGND_BTN 1152
+#define IDC_KEY_SELECT_ALL_EXT_FN_BTN 1153
+#define IDC_KEY_SELECT_ALL_BTN 1154
+
+#define IDC_ABOUT_NAME 1201
+#define IDC_ABOUT_VERSION 1202
+#define IDC_ABOUT_BUILD_TIME 1203
+#define IDC_ABOUT_ATTRIBUTION 1204
+#define IDC_ABOUT_PROD_URL 1205
#define IDM_EXIT 32771
#define IDM_RESTORE 32772
From 03f4a2d17bb9cdd37a8edd5284d57a26afeeeda8 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 27 Sep 2026 21:51:39 -0400
Subject: [PATCH 10/22] Add controls & simulation logic and for FN keys & mouse
move
---
StayAwake/SelectKeyCodesDlg.cpp | 95 ++++++++++++++++----------
StayAwake/SelectKeyCodesDlg.h | 12 +++-
StayAwake/StayAwakeCore.cpp | 111 ++++++++++++++++++++++++++++--
StayAwake/StayAwakeCore.h | 36 ++++++----
StayAwake/StayAwakeDlg.cpp | 117 ++++++--------------------------
StayAwake/StayAwakeDlg.h | 11 +--
6 files changed, 220 insertions(+), 162 deletions(-)
diff --git a/StayAwake/SelectKeyCodesDlg.cpp b/StayAwake/SelectKeyCodesDlg.cpp
index 5958d04..11992b0 100644
--- a/StayAwake/SelectKeyCodesDlg.cpp
+++ b/StayAwake/SelectKeyCodesDlg.cpp
@@ -1,22 +1,19 @@
-// CSelectKeyCodesDlg.cpp : implementation file
-//
-
#include "pch.h"
#include "StayAwake.h"
#include "afxdialogex.h"
#include "SelectKeyCodesDlg.h"
-static_assert(IDC_KEY_UNASSIGNED_10 - IDC_KEY_SCROLL_LOCK + 1 == LEN_ROSTER_KEYCODES, "Roster checkbox IDs must be consecutive");
+static_assert(IDC_MOUSE_MOVE - IDC_KEY_SCROLL_LOCK + 1 == LEN_ROSTER_KEYCODES, "Roster checkbox IDs must be consecutive");
// CSelectKeyCodesDlg dialog
IMPLEMENT_DYNAMIC(CSelectKeyCodesDlg, CDialogEx)
-CSelectKeyCodesDlg::CSelectKeyCodesDlg(CStayAwakeDlg* pCaller)
- : CDialogEx(IDD_SELECT_KEYCODES_DIALOG, pCaller)
- , m_pCaller(pCaller)
+CSelectKeyCodesDlg::CSelectKeyCodesDlg(CWnd* pParent, StayAwakeCore& pAwakeCore)
+ : CDialogEx(IDD_SELECT_KEYCODES_DIALOG, pParent)
+ , m_AwakeCore(pAwakeCore)
{
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
+ m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
CSelectKeyCodesDlg::~CSelectKeyCodesDlg()
@@ -25,63 +22,89 @@ CSelectKeyCodesDlg::~CSelectKeyCodesDlg()
void CSelectKeyCodesDlg::DoDataExchange(CDataExchange* pDX)
{
- CDialogEx::DoDataExchange(pDX);
+ CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CSelectKeyCodesDlg, CDialogEx)
- ON_BN_CLICKED(IDOK, &CSelectKeyCodesDlg::OnOK)
- ON_BN_CLICKED(IDC_KEY_SELECT_ALL_BTN, &CSelectKeyCodesDlg::OnClickedKeySelectAllBtn)
- ON_BN_CLICKED(IDC_KEY_SELECT_NONE_BTN, &CSelectKeyCodesDlg::OnClickedKeySelectNoneBtn)
+ ON_BN_CLICKED(IDOK, &CSelectKeyCodesDlg::OnOK)
+ ON_BN_CLICKED(IDC_KEY_SELECT_ALL_BTN, &CSelectKeyCodesDlg::OnClickedKeySelectAllBtn)
+ ON_BN_CLICKED(IDC_KEY_SELECT_NONE_BTN, &CSelectKeyCodesDlg::OnClickedKeySelectNoneBtn)
+ ON_BN_CLICKED(IDC_KEY_SELECT_ALL_UNASSGND_BTN, &CSelectKeyCodesDlg::OnBnClickedSelectAllUnassignedKeys)
+ ON_BN_CLICKED(IDC_KEY_SELECT_ALL_EXT_FN_BTN, &CSelectKeyCodesDlg::OnBnClickedSelectAllExtFnKeys)
+ ON_BN_CLICKED(IDC_MOUSE_MOVE, &CSelectKeyCodesDlg::OnClickedMouseMove)
END_MESSAGE_MAP()
BOOL CSelectKeyCodesDlg::OnInitDialog()
{
- CDialogEx::OnInitDialog();
+ CDialogEx::OnInitDialog();
+
+ SetIcon(m_hIcon, TRUE); // Set big icon
+ SetIcon(m_hIcon, FALSE); // Set small icon
- SetIcon(m_hIcon, TRUE); // Set big icon
- SetIcon(m_hIcon, FALSE); // Set small icon
+ CheckAllBoxes(m_AwakeCore.GetSelectedKeyCodes());
- CheckAllBoxes(m_pCaller->GetSelectedKeyCodes());
+ CheckDlgButton(IDC_MOUSE_MOVE_ZERO,
+ (m_AwakeCore.GetPreference(PREF_MOUSE_MOVE_ZERO, L"Y") == L"Y") ? BST_CHECKED : BST_UNCHECKED);
+ OnClickedMouseMove();
- return TRUE; // return TRUE unless you set the focus to a control
+ return TRUE; // return TRUE unless you set the focus to a control
}
void CSelectKeyCodesDlg::OnOK()
{
- wstring sSelectedKeyCodes{};
+ wstring sSelectedKeyCodes{};
+
+ for (int i{}; i < LEN_ROSTER_KEYCODES; i++)
+ sSelectedKeyCodes += (IsDlgButtonChecked(IDC_KEY_SCROLL_LOCK + i)) ? L"1" : L"0";
- for (int i{}; i < LEN_ROSTER_KEYCODES; i++)
- sSelectedKeyCodes += (IsDlgButtonChecked(IDC_KEY_SCROLL_LOCK + i)) ? L"1" : L"0";
+ if (sSelectedKeyCodes == wstring(LEN_ROSTER_KEYCODES, L'0'))
+ {
+ MessageBox(L"Please select at least one Key Code", SELECT_KEYCODES_DIALOG_TITLE, MB_ICONEXCLAMATION);
+ return;
+ }
- if (sSelectedKeyCodes == wstring(LEN_ROSTER_KEYCODES, L'0'))
- {
- MessageBox(L"Please select at least one Key Code", L"Select multiple Key Codes", MB_ICONEXCLAMATION);
- return;
- }
+ if (!m_AwakeCore.SaveSelectedKeyCodes(sSelectedKeyCodes))
+ {
+ MessageBox(L"Unable to save to the StayAwake.ini file.", SELECT_KEYCODES_DIALOG_TITLE, MB_ICONEXCLAMATION);
+ return;
+ }
- if (!m_pCaller->SaveSelectedKeyCodes(sSelectedKeyCodes))
- {
- MessageBox(L"Unable to save to the StayAwake.ini file.", L"Select multiple Key Codes", MB_ICONEXCLAMATION);
- return;
- }
+ m_AwakeCore.SetPreference(PREF_MOUSE_MOVE_ZERO, IsDlgButtonChecked(IDC_MOUSE_MOVE_ZERO) ? L"Y" : L"N");
- CDialogEx::OnOK();
+ CDialogEx::OnOK();
}
-void CSelectKeyCodesDlg::CheckAllBoxes(const wstring& sSelectedKeyCodes)
+void CSelectKeyCodesDlg::CheckAllBoxes(const wstring& sSelectedKeyCodes, int start, int endNext)
{
- for (int i{}; i < LEN_ROSTER_KEYCODES; i++)
- CheckDlgButton(IDC_KEY_SCROLL_LOCK + i, sSelectedKeyCodes.at(i) == L'1');
+ for (int i{ start }; i < endNext; i++)
+ CheckDlgButton(i, sSelectedKeyCodes.at(i % IDC_KEY_SCROLL_LOCK) == L'1');
}
void CSelectKeyCodesDlg::OnClickedKeySelectAllBtn()
{
- CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'));
+ CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'));
+ OnClickedMouseMove();
}
void CSelectKeyCodesDlg::OnClickedKeySelectNoneBtn()
{
- CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'0'));
+ CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'0'));
+ OnClickedMouseMove();
+}
+
+void CSelectKeyCodesDlg::OnBnClickedSelectAllUnassignedKeys()
+{
+ CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'), IDC_KEY_UNASSIGNED_1, IDC_KEY_F13);
+}
+
+void CSelectKeyCodesDlg::OnBnClickedSelectAllExtFnKeys()
+{
+ CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'), IDC_KEY_F13, IDC_MOUSE_MOVE);
+}
+
+void CSelectKeyCodesDlg::OnClickedMouseMove()
+{
+ GetDlgItem(IDC_MOUSE_MOVE_ZERO)->EnableWindow(IsDlgButtonChecked(IDC_MOUSE_MOVE));
}
diff --git a/StayAwake/SelectKeyCodesDlg.h b/StayAwake/SelectKeyCodesDlg.h
index 2a23998..1909ed3 100644
--- a/StayAwake/SelectKeyCodesDlg.h
+++ b/StayAwake/SelectKeyCodesDlg.h
@@ -3,12 +3,14 @@
#include "Utils.h"
#include "StayAwakeDlg.h"
+constexpr auto SELECT_KEYCODES_DIALOG_TITLE = L"Select multiple simulation options";
+
class CSelectKeyCodesDlg : public CDialogEx
{
DECLARE_DYNAMIC(CSelectKeyCodesDlg)
public:
- explicit CSelectKeyCodesDlg(CStayAwakeDlg* pCaller);
+ explicit CSelectKeyCodesDlg(CWnd* pParent, StayAwakeCore& pAwakeCore);
virtual ~CSelectKeyCodesDlg();
// Dialog Data
@@ -17,7 +19,7 @@ class CSelectKeyCodesDlg : public CDialogEx
#endif
protected:
- CStayAwakeDlg* m_pCaller;
+ StayAwakeCore m_AwakeCore{};
HICON m_hIcon;
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
@@ -30,5 +32,9 @@ class CSelectKeyCodesDlg : public CDialogEx
DECLARE_MESSAGE_MAP()
private:
- void CheckAllBoxes(const wstring& sSelectedKeyCodes);
+ void CheckAllBoxes(const wstring& sSelectedKeyCodes, int start = IDC_KEY_SCROLL_LOCK, int endNext = IDC_KEY_SCROLL_LOCK + LEN_ROSTER_KEYCODES);
+public:
+ afx_msg void OnBnClickedSelectAllUnassignedKeys();
+ afx_msg void OnBnClickedSelectAllExtFnKeys();
+ afx_msg void OnClickedMouseMove();
};
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index 26ccd5e..1964a84 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -1,6 +1,95 @@
#include "pch.h"
+
+#include "resource.h"
#include "StayAwakeCore.h"
+void StayAwakeCore::SetConfigFilePath(LPTSTR iniFilePath)
+{
+ wcscpy_s(sIniFilePath, MAX_PATH, iniFilePath);
+}
+
+wstring StayAwakeCore::GetSelectedKeyCodes()
+{
+ wstring sKeyCodes{ GetPreference(PREF_SELECTED_KEYCODES, L"N/A") };
+
+ if (sKeyCodes == L"N/A") {
+ // Migrate single legacy key code to 25 bits
+ UINT nLegacyKeyCode = GetPrivateProfileInt(PREF_DEFAULTS, PREF_LEGACY_KEYCODE, 99, sIniFilePath);
+
+ if (nLegacyKeyCode == 99)
+ sKeyCodes = DEF_SELECTED_KEYCODES;
+ else
+ {
+ sKeyCodes = wstring(LEN_ROSTER_KEYCODES, L'0');
+ sKeyCodes.replace(nLegacyKeyCode % LEN_ROSTER_KEYCODES, 1, L"1");
+ }
+
+ WritePrivateProfileString(PREF_DEFAULTS, PREF_LEGACY_KEYCODE, nullptr, sIniFilePath);
+ SaveSelectedKeyCodes(sKeyCodes);
+ }
+ else if (sKeyCodes.length() == (IDC_KEY_F13 - IDC_KEY_SCROLL_LOCK))
+ {
+ // Migrate 12-bit legacy key codes to 25 bits
+ sKeyCodes += wstring(LEN_ROSTER_KEYCODES - sKeyCodes.length(), L'1');
+ SaveSelectedKeyCodes(sKeyCodes);
+ }
+ else if (!CheckSelectedKeyCodes(sKeyCodes))
+ {
+ sKeyCodes = DEF_SELECTED_KEYCODES;
+ SaveSelectedKeyCodes(sKeyCodes);
+ }
+
+ return sKeyCodes;
+}
+
+bool StayAwakeCore::CheckSelectedKeyCodes(wstring sKeyCodes)
+{
+ return (sKeyCodes.length() == LEN_ROSTER_KEYCODES &&
+ sKeyCodes != wstring(LEN_ROSTER_KEYCODES, L'0') &&
+ sKeyCodes.find_first_not_of(L"01") == std::string::npos);
+}
+
+bool StayAwakeCore::SaveSelectedKeyCodes(wstring sKeyCodes)
+{
+ return CheckSelectedKeyCodes(sKeyCodes) && SetPreference(PREF_SELECTED_KEYCODES, sKeyCodes);
+}
+
+void StayAwakeCore::InitIntervals(UINT& minSeconds, UINT& maxSeconds) const
+{
+ int nIntervalLegacy{}, nIntervalMin{}, nIntervalMax{};
+
+ nIntervalLegacy = GetPrivateProfileInt(PREF_DEFAULTS, PREF_INTERVAL_LEGACY, DEF_PERIOD, sIniFilePath);
+ if (nIntervalLegacy < MIN_PERIOD || nIntervalLegacy > MAX_PERIOD)
+ nIntervalLegacy = DEF_PERIOD;
+
+ nIntervalMin = GetPrivateProfileInt(PREF_DEFAULTS, PREF_INTERVAL_MINIMUM, nIntervalLegacy, sIniFilePath);
+ if (nIntervalMin < MIN_PERIOD || nIntervalMin > MAX_PERIOD)
+ nIntervalMin = DEF_PERIOD;
+
+ nIntervalMax = GetPrivateProfileInt(PREF_DEFAULTS, PREF_INTERVAL_MAXIMUM, nIntervalLegacy, sIniFilePath);
+ if (nIntervalMax < MIN_PERIOD || nIntervalMax > MAX_PERIOD)
+ nIntervalMax = DEF_PERIOD;
+
+ minSeconds = (nIntervalMin <= nIntervalMax) ? nIntervalMin : nIntervalMax;
+ maxSeconds = (nIntervalMin >= nIntervalMax) ? nIntervalMin : nIntervalMax;
+}
+
+wstring StayAwakeCore::GetPreference(wstring key, wstring defaultVal) const
+{
+ const int bufSize{ MAX_PATH };
+ wstring ftBuf(bufSize, '\0');
+
+ GetPrivateProfileString(PREF_DEFAULTS, key.c_str(), defaultVal.c_str(), ftBuf.data(), bufSize, sIniFilePath);
+
+ return wstring{ ftBuf.c_str() };
+}
+
+bool StayAwakeCore::SetPreference(wstring key, wstring setVal) const
+{
+ return WritePrivateProfileString(PREF_DEFAULTS, key.c_str(), setVal.c_str(), sIniFilePath);
+}
+
+
void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
{
switch (inputCode)
@@ -36,6 +125,7 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
PressOneKey(VK_UNASSIGNED_10, false);
break;
+ case 12:
case 13:
case 14:
case 15:
@@ -47,14 +137,21 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
case 21:
case 22:
case 23:
- case 24:
- inputName = L"F" + to_wstring(inputCode);
- PressOneKey(static_cast(VK_F13 + inputCode - 13), false);
+ inputName = L"F" + to_wstring(inputCode + 1);
+ PressOneKey(static_cast(VK_F13 + inputCode - 12), false);
break;
- case 25:
- inputName = L"Invisible Mouse Move";
- MouseMove();
+ case 24:
+ if (GetPreference(PREF_MOUSE_MOVE_ZERO, L"Y") == L"Y")
+ {
+ inputName = L"Zero Mouse Move";
+ MouseMoveZero();
+ }
+ else
+ {
+ inputName = L"Tiny Mouse Move";
+ MouseMoveTiny();
+ }
break;
@@ -162,7 +259,7 @@ void StayAwakeCore::PressThreeKeys(BYTE vkFirst, BYTE vkSecond, BYTE vkThird, bo
SendInput(6, input, sizeof(INPUT));
}
-void StayAwakeCore::MouseMove()
+void StayAwakeCore::MouseMoveTiny()
{
INPUT input[2] = {};
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index ef310f9..862db01 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -6,8 +6,13 @@
#include
#include
+#pragma comment(lib, "Ole32.lib")
#pragma comment(lib, "Wtsapi32.lib")
+using std::string;
+using std::wstring;
+using std::to_wstring;
+
constexpr auto VK_UNASSIGNED_01 = 0x97;
constexpr auto VK_UNASSIGNED_10 = 0xE8;
@@ -22,18 +27,15 @@ constexpr auto PREF_MULTI_INSTANCE = L"MultipleInstancesAllowed";
constexpr auto PREF_INTERVAL_LEGACY = L"TimerIntervalInSeconds";
constexpr auto PREF_INTERVAL_MINIMUM = L"MinimumIntervalInSeconds";
constexpr auto PREF_INTERVAL_MAXIMUM = L"MaximumIntervalInSeconds";
+constexpr auto PREF_MOUSE_MOVE_ZERO = L"MouseMoveZeroPixels";
constexpr auto MIN_PERIOD{ 10 };
constexpr auto MAX_PERIOD{ 9990 };
constexpr auto DEF_PERIOD{ 240 };
-constexpr auto LEN_ROSTER_KEYCODES{ 12 };
-constexpr auto DEF_SELECTED_KEYCODES = L"001111111111";
-
+constexpr auto LEN_ROSTER_KEYCODES{ 25 };
+const auto DEF_SELECTED_KEYCODES = L"00" + wstring(LEN_ROSTER_KEYCODES - 2, L'1');
-using std::string;
-using std::wstring;
-using std::to_wstring;
class StayAwakeCore
@@ -42,16 +44,26 @@ class StayAwakeCore
StayAwakeCore() {};
~StayAwakeCore() {};
+ void SetConfigFilePath(LPTSTR iniFilePath);
+
+ wstring GetSelectedKeyCodes();
+ bool CheckSelectedKeyCodes(wstring sKeyCodes);
+ bool SaveSelectedKeyCodes(wstring sKeyCodes);
+ void InitIntervals(UINT& minSeconds, UINT& maxSeconds) const;
+ wstring GetPreference(wstring key, wstring defaultVal) const;
+ bool SetPreference(wstring key, wstring setVal) const;
+
void SimulateInput(int inputCode, wstring& inputName);
- static ULONGLONG GetIdleTimeMilliseconds();
+ ULONGLONG GetIdleTimeMilliseconds();
private:
- static void PressOneKey(BYTE vk, bool extended = false);
- static void PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended = false);
- static void PressThreeKeys(BYTE vkFirst, BYTE vkSecond, BYTE vkThird, bool extended = false);
+ wchar_t sIniFilePath[MAX_PATH]{};
- static void MouseMove();
- static void MouseMoveZero();
+ void PressOneKey(BYTE vk, bool extended = false);
+ void PressTwoKeys(BYTE vkFirst, BYTE vkSecond, bool extended = false);
+ void PressThreeKeys(BYTE vkFirst, BYTE vkSecond, BYTE vkThird, bool extended = false);
+ void MouseMoveTiny();
+ void MouseMoveZero();
bool GetMuteState();
};
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index 4347b54..4e48cc1 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -26,51 +26,6 @@ void CStayAwakeDlg::DoDataExchange(CDataExchange* pDX)
CDialogEx::DoDataExchange(pDX);
}
-wstring CStayAwakeDlg::GetSelectedKeyCodes()
-{
- const int bufSize{ LEN_ROSTER_KEYCODES + 1 };
- wchar_t sBuf[bufSize]{};
-
- GetPrivateProfileString(PREF_DEFAULTS, PREF_SELECTED_KEYCODES, L"N/A", sBuf, bufSize, m_IniFilePath);
-
- wstring sKeyCodes{ sBuf };
-
- if (sKeyCodes == L"N/A") {
- UINT nLegacyKeyCode = GetPrivateProfileInt(PREF_DEFAULTS, PREF_LEGACY_KEYCODE, 99, m_IniFilePath);
-
- if (nLegacyKeyCode == 99)
- sKeyCodes = DEF_SELECTED_KEYCODES;
- else
- {
- sKeyCodes = wstring(LEN_ROSTER_KEYCODES, L'0');
- sKeyCodes.replace(nLegacyKeyCode % LEN_ROSTER_KEYCODES, 1, L"1");
- }
-
- WritePrivateProfileString(PREF_DEFAULTS, PREF_LEGACY_KEYCODE, nullptr, m_IniFilePath);
- SaveSelectedKeyCodes(sKeyCodes);
- }
- else if (!CheckSelectedKeyCodes(sKeyCodes))
- {
- sKeyCodes = DEF_SELECTED_KEYCODES;
- SaveSelectedKeyCodes(sKeyCodes);
- }
-
- return sKeyCodes;
-}
-
-bool CStayAwakeDlg::CheckSelectedKeyCodes(wstring sKeyCodes)
-{
- return (sKeyCodes.length() == LEN_ROSTER_KEYCODES &&
- sKeyCodes != wstring(LEN_ROSTER_KEYCODES, L'0') &&
- sKeyCodes.find_first_not_of(L"01") == std::string::npos);
-}
-
-bool CStayAwakeDlg::SaveSelectedKeyCodes(wstring sKeyCodes)
-{
- return CheckSelectedKeyCodes(sKeyCodes) &&
- WritePrivateProfileString(PREF_DEFAULTS, PREF_SELECTED_KEYCODES, sKeyCodes.c_str(), m_IniFilePath);
-}
-
BEGIN_MESSAGE_MAP(CStayAwakeDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_MESSAGE(WM_POST_OPEN, &CStayAwakeDlg::OnPostOpen)
@@ -127,7 +82,8 @@ BOOL CStayAwakeDlg::OnInitDialog()
SetIcon(m_hIcon, FALSE); // Set small icon
InitConfigFilePath();
- InitIntervals();
+
+ m_AwakeCore.InitIntervals(m_IntervalMinSeconds, m_IntervalMaxSeconds);
SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MIN, m_IntervalMinSeconds, FALSE);
SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MAX, m_IntervalMaxSeconds, FALSE);
@@ -163,7 +119,7 @@ afx_msg LRESULT CStayAwakeDlg::OnPostOpen(WPARAM wParam, LPARAM lParam)
{
WTSRegisterSessionNotification(m_hWnd, NOTIFY_FOR_THIS_SESSION);
- if (GetPreference(PREF_MULTI_INSTANCE, L"N") != L"Y" && Utils::getProcessRunCount(L"StayAwake.exe") > 1)
+ if (m_AwakeCore.GetPreference(PREF_MULTI_INSTANCE, L"N") != L"Y" && Utils::getProcessRunCount(L"StayAwake.exe") > 1)
{
::PostMessage(HWND_BROADCAST, theApp.WM_SHOWFIRSTINSTANCE, 0, 0);
@@ -176,7 +132,7 @@ afx_msg LRESULT CStayAwakeDlg::OnPostOpen(WPARAM wParam, LPARAM lParam)
else
InitAwakes();
- BOOL bMinimized = (GetPreference(PREF_START_MINIMIZED, L"N") == L"Y");
+ BOOL bMinimized = (m_AwakeCore.GetPreference(PREF_START_MINIMIZED, L"N") == L"Y");
CheckDlgButton(IDC_START_MINIMIZED, bMinimized ? BST_CHECKED : BST_UNCHECKED);
if (bMinimized) MinimizeToTray();
@@ -247,7 +203,7 @@ void CStayAwakeDlg::OnKillfocusIntervalMax()
void CStayAwakeDlg::OnSetupKeyCodesRosterClicked()
{
- CSelectKeyCodesDlg dlgSelectKeyCodes(this);
+ CSelectKeyCodesDlg dlgSelectKeyCodes(this, m_AwakeCore);
if (dlgSelectKeyCodes.DoModal() == IDOK)
{
@@ -265,41 +221,23 @@ void CStayAwakeDlg::OnTimer(UINT_PTR nIDEvent)
void CStayAwakeDlg::InitConfigFilePath()
{
- DWORD size = GetModuleFileNameW(nullptr, m_IniFilePath, MAX_PATH);
-
- if (size > 0 && size < MAX_PATH &&
- SUCCEEDED(PathCchRemoveFileSpec(m_IniFilePath, MAX_PATH)) &&
- SUCCEEDED(PathCchAppend(m_IniFilePath, MAX_PATH, PREF_INI_FILE)))
- return;
-
- wcscpy_s(m_IniFilePath, MAX_PATH, L".\\"); // last resort: working directory
- wcscat_s(m_IniFilePath, MAX_PATH, PREF_INI_FILE);
-}
-
-
-void CStayAwakeDlg::InitIntervals()
-{
- int nIntervalLegacy{}, nIntervalMin{}, nIntervalMax{};
+ wchar_t sIniFilePath[MAX_PATH]{};
+ DWORD size = GetModuleFileNameW(nullptr, sIniFilePath, MAX_PATH);
- nIntervalLegacy = GetPrivateProfileInt(PREF_DEFAULTS, PREF_INTERVAL_LEGACY, DEF_PERIOD, m_IniFilePath);
- if (nIntervalLegacy < MIN_PERIOD || nIntervalLegacy > MAX_PERIOD)
- nIntervalLegacy = DEF_PERIOD;
-
- nIntervalMin = GetPrivateProfileInt(PREF_DEFAULTS, PREF_INTERVAL_MINIMUM, nIntervalLegacy, m_IniFilePath);
- if (nIntervalMin < MIN_PERIOD || nIntervalMin > MAX_PERIOD)
- nIntervalMin = DEF_PERIOD;
-
- nIntervalMax = GetPrivateProfileInt(PREF_DEFAULTS, PREF_INTERVAL_MAXIMUM, nIntervalLegacy, m_IniFilePath);
- if (nIntervalMax < MIN_PERIOD || nIntervalMax > MAX_PERIOD)
- nIntervalMax = DEF_PERIOD;
+ if (size <= 0 || size > MAX_PATH ||
+ FAILED(PathCchRemoveFileSpec(sIniFilePath, MAX_PATH)) ||
+ FAILED(PathCchAppend(sIniFilePath, MAX_PATH, PREF_INI_FILE)))
+ {
+ wcscpy_s(sIniFilePath, MAX_PATH, L".\\"); // last resort: working directory
+ wcscat_s(sIniFilePath, MAX_PATH, PREF_INI_FILE);
+ }
- m_IntervalMinSeconds = (nIntervalMin <= nIntervalMax) ? nIntervalMin : nIntervalMax;
- m_IntervalMaxSeconds = (nIntervalMin >= nIntervalMax) ? nIntervalMin : nIntervalMax;
+ m_AwakeCore.SetConfigFilePath(sIniFilePath);
}
void CStayAwakeDlg::InitRosterKeyCodes()
{
- wstring sSelectedKeyCodes{ GetSelectedKeyCodes() };
+ wstring sSelectedKeyCodes{ m_AwakeCore.GetSelectedKeyCodes() };
m_RosterLength = 0;
@@ -316,7 +254,7 @@ void CStayAwakeDlg::InitAwakes()
SimulateAwakeKeyPress();
SetDlgItemText(IDC_STAYAWAKE_PAUSE_RESUME_BTN, BTN_TEXT_PAUSE);
- WritePrivateProfileString(PREF_DEFAULTS, PREF_AWAKE_PAUSED, L"N", m_IniFilePath);
+ m_AwakeCore.SetPreference(PREF_AWAKE_PAUSED, L"N");
}
void CStayAwakeDlg::InitTrayIcon()
@@ -431,8 +369,8 @@ void CStayAwakeDlg::OnSetInterval()
SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MAX, m_IntervalMaxSeconds, FALSE);
}
- WritePrivateProfileString(PREF_DEFAULTS, PREF_INTERVAL_MINIMUM, to_wstring(m_IntervalMinSeconds).c_str(), m_IniFilePath);
- WritePrivateProfileString(PREF_DEFAULTS, PREF_INTERVAL_MAXIMUM, to_wstring(m_IntervalMaxSeconds).c_str(), m_IniFilePath);
+ m_AwakeCore.SetPreference(PREF_INTERVAL_MINIMUM, to_wstring(m_IntervalMinSeconds));
+ m_AwakeCore.SetPreference(PREF_INTERVAL_MAXIMUM, to_wstring(m_IntervalMaxSeconds));
InitAwakes();
}
@@ -455,24 +393,14 @@ void CStayAwakeDlg::OnPauseResume()
KillTimer(m_TimerID);
SetDlgItemText(IDC_STAYAWAKE_PAUSE_RESUME_BTN, BTN_TEXT_RESUME);
- WritePrivateProfileString(PREF_DEFAULTS, PREF_AWAKE_PAUSED, L"Y", m_IniFilePath);
+ m_AwakeCore.SetPreference(PREF_AWAKE_PAUSED, L"Y");
ShowPausedInfo(false);
}
}
-wstring CStayAwakeDlg::GetPreference(wstring key, wstring defaultVal)
-{
- const int bufSize{ MAX_PATH };
- wstring ftBuf(bufSize, '\0');
-
- GetPrivateProfileString(PREF_DEFAULTS, key.c_str(), defaultVal.c_str(), ftBuf.data(), bufSize, m_IniFilePath);
-
- return wstring{ ftBuf.c_str() };
-}
-
bool CStayAwakeDlg::IsTimerPaused()
{
- return (GetPreference(PREF_AWAKE_PAUSED, L"N") == L"Y");
+ return (m_AwakeCore.GetPreference(PREF_AWAKE_PAUSED, L"N") == L"Y");
}
void CStayAwakeDlg::ShowPausedInfo(bool both)
@@ -485,8 +413,7 @@ void CStayAwakeDlg::ShowPausedInfo(bool both)
void CStayAwakeDlg::OnStartMinimized()
{
- WritePrivateProfileString(PREF_DEFAULTS, PREF_START_MINIMIZED,
- (IsDlgButtonChecked(IDC_START_MINIMIZED) == BST_CHECKED) ? L"Y" : L"N", m_IniFilePath);
+ m_AwakeCore.SetPreference(PREF_START_MINIMIZED, (IsDlgButtonChecked(IDC_START_MINIMIZED) == BST_CHECKED) ? L"Y" : L"N");
}
void CStayAwakeDlg::OnSessionChange(UINT nSessionState, UINT nId)
diff --git a/StayAwake/StayAwakeDlg.h b/StayAwake/StayAwakeDlg.h
index 10a69c2..10a18b2 100644
--- a/StayAwake/StayAwakeDlg.h
+++ b/StayAwake/StayAwakeDlg.h
@@ -3,6 +3,7 @@
#include "StayAwakeCore.h"
#include "Utils.h"
+
#define WM_POST_OPEN (WM_APP + 1)
#define WM_TRAY_NOTIFY (WM_APP + 2)
#define WM_RESTORE_DIALOG theApp.WM_SHOWFIRSTINSTANCE
@@ -30,11 +31,6 @@ class CStayAwakeDlg : public CDialogEx
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
-public:
- wstring GetSelectedKeyCodes();
- bool CheckSelectedKeyCodes(wstring sKeyCodes);
- bool SaveSelectedKeyCodes(wstring sKeyCodes);
-
protected:
HICON m_hIcon;
@@ -43,7 +39,6 @@ class CStayAwakeDlg : public CDialogEx
DECLARE_MESSAGE_MAP()
private:
- wchar_t m_IniFilePath[MAX_PATH]{};
bool m_bMinimized{}, m_bSystemLocked{};
UINT_PTR m_TimerID{42};
@@ -53,7 +48,7 @@ class CStayAwakeDlg : public CDialogEx
UINT m_IntervalMinSeconds{};
UINT m_IntervalMaxSeconds{};
- StayAwakeCore m_AwakeCore;
+ StayAwakeCore m_AwakeCore{};
NOTIFYICONDATA m_TrayData{};
CMenu m_menu;
@@ -77,7 +72,6 @@ class CStayAwakeDlg : public CDialogEx
afx_msg void OnSessionChange(UINT nSessionState, UINT nId);
void InitConfigFilePath();
- void InitIntervals();
void InitRosterKeyCodes();
void InitAwakes();
void InitTrayIcon();
@@ -87,6 +81,5 @@ class CStayAwakeDlg : public CDialogEx
void SimulateAwakeKeyPress();
void ShowPausedInfo(bool both);
- wstring GetPreference(wstring key, wstring defaultVal);
bool IsTimerPaused();
};
From 846c1484af0e5c1f6fa1cc07bcb405fbd0e03ddb Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Mon, 28 Sep 2026 20:35:22 -0400
Subject: [PATCH 11/22] Split One-pixel Jiggle & Zero-pixel Mouse Move as
distinct options
---
StayAwake/SelectKeyCodesDlg.cpp | 23 +++++------------------
StayAwake/SelectKeyCodesDlg.h | 6 ++----
StayAwake/StayAwake.rc | 30 +++++++++++++++---------------
StayAwake/StayAwakeCore.cpp | 16 ++++++----------
StayAwake/StayAwakeCore.h | 3 +--
5 files changed, 29 insertions(+), 49 deletions(-)
diff --git a/StayAwake/SelectKeyCodesDlg.cpp b/StayAwake/SelectKeyCodesDlg.cpp
index 11992b0..ac1efce 100644
--- a/StayAwake/SelectKeyCodesDlg.cpp
+++ b/StayAwake/SelectKeyCodesDlg.cpp
@@ -3,7 +3,7 @@
#include "afxdialogex.h"
#include "SelectKeyCodesDlg.h"
-static_assert(IDC_MOUSE_MOVE - IDC_KEY_SCROLL_LOCK + 1 == LEN_ROSTER_KEYCODES, "Roster checkbox IDs must be consecutive");
+static_assert(IDC_MOUSE_MOVE_ZERO - IDC_KEY_SCROLL_LOCK + 1 == LEN_ROSTER_KEYCODES, "Roster checkbox IDs must be consecutive");
// CSelectKeyCodesDlg dialog
@@ -30,9 +30,8 @@ BEGIN_MESSAGE_MAP(CSelectKeyCodesDlg, CDialogEx)
ON_BN_CLICKED(IDOK, &CSelectKeyCodesDlg::OnOK)
ON_BN_CLICKED(IDC_KEY_SELECT_ALL_BTN, &CSelectKeyCodesDlg::OnClickedKeySelectAllBtn)
ON_BN_CLICKED(IDC_KEY_SELECT_NONE_BTN, &CSelectKeyCodesDlg::OnClickedKeySelectNoneBtn)
- ON_BN_CLICKED(IDC_KEY_SELECT_ALL_UNASSGND_BTN, &CSelectKeyCodesDlg::OnBnClickedSelectAllUnassignedKeys)
- ON_BN_CLICKED(IDC_KEY_SELECT_ALL_EXT_FN_BTN, &CSelectKeyCodesDlg::OnBnClickedSelectAllExtFnKeys)
- ON_BN_CLICKED(IDC_MOUSE_MOVE, &CSelectKeyCodesDlg::OnClickedMouseMove)
+ ON_BN_CLICKED(IDC_KEY_SELECT_ALL_UNASSGND_BTN, &CSelectKeyCodesDlg::OnClickedSelectAllUnassignedKeys)
+ ON_BN_CLICKED(IDC_KEY_SELECT_ALL_EXT_FN_BTN, &CSelectKeyCodesDlg::OnClickedSelectAllExtFnKeys)
END_MESSAGE_MAP()
@@ -45,10 +44,6 @@ BOOL CSelectKeyCodesDlg::OnInitDialog()
CheckAllBoxes(m_AwakeCore.GetSelectedKeyCodes());
- CheckDlgButton(IDC_MOUSE_MOVE_ZERO,
- (m_AwakeCore.GetPreference(PREF_MOUSE_MOVE_ZERO, L"Y") == L"Y") ? BST_CHECKED : BST_UNCHECKED);
- OnClickedMouseMove();
-
return TRUE; // return TRUE unless you set the focus to a control
}
@@ -71,8 +66,6 @@ void CSelectKeyCodesDlg::OnOK()
return;
}
- m_AwakeCore.SetPreference(PREF_MOUSE_MOVE_ZERO, IsDlgButtonChecked(IDC_MOUSE_MOVE_ZERO) ? L"Y" : L"N");
-
CDialogEx::OnOK();
}
@@ -85,26 +78,20 @@ void CSelectKeyCodesDlg::CheckAllBoxes(const wstring& sSelectedKeyCodes, int sta
void CSelectKeyCodesDlg::OnClickedKeySelectAllBtn()
{
CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'));
- OnClickedMouseMove();
}
void CSelectKeyCodesDlg::OnClickedKeySelectNoneBtn()
{
CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'0'));
- OnClickedMouseMove();
}
-void CSelectKeyCodesDlg::OnBnClickedSelectAllUnassignedKeys()
+void CSelectKeyCodesDlg::OnClickedSelectAllUnassignedKeys()
{
CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'), IDC_KEY_UNASSIGNED_1, IDC_KEY_F13);
}
-void CSelectKeyCodesDlg::OnBnClickedSelectAllExtFnKeys()
+void CSelectKeyCodesDlg::OnClickedSelectAllExtFnKeys()
{
CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'), IDC_KEY_F13, IDC_MOUSE_MOVE);
}
-void CSelectKeyCodesDlg::OnClickedMouseMove()
-{
- GetDlgItem(IDC_MOUSE_MOVE_ZERO)->EnableWindow(IsDlgButtonChecked(IDC_MOUSE_MOVE));
-}
diff --git a/StayAwake/SelectKeyCodesDlg.h b/StayAwake/SelectKeyCodesDlg.h
index 1909ed3..4423745 100644
--- a/StayAwake/SelectKeyCodesDlg.h
+++ b/StayAwake/SelectKeyCodesDlg.h
@@ -27,14 +27,12 @@ class CSelectKeyCodesDlg : public CDialogEx
virtual BOOL OnInitDialog();
afx_msg void OnClickedKeySelectAllBtn();
afx_msg void OnClickedKeySelectNoneBtn();
+ afx_msg void OnClickedSelectAllUnassignedKeys();
+ afx_msg void OnClickedSelectAllExtFnKeys();
afx_msg void OnOK();
DECLARE_MESSAGE_MAP()
private:
void CheckAllBoxes(const wstring& sSelectedKeyCodes, int start = IDC_KEY_SCROLL_LOCK, int endNext = IDC_KEY_SCROLL_LOCK + LEN_ROSTER_KEYCODES);
-public:
- afx_msg void OnBnClickedSelectAllUnassignedKeys();
- afx_msg void OnBnClickedSelectAllExtFnKeys();
- afx_msg void OnClickedMouseMove();
};
diff --git a/StayAwake/StayAwake.rc b/StayAwake/StayAwake.rc
index e3dbf19..f5b7991 100644
--- a/StayAwake/StayAwake.rc
+++ b/StayAwake/StayAwake.rc
@@ -123,23 +123,23 @@ BEGIN
CONTROL "Unassigned Key #&8",IDC_KEY_UNASSIGNED_8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,150,90,10
CONTROL "Unassigned Key #&9",IDC_KEY_UNASSIGNED_9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,165,90,10
CONTROL "Unassigned Key #1&0",IDC_KEY_UNASSIGNED_10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,180,90,10
- CONTROL "Extended FN Key: F13",IDC_KEY_F13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,15,90,10
- CONTROL "Extended FN Key: F14",IDC_KEY_F14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,30,90,10
- CONTROL "Extended FN Key: F15",IDC_KEY_F15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,45,90,10
- CONTROL "Extended FN Key: F16",IDC_KEY_F16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,60,90,10
- CONTROL "Extended FN Key: F17",IDC_KEY_F17,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,75,90,10
- CONTROL "Extended FN Key: F18",IDC_KEY_F18,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,90,90,10
- CONTROL "Extended FN Key: F19",IDC_KEY_F19,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,105,90,10
- CONTROL "Extended FN Key: F20",IDC_KEY_F20,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,120,90,10
- CONTROL "Extended FN Key: F21",IDC_KEY_F21,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,135,90,10
- CONTROL "Extended FN Key: F22",IDC_KEY_F22,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,150,90,10
- CONTROL "Extended FN Key: F23",IDC_KEY_F23,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,165,90,10
- CONTROL "Extended FN Key: F24",IDC_KEY_F24,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,180,90,10
- CONTROL "Invisible &Mouse Move",IDC_MOUSE_MOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,15,90,10
- CONTROL "&Zero-pixel move",IDC_MOUSE_MOVE_ZERO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,245,25,70,10
+ CONTROL "Extended FN Key: F13",IDC_KEY_F13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,15,90,10
+ CONTROL "Extended FN Key: F14",IDC_KEY_F14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,30,90,10
+ CONTROL "Extended FN Key: F15",IDC_KEY_F15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,45,90,10
+ CONTROL "Extended FN Key: F16",IDC_KEY_F16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,60,90,10
+ CONTROL "Extended FN Key: F17",IDC_KEY_F17,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,75,90,10
+ CONTROL "Extended FN Key: F18",IDC_KEY_F18,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,90,90,10
+ CONTROL "Extended FN Key: F19",IDC_KEY_F19,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,105,90,10
+ CONTROL "Extended FN Key: F20",IDC_KEY_F20,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,120,90,10
+ CONTROL "Extended FN Key: F21",IDC_KEY_F21,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,135,90,10
+ CONTROL "Extended FN Key: F22",IDC_KEY_F22,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,150,90,10
+ CONTROL "Extended FN Key: F23",IDC_KEY_F23,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,165,90,10
+ CONTROL "Extended FN Key: F24",IDC_KEY_F24,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,180,90,10
+ CONTROL "One-pixel Mouse &Jiggle",IDC_MOUSE_MOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,15,90,10
+ CONTROL "Zero-pixel Mouse &Move",IDC_MOUSE_MOVE_ZERO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,30,90,10
PUSHBUTTON "Select &None",IDC_KEY_SELECT_NONE_BTN,225,55,85,14
PUSHBUTTON "Select All &Unassigned", IDC_KEY_SELECT_ALL_UNASSGND_BTN,225,80,85,14
- PUSHBUTTON "Select All Ext. FN Keys", IDC_KEY_SELECT_ALL_EXT_FN_BTN,225,105,85,14
+ PUSHBUTTON "Select All Ext. &FN Keys", IDC_KEY_SELECT_ALL_EXT_FN_BTN,225,105,85,14
PUSHBUTTON "Select &All",IDC_KEY_SELECT_ALL_BTN,225,130,85,14
DEFPUSHBUTTON "A&pply",IDOK,225,155,85,14
PUSHBUTTON "&Cancel",IDCANCEL,225,180,85,14
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index 1964a84..c05acc9 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -142,18 +142,14 @@ void StayAwakeCore::SimulateInput(int inputCode, wstring& inputName)
break;
case 24:
- if (GetPreference(PREF_MOUSE_MOVE_ZERO, L"Y") == L"Y")
- {
- inputName = L"Zero Mouse Move";
- MouseMoveZero();
- }
- else
- {
- inputName = L"Tiny Mouse Move";
- MouseMoveTiny();
- }
+ inputName = L"One-pixel Mouse Jiggle";
+ MouseMoveTiny();
break;
+ case 25:
+ inputName = L"Zero-pixel Mouse Move";
+ MouseMoveZero();
+ break;
default:
inputName = L"Invalid Awake Key";
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index 862db01..408d450 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -27,13 +27,12 @@ constexpr auto PREF_MULTI_INSTANCE = L"MultipleInstancesAllowed";
constexpr auto PREF_INTERVAL_LEGACY = L"TimerIntervalInSeconds";
constexpr auto PREF_INTERVAL_MINIMUM = L"MinimumIntervalInSeconds";
constexpr auto PREF_INTERVAL_MAXIMUM = L"MaximumIntervalInSeconds";
-constexpr auto PREF_MOUSE_MOVE_ZERO = L"MouseMoveZeroPixels";
constexpr auto MIN_PERIOD{ 10 };
constexpr auto MAX_PERIOD{ 9990 };
constexpr auto DEF_PERIOD{ 240 };
-constexpr auto LEN_ROSTER_KEYCODES{ 25 };
+constexpr auto LEN_ROSTER_KEYCODES{ 26 };
const auto DEF_SELECTED_KEYCODES = L"00" + wstring(LEN_ROSTER_KEYCODES - 2, L'1');
From c89efaa979f2ede7dda565e75d83daa6683a45ad Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Mon, 28 Sep 2026 21:39:30 -0400
Subject: [PATCH 12/22] Update About dialog
---
StayAwake/StayAwake.h | 2 +-
StayAwake/StayAwake.rc | 17 +++++++++--------
StayAwake/StayAwakeAboutDlg.cpp | 4 ++--
StayAwake/resource.h | 1 +
4 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/StayAwake/StayAwake.h b/StayAwake/StayAwake.h
index 7b5e178..c27ea52 100644
--- a/StayAwake/StayAwake.h
+++ b/StayAwake/StayAwake.h
@@ -6,7 +6,7 @@
#include "resource.h"
-constexpr auto LAST_CODE_UPDATE_TIME = "Sep 21 2026 - 15:45:46";
+constexpr auto LAST_CODE_UPDATE_TIME = L"Sep 21 2026 - 15:45:46";
class CStayAwakeApp : public CWinApp
diff --git a/StayAwake/StayAwake.rc b/StayAwake/StayAwake.rc
index f5b7991..51198d3 100644
--- a/StayAwake/StayAwake.rc
+++ b/StayAwake/StayAwake.rc
@@ -69,18 +69,19 @@ END
// Dialog
//
-IDD_ABOUTBOX DIALOGEX 0, 0, 191, 104
+IDD_ABOUTBOX DIALOGEX 0, 0, 210, 120
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About StayAwake"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
- ICON IDR_MAINFRAME,IDC_STATIC,12,14,21,20
- CTEXT "StayAwake",IDC_ABOUT_NAME,39,14,111,8,SS_NOPREFIX
- CTEXT "Copyright (C) Shridhar Kumar, 2026",IDC_ABOUT_ATTRIBUTION,18,54,154,8
- DEFPUSHBUTTON "OK",IDOK,70,81,50,14,WS_GROUP
- CTEXT "Version 1.0.0.1",IDC_ABOUT_VERSION,39,26,111,8
- CTEXT "Last code update:",IDC_ABOUT_BUILD_TIME,19,38,151,8
- CONTROL "GitHub Link",IDC_ABOUT_PROD_URL,"SysLink",WS_TABSTOP,77,66,37,8
+ ICON IDR_MAINFRAME,IDC_STATIC,35,14,21,20
+ CTEXT "StayAwake",IDC_ABOUT_NAME,55,14,100,8,SS_NOPREFIX
+ CTEXT "Version 1.0.0.1",IDC_ABOUT_VERSION,55,26,100,8
+ CTEXT "Last code update:",IDC_ABOUT_BUILD_TIME,10,38,190,8
+ CTEXT "Copyright (C) Shridhar Kumar, 2026",IDC_ABOUT_ATTRIBUTION,10,54,190,8
+ CONTROL "GitHub Link",IDC_ABOUT_PROD_URL,"SysLink",WS_TABSTOP,86,70,37,8
+ CTEXT "If you like StayAwake, please star this repo in GitHub.",IDC_ABOUT_PROD_REQ,10,82,190,8
+ DEFPUSHBUTTON "OK",IDOK,80,100,50,14,WS_GROUP
END
IDD_STAYAWAKE_DIALOG DIALOGEX 0, 0, 205, 144
diff --git a/StayAwake/StayAwakeAboutDlg.cpp b/StayAwake/StayAwakeAboutDlg.cpp
index fb1a5ac..50c6094 100644
--- a/StayAwake/StayAwakeAboutDlg.cpp
+++ b/StayAwake/StayAwakeAboutDlg.cpp
@@ -37,7 +37,7 @@ BOOL CAboutDlg::OnInitDialog()
SetDlgItemText(IDC_ABOUT_NAME, Utils::getVersionInfo(theApp.m_hInstance, L"FileDescription").c_str());
SetDlgItemText(IDC_ABOUT_VERSION, (L"Version: " + Utils::getVersionInfo(theApp.m_hInstance, L"FileVersion") + buildBit).c_str());
- SetDlgItemTextA(m_hWnd, IDC_ABOUT_BUILD_TIME, (string{ "Last code update: " } + LAST_CODE_UPDATE_TIME).c_str());
+ SetDlgItemText(IDC_ABOUT_BUILD_TIME, (wstring{ L"Last code update: " } + LAST_CODE_UPDATE_TIME).c_str());
SetDlgItemText(IDC_ABOUT_ATTRIBUTION, Utils::getVersionInfo(theApp.m_hInstance, L"LegalCopyright").c_str());
return TRUE;
@@ -52,5 +52,5 @@ afx_msg void CAboutDlg::OnProdUrlClick(NMHDR* pNotifyStruct, LRESULT* result)
afx_msg void CAboutDlg::OnProdUrlReturn(NMHDR* pNotifyStruct, LRESULT* result)
{
- ShellExecute(NULL, L"open", Utils::getVersionInfo(theApp.m_hInstance, L"CompanyName").c_str(), NULL, NULL, SW_SHOW);
+ OnProdUrlClick(pNotifyStruct, result);
}
diff --git a/StayAwake/resource.h b/StayAwake/resource.h
index 5b80db5..59df107 100644
--- a/StayAwake/resource.h
+++ b/StayAwake/resource.h
@@ -62,6 +62,7 @@
#define IDC_ABOUT_BUILD_TIME 1203
#define IDC_ABOUT_ATTRIBUTION 1204
#define IDC_ABOUT_PROD_URL 1205
+#define IDC_ABOUT_PROD_REQ 1206
#define IDM_EXIT 32771
#define IDM_RESTORE 32772
From 870913c0999d0838f09e60824b88181b704757c2 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sat, 3 Oct 2026 22:02:06 -0400
Subject: [PATCH 13/22] Optimize builds favoring size and use LTCG
---
StayAwake/StayAwake.vcxproj | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/StayAwake/StayAwake.vcxproj b/StayAwake/StayAwake.vcxproj
index 23c26c9..4393f47 100644
--- a/StayAwake/StayAwake.vcxproj
+++ b/StayAwake/StayAwake.vcxproj
@@ -150,16 +150,18 @@
stdc17
true
true
- Speed
+ Size
true
- false
+ true
+ OnlyExplicitInline
+ MinSpace
Windows
true
true
/BREPRO %(AdditionalOptions)
- Default
+ UseLinkTimeCodeGeneration
false
@@ -184,18 +186,20 @@
pch.h
true
true
- Speed
+ Size
true
stdcpp20
stdc17
- false
+ true
+ OnlyExplicitInline
+ MinSpace
Windows
true
true
/BREPRO
- Default
+ UseLinkTimeCodeGeneration
false
From 5d47de716ec2b118265e53c224ceaa2ab9c0b974 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sat, 3 Oct 2026 22:10:07 -0400
Subject: [PATCH 14/22] Add VersionUpdate class for 'Check for Updates' feature
Add functions to:
1. Fetch repo's latest release JSON data from GitHub
2. Parse latest release version info from that JSON data
---
StayAwake/StayAwake.vcxproj | 2 +
StayAwake/StayAwake.vcxproj.filters | 6 ++
StayAwake/VersionUpdate.cpp | 101 ++++++++++++++++++++++++++++
StayAwake/VersionUpdate.h | 21 ++++++
4 files changed, 130 insertions(+)
create mode 100644 StayAwake/VersionUpdate.cpp
create mode 100644 StayAwake/VersionUpdate.h
diff --git a/StayAwake/StayAwake.vcxproj b/StayAwake/StayAwake.vcxproj
index 4393f47..b0ce24e 100644
--- a/StayAwake/StayAwake.vcxproj
+++ b/StayAwake/StayAwake.vcxproj
@@ -223,6 +223,7 @@
+
@@ -237,6 +238,7 @@
Create
+
diff --git a/StayAwake/StayAwake.vcxproj.filters b/StayAwake/StayAwake.vcxproj.filters
index 856a3d4..7fa4b7b 100644
--- a/StayAwake/StayAwake.vcxproj.filters
+++ b/StayAwake/StayAwake.vcxproj.filters
@@ -42,6 +42,9 @@
Header Files
+
+ Header Files
+
@@ -65,6 +68,9 @@
Source Files
+
+ Source Files
+
diff --git a/StayAwake/VersionUpdate.cpp b/StayAwake/VersionUpdate.cpp
new file mode 100644
index 0000000..201c12e
--- /dev/null
+++ b/StayAwake/VersionUpdate.cpp
@@ -0,0 +1,101 @@
+#include "pch.h"
+
+#include "VersionUpdate.h"
+#include
+
+#pragma comment(lib, "winhttp.lib")
+
+wstring VersionUpdate::DownloadUrl(const wstring& host, const wstring& path)
+{
+ wstring response{};
+
+ HINTERNET hSession =
+ WinHttpOpen(L"StayAwake Update Checker/1.0",
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+
+ if (!hSession) return response;
+
+ HINTERNET hConnect = WinHttpConnect(hSession, host.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
+
+ if (!hConnect)
+ {
+ WinHttpCloseHandle(hSession);
+ return response;
+ }
+
+ HINTERNET hRequest =
+ WinHttpOpenRequest(hConnect, L"GET", path.c_str(), nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
+
+ if (!hRequest)
+ {
+ WinHttpCloseHandle(hConnect);
+ WinHttpCloseHandle(hSession);
+ return response;
+ }
+
+ // GitHub likes having a User-Agent.
+ BOOL bSuccess = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
+
+ if (bSuccess)
+ bSuccess = WinHttpReceiveResponse(hRequest, nullptr);
+
+ if (bSuccess)
+ {
+ DWORD dwSize = 0;
+
+ do
+ {
+ dwSize = 0;
+
+ if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
+ break;
+
+ if (dwSize == 0)
+ break;
+
+ string buffer(dwSize, '\0');
+
+ DWORD dwDownloaded = 0;
+
+ if (!WinHttpReadData(hRequest, buffer.data(), dwSize, &dwDownloaded))
+ break;
+
+ response.append(wstring(buffer.begin(), buffer.begin() + dwDownloaded));
+ } while (dwSize > 0);
+ }
+
+ WinHttpCloseHandle(hRequest);
+ WinHttpCloseHandle(hConnect);
+ WinHttpCloseHandle(hSession);
+
+ return response;
+}
+
+wstring VersionUpdate::ExtractTagNameValue(const wstring& json)
+{
+ const wchar_t* tagStart = wcsstr(json.c_str(), GITHUB_API_TAGNAME);
+ if (!tagStart) return L"";
+
+ const wchar_t* valStart = wcschr(tagStart + wcslen(GITHUB_API_TAGNAME), L'"');
+ if (!valStart) return L"";
+ ++valStart; // Move past the first quote
+
+ const wchar_t* valEnd = wcschr(valStart, L'"');
+ if (!valEnd) return L"";
+
+ return wstring (valStart, valEnd - valStart);
+}
+
+wstring VersionUpdate::GetVersion()
+{
+ auto json = DownloadUrl(GITHUB_API_HOST, GITHUB_APP_PATH);
+ if (json.empty()) return L"";
+
+ wstring tag{ ExtractTagNameValue(json) };
+ if (!tag.empty() && tag[0] == L'v')
+ tag.erase(0, 1); // Remove leading 'v'
+ return tag;
+}
diff --git a/StayAwake/VersionUpdate.h b/StayAwake/VersionUpdate.h
new file mode 100644
index 0000000..aa70b85
--- /dev/null
+++ b/StayAwake/VersionUpdate.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+using std::string;
+using std::wstring;
+
+constexpr auto GITHUB_API_HOST = L"api.github.com";
+constexpr auto GITHUB_APP_PATH = L"/repos/shriprem/StayAwake/releases/latest";
+constexpr auto GITHUB_API_TAGNAME = L"\"tag_name\"";
+
+class VersionUpdate
+{
+public:
+ wstring GetVersion();
+
+private:
+ wstring DownloadUrl(const wstring& host, const wstring& path);
+ wstring ExtractTagNameValue(const wstring& json);
+};
+
From 725f1be30a033850d841030b21d9c91652ac3cc5 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 4 Oct 2026 12:24:49 -0400
Subject: [PATCH 15/22] StayAwake main dialog: Rename resource IDs and dialog
events
Renaming to better reflect the purpose for controls and events
---
StayAwake/StayAwake.rc | 25 ++++++------
StayAwake/StayAwakeCore.cpp | 2 +-
StayAwake/StayAwakeDlg.cpp | 80 ++++++++++++++++++-------------------
StayAwake/StayAwakeDlg.h | 8 ++--
StayAwake/resource.h | 24 +++++------
5 files changed, 69 insertions(+), 70 deletions(-)
diff --git a/StayAwake/StayAwake.rc b/StayAwake/StayAwake.rc
index 51198d3..96f839d 100644
--- a/StayAwake/StayAwake.rc
+++ b/StayAwake/StayAwake.rc
@@ -90,21 +90,20 @@ EXSTYLE WS_EX_APPWINDOW
CAPTION "StayAwake"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
- PUSHBUTTON "Select multiple &options for random simulation",IDC_STAYAWAKE_KEYS_ROSTER_BTN,13,12,179,14
+ PUSHBUTTON "Select multiple &options for random simulation",IDC_SELECT_INPUT_OPTIONS_BTN,13,12,179,14
RTEXT "&Minimum Awake Seconds:",IDC_STATIC,1,35,100,8
- EDITTEXT IDC_STAYAWAKE_INTERVAL_MIN,102,32,27,13,ES_NUMBER
+ EDITTEXT IDC_INTERVAL_MIN_EDIT,102,32,27,13,ES_NUMBER
RTEXT "Ma&ximum Awake Seconds:",IDC_STATIC,1,55,100,8
- EDITTEXT IDC_STAYAWAKE_INTERVAL_MAX,102,52,27,13,ES_NUMBER
- PUSHBUTTON "Set &Timer",IDC_STAYAWAKE_SET_INTERVAL_BTN,132,32,60,34
- RTEXT "",IDC_STAYAWAKE_LAST_EVENT,1,74,143,8
- RTEXT "",IDC_STAYAWAKE_LAST_KEYCODE,1,82,143,8
- RTEXT "",IDC_STAYAWAKE_NEXT_EVENT,1,93,143,8
- PUSHBUTTON "&Pause",IDC_STAYAWAKE_PAUSE_RESUME_BTN,16,110,50,14
- PUSHBUTTON "Minimi&ze",IDC_MINIMIZE,74,110,50,14
- PUSHBUTTON "&Exit",IDC_EXIT,132,110,50,14
- CONTROL "&Launch application in minimized state",IDC_START_MINIMIZED,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,38,128,128,10
- PUSHBUTTON "...",IDC_ABOUT_BUTTON,188,126,15,13,BS_BITMAP
+ EDITTEXT IDC_INTERVAL_MAX_EDIT,102,52,27,13,ES_NUMBER
+ PUSHBUTTON "Set &Timer",IDC_SET_TIMER_BTN,132,32,60,34
+ RTEXT "",IDC_LAST_EVENT_TIME_INFO,1,74,143,8
+ RTEXT "",IDC_LAST_EVENT_INPUT_INFO,1,82,143,8
+ RTEXT "",IDC_NEXT_EVENT_TIME_INFO,1,93,143,8
+ PUSHBUTTON "&Pause",IDC_PAUSE_RESUME_BTN,16,110,50,14
+ PUSHBUTTON "Minimi&ze",IDC_MINIMIZE_BTN,74,110,50,14
+ PUSHBUTTON "&Exit",IDC_EXIT_BTN,132,110,50,14
+ CONTROL "&Start minimized",IDC_START_MINIMIZED_CBX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,128,70,10
+ PUSHBUTTON "...",IDC_ABOUT_BTN,188,126,15,13,BS_BITMAP
END
IDD_SELECT_KEYCODES_DIALOG DIALOGEX 0, 0, 330, 205
diff --git a/StayAwake/StayAwakeCore.cpp b/StayAwake/StayAwakeCore.cpp
index c05acc9..7d2d15f 100644
--- a/StayAwake/StayAwakeCore.cpp
+++ b/StayAwake/StayAwakeCore.cpp
@@ -46,7 +46,7 @@ bool StayAwakeCore::CheckSelectedKeyCodes(wstring sKeyCodes)
{
return (sKeyCodes.length() == LEN_ROSTER_KEYCODES &&
sKeyCodes != wstring(LEN_ROSTER_KEYCODES, L'0') &&
- sKeyCodes.find_first_not_of(L"01") == std::string::npos);
+ sKeyCodes.find_first_not_of(L"01") == string::npos);
}
bool StayAwakeCore::SaveSelectedKeyCodes(wstring sKeyCodes)
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index 4e48cc1..686c64f 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -37,15 +37,15 @@ BEGIN_MESSAGE_MAP(CStayAwakeDlg, CDialogEx)
ON_COMMAND(IDM_EXIT, &CStayAwakeDlg::OnExit)
ON_WM_DESTROY()
ON_WM_TIMER()
- ON_BN_CLICKED(IDC_MINIMIZE, &CStayAwakeDlg::OnMinimize)
- ON_BN_CLICKED(IDC_EXIT, &CStayAwakeDlg::OnExit)
- ON_BN_CLICKED(IDC_STAYAWAKE_KEYS_ROSTER_BTN, &CStayAwakeDlg::OnSetupKeyCodesRosterClicked)
- ON_BN_CLICKED(IDC_STAYAWAKE_SET_INTERVAL_BTN, &CStayAwakeDlg::OnSetInterval)
- ON_EN_KILLFOCUS(IDC_STAYAWAKE_INTERVAL_MIN, &CStayAwakeDlg::OnKillfocusIntervalMin)
- ON_EN_KILLFOCUS(IDC_STAYAWAKE_INTERVAL_MAX, &CStayAwakeDlg::OnKillfocusIntervalMax)
- ON_BN_CLICKED(IDC_ABOUT_BUTTON, &CStayAwakeDlg::OnClickedAboutButton)
- ON_BN_CLICKED(IDC_STAYAWAKE_PAUSE_RESUME_BTN, &CStayAwakeDlg::OnPauseResume)
- ON_BN_CLICKED(IDC_START_MINIMIZED, &CStayAwakeDlg::OnStartMinimized)
+ ON_BN_CLICKED(IDC_MINIMIZE_BTN, &CStayAwakeDlg::OnMinimize)
+ ON_BN_CLICKED(IDC_EXIT_BTN, &CStayAwakeDlg::OnExit)
+ ON_BN_CLICKED(IDC_SELECT_INPUT_OPTIONS_BTN, &CStayAwakeDlg::OnSelectInputOptionsClicked)
+ ON_BN_CLICKED(IDC_SET_TIMER_BTN, &CStayAwakeDlg::OnSetTimerClicked)
+ ON_EN_KILLFOCUS(IDC_INTERVAL_MIN_EDIT, &CStayAwakeDlg::OnKillfocusIntervalMin)
+ ON_EN_KILLFOCUS(IDC_INTERVAL_MAX_EDIT, &CStayAwakeDlg::OnKillfocusIntervalMax)
+ ON_BN_CLICKED(IDC_ABOUT_BTN, &CStayAwakeDlg::OnClickedAboutButton)
+ ON_BN_CLICKED(IDC_PAUSE_RESUME_BTN, &CStayAwakeDlg::OnPauseResumeClicked)
+ ON_BN_CLICKED(IDC_START_MINIMIZED_CBX, &CStayAwakeDlg::OnStartMinimized)
ON_WM_WTSSESSION_CHANGE()
END_MESSAGE_MAP()
@@ -84,16 +84,16 @@ BOOL CStayAwakeDlg::OnInitDialog()
InitConfigFilePath();
m_AwakeCore.InitIntervals(m_IntervalMinSeconds, m_IntervalMaxSeconds);
- SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MIN, m_IntervalMinSeconds, FALSE);
- SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MAX, m_IntervalMaxSeconds, FALSE);
+ SetDlgItemInt(IDC_INTERVAL_MIN_EDIT, m_IntervalMinSeconds, FALSE);
+ SetDlgItemInt(IDC_INTERVAL_MAX_EDIT, m_IntervalMaxSeconds, FALSE);
- Utils::addTooltip(theApp.m_hInstance, m_hWnd, IDC_STAYAWAKE_INTERVAL_MIN, L"", INTERVAL_TOOLTIP, 3, TRUE);
- Utils::addTooltip(theApp.m_hInstance, m_hWnd, IDC_STAYAWAKE_INTERVAL_MAX, L"", INTERVAL_TOOLTIP, 3, TRUE);
+ Utils::addTooltip(theApp.m_hInstance, m_hWnd, IDC_INTERVAL_MIN_EDIT, L"", INTERVAL_TOOLTIP, 3, TRUE);
+ Utils::addTooltip(theApp.m_hInstance, m_hWnd, IDC_INTERVAL_MAX_EDIT, L"", INTERVAL_TOOLTIP, 3, TRUE);
- SetDlgItemText(IDC_STAYAWAKE_PAUSE_RESUME_BTN, IsTimerPaused() ? BTN_TEXT_RESUME : BTN_TEXT_PAUSE);
+ SetDlgItemText(IDC_PAUSE_RESUME_BTN, IsTimerPaused() ? BTN_TEXT_RESUME : BTN_TEXT_PAUSE);
- Utils::addTooltip(theApp.m_hInstance, m_hWnd, IDC_ABOUT_BUTTON, L"", L"About StayAwake", 3, TRUE);
- Utils::loadBitmap(theApp.m_hInstance, m_hWnd, IDC_ABOUT_BUTTON, IDB_ABOUT_BITMAP);
+ Utils::addTooltip(theApp.m_hInstance, m_hWnd, IDC_ABOUT_BTN, L"", L"About StayAwake", 3, TRUE);
+ Utils::loadBitmap(theApp.m_hInstance, m_hWnd, IDC_ABOUT_BTN, IDB_ABOUT_BITMAP);
InitTrayIcon();
PostMessage(WM_POST_OPEN, 0, 0);
@@ -133,7 +133,7 @@ afx_msg LRESULT CStayAwakeDlg::OnPostOpen(WPARAM wParam, LPARAM lParam)
InitAwakes();
BOOL bMinimized = (m_AwakeCore.GetPreference(PREF_START_MINIMIZED, L"N") == L"Y");
- CheckDlgButton(IDC_START_MINIMIZED, bMinimized ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(IDC_START_MINIMIZED_CBX, bMinimized ? BST_CHECKED : BST_UNCHECKED);
if (bMinimized) MinimizeToTray();
return 0;
@@ -171,12 +171,12 @@ void CStayAwakeDlg::OnKillfocusIntervalMin()
{
int nInterval{};
- nInterval = GetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MIN, nullptr, FALSE);
+ nInterval = GetDlgItemInt(IDC_INTERVAL_MIN_EDIT, nullptr, FALSE);
if (nInterval < MIN_PERIOD || nInterval > MAX_PERIOD)
{
- Utils::showEditBalloonTip(GetDlgItem(IDC_STAYAWAKE_INTERVAL_MIN)->m_hWnd, INTERVAL_WARN_TITLE, INTERVAL_WARNING.c_str());
- SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MIN, m_IntervalMinSeconds, FALSE);
+ Utils::showEditBalloonTip(GetDlgItem(IDC_INTERVAL_MIN_EDIT)->m_hWnd, INTERVAL_WARN_TITLE, INTERVAL_WARNING.c_str());
+ SetDlgItemInt(IDC_INTERVAL_MIN_EDIT, m_IntervalMinSeconds, FALSE);
return;
}
@@ -188,12 +188,12 @@ void CStayAwakeDlg::OnKillfocusIntervalMax()
{
int nInterval{};
- nInterval = GetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MAX, nullptr, FALSE);
+ nInterval = GetDlgItemInt(IDC_INTERVAL_MAX_EDIT, nullptr, FALSE);
if (nInterval < MIN_PERIOD || nInterval > MAX_PERIOD)
{
- Utils::showEditBalloonTip(GetDlgItem(IDC_STAYAWAKE_INTERVAL_MAX)->m_hWnd, INTERVAL_WARN_TITLE, INTERVAL_WARNING.c_str());
- SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MAX, m_IntervalMaxSeconds, FALSE);
+ Utils::showEditBalloonTip(GetDlgItem(IDC_INTERVAL_MAX_EDIT)->m_hWnd, INTERVAL_WARN_TITLE, INTERVAL_WARNING.c_str());
+ SetDlgItemInt(IDC_INTERVAL_MAX_EDIT, m_IntervalMaxSeconds, FALSE);
return;
}
@@ -201,7 +201,7 @@ void CStayAwakeDlg::OnKillfocusIntervalMax()
}
-void CStayAwakeDlg::OnSetupKeyCodesRosterClicked()
+void CStayAwakeDlg::OnSelectInputOptionsClicked()
{
CSelectKeyCodesDlg dlgSelectKeyCodes(this, m_AwakeCore);
@@ -253,7 +253,7 @@ void CStayAwakeDlg::InitAwakes()
InitRosterKeyCodes();
SimulateAwakeKeyPress();
- SetDlgItemText(IDC_STAYAWAKE_PAUSE_RESUME_BTN, BTN_TEXT_PAUSE);
+ SetDlgItemText(IDC_PAUSE_RESUME_BTN, BTN_TEXT_PAUSE);
m_AwakeCore.SetPreference(PREF_AWAKE_PAUSED, L"N");
}
@@ -276,8 +276,8 @@ void CStayAwakeDlg::InitTrayIcon()
void CStayAwakeDlg::MinimizeToTray()
{
if (m_bMinimized) return;
- SendDlgItemMessage(IDC_STAYAWAKE_INTERVAL_MIN, EM_HIDEBALLOONTIP, 0, 0);
- SendDlgItemMessage(IDC_STAYAWAKE_INTERVAL_MAX, EM_HIDEBALLOONTIP, 0, 0);
+ SendDlgItemMessage(IDC_INTERVAL_MIN_EDIT, EM_HIDEBALLOONTIP, 0, 0);
+ SendDlgItemMessage(IDC_INTERVAL_MAX_EDIT, EM_HIDEBALLOONTIP, 0, 0);
if (!Shell_NotifyIcon(NIM_ADD, &m_TrayData))
MessageBox(L"Unable to Display Tray Icon", L"Error!");
@@ -320,7 +320,7 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
if (m_bSystemLocked)
{
- SetDlgItemText(IDC_STAYAWAKE_NEXT_EVENT, L"PAUSED since Windows is LOCKED");
+ SetDlgItemText(IDC_NEXT_EVENT_TIME_INFO, L"PAUSED since Windows is LOCKED");
return;
}
@@ -331,14 +331,14 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
SYSTEMTIME lastTime{};
GetLocalTime(&lastTime);
- SetDlgItemText(IDC_STAYAWAKE_LAST_EVENT, Utils::formatSystemTime(lastTime, L"Last StayAwake event").c_str());
+ SetDlgItemText(IDC_LAST_EVENT_TIME_INFO, Utils::formatSystemTime(lastTime, L"Last StayAwake event").c_str());
#ifdef DEBUG_DISPLAY_IDLE_TICKS
Sleep((rand() % 20) + 1); // simulate a small delay to get a more accurate idle time
ULONGLONG idleTime = m_AwakeCore.GetIdleTimeMilliseconds();
- SetDlgItemText(IDC_STAYAWAKE_LAST_KEYCODE, (L"[" + sAwakeKeyCode + L":" + to_wstring(idleTime) + L" ms]").c_str());
+ SetDlgItemText(IDC_LAST_EVENT_INPUT_INFO, (L"[" + sAwakeKeyCode + L":" + to_wstring(idleTime) + L" ms]").c_str());
#else
- SetDlgItemText(IDC_STAYAWAKE_LAST_KEYCODE, (L"[" + sAwakeKeyCode + L"]").c_str());
+ SetDlgItemText(IDC_LAST_EVENT_INPUT_INFO, (L"[" + sAwakeKeyCode + L"]").c_str());
#endif
UINT nTimerSeconds{ m_IntervalMinSeconds };
@@ -350,11 +350,11 @@ void CStayAwakeDlg::SimulateAwakeKeyPress()
SYSTEMTIME nextTime{};
GetSystemTime(&nextTime);
Utils::addSecondsToTime(nextTime, nTimerSeconds);
- SetDlgItemText(IDC_STAYAWAKE_NEXT_EVENT, Utils::formatSystemTime(nextTime, L"Next StayAwake event").c_str());
+ SetDlgItemText(IDC_NEXT_EVENT_TIME_INFO, Utils::formatSystemTime(nextTime, L"Next StayAwake event").c_str());
}
-void CStayAwakeDlg::OnSetInterval()
+void CStayAwakeDlg::OnSetTimerClicked()
{
OnKillfocusIntervalMin();
OnKillfocusIntervalMax();
@@ -365,8 +365,8 @@ void CStayAwakeDlg::OnSetInterval()
m_IntervalMinSeconds = m_IntervalMaxSeconds;
m_IntervalMaxSeconds = nTemp;
- SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MIN, m_IntervalMinSeconds, FALSE);
- SetDlgItemInt(IDC_STAYAWAKE_INTERVAL_MAX, m_IntervalMaxSeconds, FALSE);
+ SetDlgItemInt(IDC_INTERVAL_MIN_EDIT, m_IntervalMinSeconds, FALSE);
+ SetDlgItemInt(IDC_INTERVAL_MAX_EDIT, m_IntervalMaxSeconds, FALSE);
}
m_AwakeCore.SetPreference(PREF_INTERVAL_MINIMUM, to_wstring(m_IntervalMinSeconds));
@@ -382,7 +382,7 @@ void CStayAwakeDlg::OnClickedAboutButton()
dlgAbout.DoModal();
}
-void CStayAwakeDlg::OnPauseResume()
+void CStayAwakeDlg::OnPauseResumeClicked()
{
if (IsTimerPaused())
{
@@ -392,7 +392,7 @@ void CStayAwakeDlg::OnPauseResume()
{
KillTimer(m_TimerID);
- SetDlgItemText(IDC_STAYAWAKE_PAUSE_RESUME_BTN, BTN_TEXT_RESUME);
+ SetDlgItemText(IDC_PAUSE_RESUME_BTN, BTN_TEXT_RESUME);
m_AwakeCore.SetPreference(PREF_AWAKE_PAUSED, L"Y");
ShowPausedInfo(false);
}
@@ -406,14 +406,14 @@ bool CStayAwakeDlg::IsTimerPaused()
void CStayAwakeDlg::ShowPausedInfo(bool both)
{
if (both)
- SetDlgItemText(IDC_STAYAWAKE_LAST_EVENT, L"Last StayAwake event: PAUSED");
+ SetDlgItemText(IDC_LAST_EVENT_TIME_INFO, L"Last StayAwake event: PAUSED");
- SetDlgItemText(IDC_STAYAWAKE_NEXT_EVENT, L"Next StayAwake event: PAUSED");
+ SetDlgItemText(IDC_NEXT_EVENT_TIME_INFO, L"Next StayAwake event: PAUSED");
}
void CStayAwakeDlg::OnStartMinimized()
{
- m_AwakeCore.SetPreference(PREF_START_MINIMIZED, (IsDlgButtonChecked(IDC_START_MINIMIZED) == BST_CHECKED) ? L"Y" : L"N");
+ m_AwakeCore.SetPreference(PREF_START_MINIMIZED, (IsDlgButtonChecked(IDC_START_MINIMIZED_CBX) == BST_CHECKED) ? L"Y" : L"N");
}
void CStayAwakeDlg::OnSessionChange(UINT nSessionState, UINT nId)
diff --git a/StayAwake/StayAwakeDlg.h b/StayAwake/StayAwakeDlg.h
index 10a18b2..af9489c 100644
--- a/StayAwake/StayAwakeDlg.h
+++ b/StayAwake/StayAwakeDlg.h
@@ -8,7 +8,7 @@
#define WM_TRAY_NOTIFY (WM_APP + 2)
#define WM_RESTORE_DIALOG theApp.WM_SHOWFIRSTINSTANCE
-#define DEBUG_DISPLAY_IDLE_TICKS
+//#define DEBUG_DISPLAY_IDLE_TICKS
constexpr auto BTN_TEXT_PAUSE = L"&Pause";
constexpr auto BTN_TEXT_RESUME = L"&Resume";
@@ -55,12 +55,12 @@ class CStayAwakeDlg : public CDialogEx
afx_msg LRESULT OnPostOpen(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnTrayNotify(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnRestoreDialog(WPARAM wParam, LPARAM lParam);
- afx_msg void OnSetupKeyCodesRosterClicked();
+ afx_msg void OnSelectInputOptionsClicked();
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg void OnKillfocusIntervalMin();
afx_msg void OnKillfocusIntervalMax();
- afx_msg void OnSetInterval();
- afx_msg void OnPauseResume();
+ afx_msg void OnSetTimerClicked();
+ afx_msg void OnPauseResumeClicked();
afx_msg void OnMinimize() { MinimizeToTray(); };
afx_msg void OnClickedAboutButton();
afx_msg void OnEnter() { /* Ignore ENTER key press */ };
diff --git a/StayAwake/resource.h b/StayAwake/resource.h
index 59df107..fb38d5e 100644
--- a/StayAwake/resource.h
+++ b/StayAwake/resource.h
@@ -12,18 +12,18 @@
#define IDR_MAINFRAME 133
#define IDB_ABOUT_BITMAP 134
-#define IDC_STAYAWAKE_KEYS_ROSTER_BTN 1001
-#define IDC_STAYAWAKE_INTERVAL_MIN 1002
-#define IDC_STAYAWAKE_INTERVAL_MAX 1003
-#define IDC_STAYAWAKE_SET_INTERVAL_BTN 1004
-#define IDC_STAYAWAKE_LAST_EVENT 1005
-#define IDC_STAYAWAKE_NEXT_EVENT 1006
-#define IDC_STAYAWAKE_LAST_KEYCODE 1007
-#define IDC_STAYAWAKE_PAUSE_RESUME_BTN 1011
-#define IDC_MINIMIZE 1012
-#define IDC_EXIT 1013
-#define IDC_START_MINIMIZED 1014
-#define IDC_ABOUT_BUTTON 1015
+#define IDC_SELECT_INPUT_OPTIONS_BTN 1001
+#define IDC_INTERVAL_MIN_EDIT 1002
+#define IDC_INTERVAL_MAX_EDIT 1003
+#define IDC_SET_TIMER_BTN 1004
+#define IDC_LAST_EVENT_TIME_INFO 1005
+#define IDC_LAST_EVENT_INPUT_INFO 1006
+#define IDC_NEXT_EVENT_TIME_INFO 1007
+#define IDC_PAUSE_RESUME_BTN 1011
+#define IDC_MINIMIZE_BTN 1012
+#define IDC_EXIT_BTN 1013
+#define IDC_START_MINIMIZED_CBX 1014
+#define IDC_ABOUT_BTN 1015
#define IDC_KEY_SCROLL_LOCK 1101
#define IDC_KEY_VOLUME_UP_DOWN 1102
From 68a46ec1030b67356eb4b9f1c2eb3020457f9b05 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 4 Oct 2026 13:32:03 -0400
Subject: [PATCH 16/22] Add 'Update Check' button
---
StayAwake/StayAwake.rc | 15 ++++++++-------
StayAwake/StayAwakeDlg.cpp | 9 ++++++++-
StayAwake/StayAwakeDlg.h | 2 ++
StayAwake/resource.h | 3 ++-
4 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/StayAwake/StayAwake.rc b/StayAwake/StayAwake.rc
index 96f839d..3d16906 100644
--- a/StayAwake/StayAwake.rc
+++ b/StayAwake/StayAwake.rc
@@ -84,7 +84,7 @@ BEGIN
DEFPUSHBUTTON "OK",IDOK,80,100,50,14,WS_GROUP
END
-IDD_STAYAWAKE_DIALOG DIALOGEX 0, 0, 205, 144
+IDD_STAYAWAKE_DIALOG DIALOGEX 0, 0, 205, 160
STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "StayAwake"
@@ -99,11 +99,12 @@ BEGIN
RTEXT "",IDC_LAST_EVENT_TIME_INFO,1,74,143,8
RTEXT "",IDC_LAST_EVENT_INPUT_INFO,1,82,143,8
RTEXT "",IDC_NEXT_EVENT_TIME_INFO,1,93,143,8
- PUSHBUTTON "&Pause",IDC_PAUSE_RESUME_BTN,16,110,50,14
- PUSHBUTTON "Minimi&ze",IDC_MINIMIZE_BTN,74,110,50,14
- PUSHBUTTON "&Exit",IDC_EXIT_BTN,132,110,50,14
- CONTROL "&Start minimized",IDC_START_MINIMIZED_CBX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,128,70,10
- PUSHBUTTON "...",IDC_ABOUT_BTN,188,126,15,13,BS_BITMAP
+ PUSHBUTTON "&Pause",IDC_PAUSE_RESUME_BTN,10,110,55,14
+ PUSHBUTTON "Minimi&ze",IDC_MINIMIZE_BTN,75,110,55,14
+ PUSHBUTTON "&Exit",IDC_EXIT_BTN,140,110,55,14
+ CONTROL "&Start minimized",IDC_START_MINIMIZED_CBX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,77,125,70,10
+ PUSHBUTTON "&Update Check",IDC_UPDATE_CHECK_BTN,75,144,55,14
+ PUSHBUTTON "...",IDC_ABOUT_BTN,188,144,15,13,BS_BITMAP
END
IDD_SELECT_KEYCODES_DIALOG DIALOGEX 0, 0, 330, 205
@@ -203,7 +204,7 @@ BEGIN
IDD_STAYAWAKE_DIALOG, DIALOG
BEGIN
RIGHTMARGIN, 203
- BOTTOMMARGIN, 142
+ BOTTOMMARGIN, 158
END
IDD_SELECT_KEYCODES_DIALOG, DIALOG
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index 686c64f..ec792de 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -1,10 +1,12 @@
#include "pch.h"
#include "framework.h"
+#include "afxdialogex.h"
+
#include "StayAwake.h"
#include "StayAwakeDlg.h"
#include "SelectKeyCodesDlg.h"
#include "StayAwakeAboutDlg.h"
-#include "afxdialogex.h"
+#include "VersionUpdate.h"
#include "Utils.h"
#include
@@ -47,6 +49,7 @@ BEGIN_MESSAGE_MAP(CStayAwakeDlg, CDialogEx)
ON_BN_CLICKED(IDC_PAUSE_RESUME_BTN, &CStayAwakeDlg::OnPauseResumeClicked)
ON_BN_CLICKED(IDC_START_MINIMIZED_CBX, &CStayAwakeDlg::OnStartMinimized)
ON_WM_WTSSESSION_CHANGE()
+ ON_BN_CLICKED(IDC_UPDATE_CHECK_BTN, &CStayAwakeDlg::OnUpdateCheckClicked)
END_MESSAGE_MAP()
@@ -431,3 +434,7 @@ void CStayAwakeDlg::OnSessionChange(UINT nSessionState, UINT nId)
CDialogEx::OnSessionChange(nSessionState, nId);
}
+
+void CStayAwakeDlg::OnUpdateCheckClicked()
+{
+}
diff --git a/StayAwake/StayAwakeDlg.h b/StayAwake/StayAwakeDlg.h
index af9489c..4fd4760 100644
--- a/StayAwake/StayAwakeDlg.h
+++ b/StayAwake/StayAwakeDlg.h
@@ -82,4 +82,6 @@ class CStayAwakeDlg : public CDialogEx
void ShowPausedInfo(bool both);
bool IsTimerPaused();
+public:
+ afx_msg void OnUpdateCheckClicked();
};
diff --git a/StayAwake/resource.h b/StayAwake/resource.h
index fb38d5e..416a530 100644
--- a/StayAwake/resource.h
+++ b/StayAwake/resource.h
@@ -23,7 +23,8 @@
#define IDC_MINIMIZE_BTN 1012
#define IDC_EXIT_BTN 1013
#define IDC_START_MINIMIZED_CBX 1014
-#define IDC_ABOUT_BTN 1015
+#define IDC_UPDATE_CHECK_BTN 1015
+#define IDC_ABOUT_BTN 1016
#define IDC_KEY_SCROLL_LOCK 1101
#define IDC_KEY_VOLUME_UP_DOWN 1102
From ed69537764858c444b4878fe2ae15fbd033fdc7d Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 4 Oct 2026 22:40:52 -0400
Subject: [PATCH 17/22] Add control IDs & layout resource for Update Check
dialog
---
StayAwake/StayAwake.rc | 59 ++++++++++++++++++++++++++++++------------
StayAwake/resource.h | 19 +++++++++-----
2 files changed, 55 insertions(+), 23 deletions(-)
diff --git a/StayAwake/StayAwake.rc b/StayAwake/StayAwake.rc
index 3d16906..115ba37 100644
--- a/StayAwake/StayAwake.rc
+++ b/StayAwake/StayAwake.rc
@@ -21,6 +21,7 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
@@ -28,12 +29,12 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// TEXTINCLUDE
//
-1 TEXTINCLUDE
+1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
-2 TEXTINCLUDE
+2 TEXTINCLUDE
BEGIN
"#ifndef APSTUDIO_INVOKED\r\n"
"#include ""targetver.h""\r\n"
@@ -43,7 +44,7 @@ BEGIN
"\0"
END
-3 TEXTINCLUDE
+3 TEXTINCLUDE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
@@ -103,8 +104,8 @@ BEGIN
PUSHBUTTON "Minimi&ze",IDC_MINIMIZE_BTN,75,110,55,14
PUSHBUTTON "&Exit",IDC_EXIT_BTN,140,110,55,14
CONTROL "&Start minimized",IDC_START_MINIMIZED_CBX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,77,125,70,10
- PUSHBUTTON "&Update Check",IDC_UPDATE_CHECK_BTN,75,144,55,14
- PUSHBUTTON "...",IDC_ABOUT_BTN,188,144,15,13,BS_BITMAP
+ PUSHBUTTON "&Update Check",IDC_UPDATE_CHECK_BTN,75,142,55,14
+ PUSHBUTTON "...",IDC_ABOUT_BTN,188,142,15,13,BS_BITMAP
END
IDD_SELECT_KEYCODES_DIALOG DIALOGEX 0, 0, 330, 205
@@ -112,17 +113,17 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM
CAPTION "Select multiple simulation options"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
- CONTROL "&Scroll Lock cycling",IDC_KEY_SCROLL_LOCK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,15,90,10
+ CONTROL "&Scroll Lock cycling",IDC_KEY_SCROLL_LOCK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,15,90,10
CONTROL "&Volume Down && Up",IDC_KEY_VOLUME_UP_DOWN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,30,90,10
- CONTROL "Unassigned Key #&1",IDC_KEY_UNASSIGNED_1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,45,90,10
- CONTROL "Unassigned Key #&2",IDC_KEY_UNASSIGNED_2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,60,90,10
- CONTROL "Unassigned Key #&3",IDC_KEY_UNASSIGNED_3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,75,90,10
- CONTROL "Unassigned Key #&4",IDC_KEY_UNASSIGNED_4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,90,90,10
- CONTROL "Unassigned Key #&5",IDC_KEY_UNASSIGNED_5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,105,90,10
- CONTROL "Unassigned Key #&6",IDC_KEY_UNASSIGNED_6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,120,90,10
- CONTROL "Unassigned Key #&7",IDC_KEY_UNASSIGNED_7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,135,90,10
- CONTROL "Unassigned Key #&8",IDC_KEY_UNASSIGNED_8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,150,90,10
- CONTROL "Unassigned Key #&9",IDC_KEY_UNASSIGNED_9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,165,90,10
+ CONTROL "Unassigned Key #&1",IDC_KEY_UNASSIGNED_1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,45,90,10
+ CONTROL "Unassigned Key #&2",IDC_KEY_UNASSIGNED_2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,60,90,10
+ CONTROL "Unassigned Key #&3",IDC_KEY_UNASSIGNED_3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,75,90,10
+ CONTROL "Unassigned Key #&4",IDC_KEY_UNASSIGNED_4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,90,90,10
+ CONTROL "Unassigned Key #&5",IDC_KEY_UNASSIGNED_5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,105,90,10
+ CONTROL "Unassigned Key #&6",IDC_KEY_UNASSIGNED_6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,120,90,10
+ CONTROL "Unassigned Key #&7",IDC_KEY_UNASSIGNED_7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,135,90,10
+ CONTROL "Unassigned Key #&8",IDC_KEY_UNASSIGNED_8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,150,90,10
+ CONTROL "Unassigned Key #&9",IDC_KEY_UNASSIGNED_9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,165,90,10
CONTROL "Unassigned Key #1&0",IDC_KEY_UNASSIGNED_10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,180,90,10
CONTROL "Extended FN Key: F13",IDC_KEY_F13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,15,90,10
CONTROL "Extended FN Key: F14",IDC_KEY_F14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,30,90,10
@@ -139,13 +140,29 @@ BEGIN
CONTROL "One-pixel Mouse &Jiggle",IDC_MOUSE_MOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,15,90,10
CONTROL "Zero-pixel Mouse &Move",IDC_MOUSE_MOVE_ZERO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,225,30,90,10
PUSHBUTTON "Select &None",IDC_KEY_SELECT_NONE_BTN,225,55,85,14
- PUSHBUTTON "Select All &Unassigned", IDC_KEY_SELECT_ALL_UNASSGND_BTN,225,80,85,14
- PUSHBUTTON "Select All Ext. &FN Keys", IDC_KEY_SELECT_ALL_EXT_FN_BTN,225,105,85,14
+ PUSHBUTTON "Select All &Unassigned",IDC_KEY_SELECT_ALL_UNASSGND_BTN,225,80,85,14
+ PUSHBUTTON "Select All Ext. &FN Keys",IDC_KEY_SELECT_ALL_EXT_FN_BTN,225,105,85,14
PUSHBUTTON "Select &All",IDC_KEY_SELECT_ALL_BTN,225,130,85,14
DEFPUSHBUTTON "A&pply",IDOK,225,155,85,14
PUSHBUTTON "&Cancel",IDCANCEL,225,180,85,14
END
+IDD_UPDATE_CHECK_DIALOG DIALOGEX 0, 0, 170, 115
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Check for Updates"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ RTEXT "Installed Version:",IDC_STATIC,5,12,80,8
+ LTEXT "",IDC_INSTALLED_VERSION_INFO,86,12,60,8
+ RTEXT "Latest Version:",IDC_STATIC,5,27,80,8
+ LTEXT "",IDC_LATEST_VERSION_INFO,86,27,60,8
+ CTEXT "",IDC_VERSION_CHECK_STATUS,5,42,160,8
+ CONTROL "Stay Awake - Latest Release",IDC_LATEST_RELEASE_URL,"SysLink",WS_TABSTOP,38,57,94,8
+ CONTROL "Check for &updates when StayAwake starts",IDC_START_VERSION_CHECK_CBX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,77,150,10
+ PUSHBUTTON "&Close",IDCANCEL,60,92,50,14
+END
+
+
/////////////////////////////////////////////////////////////////////////////
//
// Version
@@ -209,6 +226,14 @@ BEGIN
IDD_SELECT_KEYCODES_DIALOG, DIALOG
BEGIN
+ RIGHTMARGIN, 329
+ BOTTOMMARGIN, 204
+ END
+
+ IDD_UPDATE_CHECK_DIALOG, DIALOG
+ BEGIN
+ RIGHTMARGIN, 169
+ BOTTOMMARGIN, 114
END
END
#endif // APSTUDIO_INVOKED
diff --git a/StayAwake/resource.h b/StayAwake/resource.h
index 416a530..85881bc 100644
--- a/StayAwake/resource.h
+++ b/StayAwake/resource.h
@@ -7,6 +7,7 @@
#define IDS_ABOUTBOX 101
#define IDD_STAYAWAKE_DIALOG 102
#define IDD_SELECT_KEYCODES_DIALOG 103
+#define IDD_UPDATE_CHECK_DIALOG 104
#define IDR_MENU1 130
#define IDI_ICON1 133
#define IDR_MAINFRAME 133
@@ -58,12 +59,18 @@
#define IDC_KEY_SELECT_ALL_EXT_FN_BTN 1153
#define IDC_KEY_SELECT_ALL_BTN 1154
-#define IDC_ABOUT_NAME 1201
-#define IDC_ABOUT_VERSION 1202
-#define IDC_ABOUT_BUILD_TIME 1203
-#define IDC_ABOUT_ATTRIBUTION 1204
-#define IDC_ABOUT_PROD_URL 1205
-#define IDC_ABOUT_PROD_REQ 1206
+#define IDC_INSTALLED_VERSION_INFO 1201
+#define IDC_LATEST_VERSION_INFO 1202
+#define IDC_VERSION_CHECK_STATUS 1203
+#define IDC_LATEST_RELEASE_URL 1204
+#define IDC_START_VERSION_CHECK_CBX 1205
+
+#define IDC_ABOUT_NAME 1301
+#define IDC_ABOUT_VERSION 1302
+#define IDC_ABOUT_BUILD_TIME 1303
+#define IDC_ABOUT_ATTRIBUTION 1304
+#define IDC_ABOUT_PROD_URL 1305
+#define IDC_ABOUT_PROD_REQ 1306
#define IDM_EXIT 32771
#define IDM_RESTORE 32772
From 8c468b3a247d64d6a20fadf15bb98ceb3fa71157 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 4 Oct 2026 22:42:44 -0400
Subject: [PATCH 18/22] Implement functionality for Update Check dialog
---
StayAwake/SelectKeyCodesDlg.h | 1 -
StayAwake/StayAwake.vcxproj | 2 +
StayAwake/StayAwake.vcxproj.filters | 6 +++
StayAwake/StayAwakeAboutDlg.cpp | 3 +-
StayAwake/StayAwakeCore.h | 1 +
StayAwake/StayAwakeDlg.cpp | 7 ++-
StayAwake/UpdateCheckDlg.cpp | 76 +++++++++++++++++++++++++++++
StayAwake/UpdateCheckDlg.h | 34 +++++++++++++
8 files changed, 127 insertions(+), 3 deletions(-)
create mode 100644 StayAwake/UpdateCheckDlg.cpp
create mode 100644 StayAwake/UpdateCheckDlg.h
diff --git a/StayAwake/SelectKeyCodesDlg.h b/StayAwake/SelectKeyCodesDlg.h
index 4423745..65442e5 100644
--- a/StayAwake/SelectKeyCodesDlg.h
+++ b/StayAwake/SelectKeyCodesDlg.h
@@ -33,6 +33,5 @@ class CSelectKeyCodesDlg : public CDialogEx
DECLARE_MESSAGE_MAP()
-private:
void CheckAllBoxes(const wstring& sSelectedKeyCodes, int start = IDC_KEY_SCROLL_LOCK, int endNext = IDC_KEY_SCROLL_LOCK + LEN_ROSTER_KEYCODES);
};
diff --git a/StayAwake/StayAwake.vcxproj b/StayAwake/StayAwake.vcxproj
index b0ce24e..cc452eb 100644
--- a/StayAwake/StayAwake.vcxproj
+++ b/StayAwake/StayAwake.vcxproj
@@ -222,6 +222,7 @@
+
@@ -237,6 +238,7 @@
Create
Create
+
diff --git a/StayAwake/StayAwake.vcxproj.filters b/StayAwake/StayAwake.vcxproj.filters
index 7fa4b7b..d7fb28f 100644
--- a/StayAwake/StayAwake.vcxproj.filters
+++ b/StayAwake/StayAwake.vcxproj.filters
@@ -45,6 +45,9 @@
Header Files
+
+ Header Files
+
@@ -71,6 +74,9 @@
Source Files
+
+ Source Files
+
diff --git a/StayAwake/StayAwakeAboutDlg.cpp b/StayAwake/StayAwakeAboutDlg.cpp
index 50c6094..c326c4e 100644
--- a/StayAwake/StayAwakeAboutDlg.cpp
+++ b/StayAwake/StayAwakeAboutDlg.cpp
@@ -46,7 +46,8 @@ BOOL CAboutDlg::OnInitDialog()
afx_msg void CAboutDlg::OnProdUrlClick(NMHDR* pNotifyStruct, LRESULT* result)
{
- ShellExecute(NULL, L"open", Utils::getVersionInfo(theApp.m_hInstance, L"CompanyName").c_str(), NULL, NULL, SW_SHOW);
+ ShellExecute(nullptr, L"open", Utils::getVersionInfo(theApp.m_hInstance, L"CompanyName").c_str(), nullptr, nullptr, SW_SHOW);
+ *result = 0;
}
diff --git a/StayAwake/StayAwakeCore.h b/StayAwake/StayAwakeCore.h
index 408d450..4d74024 100644
--- a/StayAwake/StayAwakeCore.h
+++ b/StayAwake/StayAwakeCore.h
@@ -27,6 +27,7 @@ constexpr auto PREF_MULTI_INSTANCE = L"MultipleInstancesAllowed";
constexpr auto PREF_INTERVAL_LEGACY = L"TimerIntervalInSeconds";
constexpr auto PREF_INTERVAL_MINIMUM = L"MinimumIntervalInSeconds";
constexpr auto PREF_INTERVAL_MAXIMUM = L"MaximumIntervalInSeconds";
+constexpr auto PREF_START_UPDATE_CHECK = L"StartCheckUpdates";
constexpr auto MIN_PERIOD{ 10 };
constexpr auto MAX_PERIOD{ 9990 };
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index ec792de..4cf6354 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -6,7 +6,7 @@
#include "StayAwakeDlg.h"
#include "SelectKeyCodesDlg.h"
#include "StayAwakeAboutDlg.h"
-#include "VersionUpdate.h"
+#include "UpdateCheckDlg.h"
#include "Utils.h"
#include
@@ -135,6 +135,9 @@ afx_msg LRESULT CStayAwakeDlg::OnPostOpen(WPARAM wParam, LPARAM lParam)
else
InitAwakes();
+ if (m_AwakeCore.GetPreference(PREF_START_UPDATE_CHECK, L"N") == L"Y")
+ OnUpdateCheckClicked();
+
BOOL bMinimized = (m_AwakeCore.GetPreference(PREF_START_MINIMIZED, L"N") == L"Y");
CheckDlgButton(IDC_START_MINIMIZED_CBX, bMinimized ? BST_CHECKED : BST_UNCHECKED);
if (bMinimized) MinimizeToTray();
@@ -437,4 +440,6 @@ void CStayAwakeDlg::OnSessionChange(UINT nSessionState, UINT nId)
void CStayAwakeDlg::OnUpdateCheckClicked()
{
+ CUpdateCheckDlg dlgUpdateCheck(this, m_AwakeCore);
+ dlgUpdateCheck.DoModal();
}
diff --git a/StayAwake/UpdateCheckDlg.cpp b/StayAwake/UpdateCheckDlg.cpp
new file mode 100644
index 0000000..819844a
--- /dev/null
+++ b/StayAwake/UpdateCheckDlg.cpp
@@ -0,0 +1,76 @@
+#include "pch.h"
+
+#include "afxdialogex.h"
+#include "StayAwake.h"
+#include "UpdateCheckDlg.h"
+#include "Utils.h"
+#include "VersionUpdate.h"
+
+
+// CUpdateCheckDlg dialog
+
+IMPLEMENT_DYNAMIC(CUpdateCheckDlg, CDialogEx)
+
+CUpdateCheckDlg::CUpdateCheckDlg(CWnd* pParent, StayAwakeCore& pAwakeCore)
+ : CDialogEx(IDD_UPDATE_CHECK_DIALOG, pParent)
+ , m_AwakeCore(pAwakeCore)
+{
+ m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
+}
+
+CUpdateCheckDlg::~CUpdateCheckDlg()
+{
+}
+
+void CUpdateCheckDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+}
+
+
+BEGIN_MESSAGE_MAP(CUpdateCheckDlg, CDialogEx)
+ ON_NOTIFY(NM_CLICK, IDC_LATEST_RELEASE_URL, &CUpdateCheckDlg::OnClickLatestReleaseUrl)
+ ON_BN_CLICKED(IDC_START_VERSION_CHECK_CBX, &CUpdateCheckDlg::OnClickedStartVersionCheck)
+ ON_NOTIFY(NM_RETURN, IDC_LATEST_RELEASE_URL, &CUpdateCheckDlg::OnReturnLatestReleaseUrl)
+END_MESSAGE_MAP()
+
+
+// CUpdateCheckDlg message handlers
+
+BOOL CUpdateCheckDlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ wstring currentVersionInfo{ Utils::getVersionInfo(theApp.m_hInstance, L"FileVersion") };
+
+ VersionUpdate versionUpdate;
+ wstring latestVersionInfo{ versionUpdate.GetVersion() };
+
+ SetDlgItemText(IDC_INSTALLED_VERSION_INFO, currentVersionInfo.c_str());
+ SetDlgItemText(IDC_LATEST_VERSION_INFO, latestVersionInfo.c_str());
+ SetDlgItemText(IDC_VERSION_CHECK_STATUS,
+ (latestVersionInfo == currentVersionInfo) ?
+ L"This is the latest available version." :
+ L"A newer version is available. Click the link below.");
+
+ BOOL bStartUpdateCheck = (m_AwakeCore.GetPreference(PREF_START_UPDATE_CHECK, L"N") == L"Y");
+ CheckDlgButton(IDC_START_VERSION_CHECK_CBX, bStartUpdateCheck ? BST_CHECKED : BST_UNCHECKED);
+
+ return TRUE; // return TRUE unless you set the focus to a control
+}
+
+void CUpdateCheckDlg::OnClickLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ ShellExecute(nullptr, L"open", L"https://github.com/shriprem/StayAwake/releases/latest", nullptr, nullptr, SW_SHOW);
+ *pResult = 0;
+}
+
+void CUpdateCheckDlg::OnReturnLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ OnClickLatestReleaseUrl(pNMHDR, pResult);
+}
+
+void CUpdateCheckDlg::OnClickedStartVersionCheck()
+{
+ m_AwakeCore.SetPreference(PREF_START_UPDATE_CHECK, (IsDlgButtonChecked(IDC_START_VERSION_CHECK_CBX) == BST_CHECKED) ? L"Y" : L"N");
+}
diff --git a/StayAwake/UpdateCheckDlg.h b/StayAwake/UpdateCheckDlg.h
new file mode 100644
index 0000000..428d7bc
--- /dev/null
+++ b/StayAwake/UpdateCheckDlg.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "afxdialogex.h"
+#include "StayAwakeDlg.h"
+
+
+// CUpdateCheckDlg dialog
+
+class CUpdateCheckDlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CUpdateCheckDlg)
+
+public:
+ explicit CUpdateCheckDlg(CWnd* pParent, StayAwakeCore& pAwakeCore);
+ virtual ~CUpdateCheckDlg();
+
+// Dialog Data
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_UPDATE_CHECK_DIALOG };
+#endif
+
+protected:
+ StayAwakeCore m_AwakeCore{};
+ HICON m_hIcon;
+
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+
+ virtual BOOL OnInitDialog();
+ afx_msg void OnClickLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnClickedStartVersionCheck();
+ afx_msg void OnReturnLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult);
+
+ DECLARE_MESSAGE_MAP()
+};
From d504cb104b21d9d9e8741f2f84566234a5e2f49a Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 4 Oct 2026 23:17:59 -0400
Subject: [PATCH 19/22] Split InputOptions.md from README.md
---
InputOptions.md | 25 +++++++++++++++++++++++++
README.md | 31 ++-----------------------------
2 files changed, 27 insertions(+), 29 deletions(-)
create mode 100644 InputOptions.md
diff --git a/InputOptions.md b/InputOptions.md
new file mode 100644
index 0000000..064c434
--- /dev/null
+++ b/InputOptions.md
@@ -0,0 +1,25 @@
+## Input Simulation Options
+
+
+
+StayAwake can randomly pick any of the 12 key codes during each simulation. In this popup, users can specify _at least_ one OR a select few OR all of the 12 key codes to be included in the simulation roster.
+
+#### Scroll Lock cycling
+Scroll Lock toggling will work just fine for most users. However, some users using Remote Desktop alongside have reported spurious keystrokes being transmitted between the connected Windows devices. Users in such situations should explore other key simulation options.
+
+#### Volume Up & Down
+With this option, StayAwake will simulate a _Volume Down_ key press, immediately followed by a _Volume Up_ key press, thereby resulting in no net change in volume level.
+
+A minor benefit with this option is the visual feedback. Each StayAwake key simulation event will cause the Windows Volume Slider bar to display for a few seconds above the Windows Taskbar.
+
+However, if a user has muted the audio, then this option will unmute it. Users wishing to avoid this side effect should explore other key simulation options.
+
+#### Unassigned Key (1 to 10)
+With these options, StayWake will simulate key press of an unassigned keycode.
+
+These unassigned keycodes have been obtained from this Microsoft article on [Virtual-Key Codes](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
+
+Microsoft may start utilizing any of these unassigned codes in the future -- but not all of them in one go. Hence all 10 of the currently unassigned codes have been included in the [Select multiple Key Codes](#select-multiple-key-codes) popup so that users can select other still available unassigned keycodes.
+
+#### Optional Tip
+ You can verify StayAwake in action by *temporarily* enabling only the [Volume Up & Down](#volume-up--down) key code and setting both *Minimum Awake Seconds* and *Maximum Awake Seconds* fields to 10 seconds.
diff --git a/README.md b/README.md
index 0dabb5b..c0e7410 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ StayAwake executable is now digitally signed by the kind folks at [OSSign](https

#### Select multiple Key Codes for random simulation
-StayAwake can randomly pick any of the 12 key codes during each simulation. Users can specify _at least_ one OR a select few OR all of the 12 key codes to be included in the simulation roster. See the [Select multiple Key Codes](#select-multiple-key-codes) section below for more info.
+StayAwake can randomly pick any of the 12 key codes during each simulation. Users can specify _at least_ one OR a select few OR all of the 12 key codes to be included in the simulation roster. See the [Select multiple Key Codes](https://github.com/shriprem/StayAwake/blob/Version2.0/InputOptions.md) section below for more info.
#### Minimum Awake Seconds
This field allows you to specify the minimum interval between key simulations. The initial default value for this is 240 seconds (_i.e._, 4 minutes). However, you can change this to any value between 10 and 9990 seconds. Your new value will be saved and used the next time you run StayAwake.
@@ -67,33 +67,6 @@ Check the _Launch application in minimized state_ box to have StayAwake automati
When the _Start Minimized_ option is enabled, StayAwake will launch and minimize immediately only on the first launch attempt. If StayAwake is still running in a minimized state, trying to launch the application again will restore the StayAwake dialog from the minimized state. This is by design when `MultipleInstancesAllowed=N`. See under: [Configuration File Details](#configuration-file-details)
-### Select multiple Key Codes
-
-
-
-StayAwake can randomly pick any of the 12 key codes during each simulation. In this popup, users can specify _at least_ one OR a select few OR all of the 12 key codes to be included in the simulation roster.
-
-#### Scroll Lock cycling
-Scroll Lock toggling will work just fine for most users. However, some users using Remote Desktop alongside have reported spurious keystrokes being transmitted between the connected Windows devices. Users in such situations should explore other key simulation options.
-
-#### Volume Up & Down
-With this option, StayAwake will simulate a _Volume Down_ key press, immediately followed by a _Volume Up_ key press, thereby resulting in no net change in volume level.
-
-A minor benefit with this option is the visual feedback. Each StayAwake key simulation event will cause the Windows Volume Slider bar to display for a few seconds above the Windows Taskbar.
-
-However, if a user has muted the audio, then this option will unmute it. Users wishing to avoid this side effect should explore other key simulation options.
-
-#### Unassigned Key (1 to 10)
-With these options, StayWake will simulate key press of an unassigned keycode.
-
-These unassigned keycodes have been obtained from this Microsoft article on [Virtual-Key Codes](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
-
-Microsoft may start utilizing any of these unassigned codes in the future -- but not all of them in one go. Hence all 10 of the currently unassigned codes have been included in the [Select multiple Key Codes](#select-multiple-key-codes) popup so that users can select other still available unassigned keycodes.
-
-#### Optional Tip
- You can verify StayAwake in action by *temporarily* enabling only the [Volume Up & Down](#volume-up--down) key code and setting both *Minimum Awake Seconds* and *Maximum Awake Seconds* fields to 10 seconds.
-
-
### System Tray Icon

@@ -107,7 +80,7 @@ When you minimize StayAwake, it will be represented by the System Tray icon show
## Configuration File Details
StayAwake saves its configuration in a file named `StayAwake.ini`. This file is co-located in the `StayAwake.exe` application file folder. These key-value pairs are stored in this file:
-1. `SelectedKeyCodes`: This key stores a string of 12 characters -- each character indicating if a key code in the [Select multiple Key Codes](#select-multiple-key-codes) popup is selected or not with either a `1` or `0`, respectively.
+1. `SelectedKeyCodes`: This key stores a string of 12 characters -- each character indicating if a key code in the [Select multiple Key Codes](https://github.com/shriprem/StayAwake/blob/Version2.0/InputOptions.md) popup is selected or not with either a `1` or `0`, respectively.
2. `MinimumIntervalInSeconds`: This key stores the minimum number of seconds between each StayAwake Key simulation as specified in the [Minimum Awake Seconds](#minimum-awake-seconds) field.
From bf7275386946796affac782ff99f118c15894ab2 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 4 Oct 2026 23:40:29 -0400
Subject: [PATCH 20/22] Add link to InputOptions.md in SelectKeyCodes dialog
---
StayAwake/SelectKeyCodesDlg.cpp | 9 +++++++++
StayAwake/SelectKeyCodesDlg.h | 1 +
StayAwake/StayAwake.rc | 8 +++++---
StayAwake/StayAwake.vcxproj | 2 ++
StayAwake/StayAwake.vcxproj.filters | 6 ++++++
StayAwake/res/information.bmp | Bin 0 -> 1078 bytes
StayAwake/resource.h | 3 +++
7 files changed, 26 insertions(+), 3 deletions(-)
create mode 100644 StayAwake/res/information.bmp
diff --git a/StayAwake/SelectKeyCodesDlg.cpp b/StayAwake/SelectKeyCodesDlg.cpp
index ac1efce..59fdd89 100644
--- a/StayAwake/SelectKeyCodesDlg.cpp
+++ b/StayAwake/SelectKeyCodesDlg.cpp
@@ -32,6 +32,7 @@ BEGIN_MESSAGE_MAP(CSelectKeyCodesDlg, CDialogEx)
ON_BN_CLICKED(IDC_KEY_SELECT_NONE_BTN, &CSelectKeyCodesDlg::OnClickedKeySelectNoneBtn)
ON_BN_CLICKED(IDC_KEY_SELECT_ALL_UNASSGND_BTN, &CSelectKeyCodesDlg::OnClickedSelectAllUnassignedKeys)
ON_BN_CLICKED(IDC_KEY_SELECT_ALL_EXT_FN_BTN, &CSelectKeyCodesDlg::OnClickedSelectAllExtFnKeys)
+ ON_BN_CLICKED(IDC_INPUT_OPTIONS_INFO_BTN, &CSelectKeyCodesDlg::OnClickedInputOptionsInfoBtn)
END_MESSAGE_MAP()
@@ -42,6 +43,9 @@ BOOL CSelectKeyCodesDlg::OnInitDialog()
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
+ Utils::addTooltip(theApp.m_hInstance, m_hWnd, IDC_INPUT_OPTIONS_INFO_BTN, L"", L"View Readme Online", 3, TRUE);
+ Utils::loadBitmap(theApp.m_hInstance, m_hWnd, IDC_INPUT_OPTIONS_INFO_BTN, IDB_INFO_BITMAP);
+
CheckAllBoxes(m_AwakeCore.GetSelectedKeyCodes());
return TRUE; // return TRUE unless you set the focus to a control
@@ -95,3 +99,8 @@ void CSelectKeyCodesDlg::OnClickedSelectAllExtFnKeys()
CheckAllBoxes(wstring(LEN_ROSTER_KEYCODES, L'1'), IDC_KEY_F13, IDC_MOUSE_MOVE);
}
+
+void CSelectKeyCodesDlg::OnClickedInputOptionsInfoBtn()
+{
+ ShellExecute(nullptr, L"open", L"https://github.com/shriprem/StayAwake/blob/Version2.0/InputOptions.md", nullptr, nullptr, SW_SHOW);
+}
diff --git a/StayAwake/SelectKeyCodesDlg.h b/StayAwake/SelectKeyCodesDlg.h
index 65442e5..896c125 100644
--- a/StayAwake/SelectKeyCodesDlg.h
+++ b/StayAwake/SelectKeyCodesDlg.h
@@ -29,6 +29,7 @@ class CSelectKeyCodesDlg : public CDialogEx
afx_msg void OnClickedKeySelectNoneBtn();
afx_msg void OnClickedSelectAllUnassignedKeys();
afx_msg void OnClickedSelectAllExtFnKeys();
+ afx_msg void OnClickedInputOptionsInfoBtn();
afx_msg void OnOK();
DECLARE_MESSAGE_MAP()
diff --git a/StayAwake/StayAwake.rc b/StayAwake/StayAwake.rc
index 115ba37..b5472a5 100644
--- a/StayAwake/StayAwake.rc
+++ b/StayAwake/StayAwake.rc
@@ -29,12 +29,12 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// TEXTINCLUDE
//
-1 TEXTINCLUDE
+1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
-2 TEXTINCLUDE
+2 TEXTINCLUDE
BEGIN
"#ifndef APSTUDIO_INVOKED\r\n"
"#include ""targetver.h""\r\n"
@@ -44,7 +44,7 @@ BEGIN
"\0"
END
-3 TEXTINCLUDE
+3 TEXTINCLUDE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
@@ -145,6 +145,7 @@ BEGIN
PUSHBUTTON "Select &All",IDC_KEY_SELECT_ALL_BTN,225,130,85,14
DEFPUSHBUTTON "A&pply",IDOK,225,155,85,14
PUSHBUTTON "&Cancel",IDCANCEL,225,180,85,14
+ PUSHBUTTON "...",IDC_INPUT_OPTIONS_INFO_BTN,312,2,15,13,BS_BITMAP
END
IDD_UPDATE_CHECK_DIALOG DIALOGEX 0, 0, 170, 115
@@ -286,6 +287,7 @@ IDR_MAINFRAME ICON "res\\StayAwake.ico"
//
IDB_ABOUT_BITMAP BITMAP "res\\about.bmp"
+IDB_INFO_BITMAP BITMAP "res\\information.bmp"
/////////////////////////////////////////////////////////////////////////////
diff --git a/StayAwake/StayAwake.vcxproj b/StayAwake/StayAwake.vcxproj
index cc452eb..9598b0a 100644
--- a/StayAwake/StayAwake.vcxproj
+++ b/StayAwake/StayAwake.vcxproj
@@ -216,6 +216,7 @@
+
@@ -250,6 +251,7 @@
+
diff --git a/StayAwake/StayAwake.vcxproj.filters b/StayAwake/StayAwake.vcxproj.filters
index d7fb28f..d0f8204 100644
--- a/StayAwake/StayAwake.vcxproj.filters
+++ b/StayAwake/StayAwake.vcxproj.filters
@@ -48,6 +48,9 @@
Header Files
+
+ Header Files
+
@@ -95,5 +98,8 @@
Resource Files
+
+ Resource Files
+
\ No newline at end of file
diff --git a/StayAwake/res/information.bmp b/StayAwake/res/information.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..0fe3f416a252b650c9950c340cd9d0025ebc440f
GIT binary patch
literal 1078
zcmbu3T}YE*6vyYgDUk|YWKDxu%rDkt8gmTGkJg84kw|_uyU?wD1%ZRLBxDk4f#0-hl}!FI_o8iO9mDHyT2)oV8$NKaEI+@*DGR9H(HuM
zy=YYNiM~ci^fka-cMSzIve2vKN2TkhaW~t&zJ)6u*|yFZGN}0@|@_f8#lLdHg|JbICxAAf!$lbj`cvquz^sdhQ|{eF4_GwWl0+bT<d*qxLPW~
z=kw7Un6bz3bSTp3F#m2D=L!$%Q(ntnm@zhiyS)a+4wQq}>!mlIYRkZ(EB_0br_f>k
z{R(b0JkYr^cf1)G?@bzbq%DUB`=szhR{;G5NZFqX&1hU6!2+q1LS~d=|9r
z4#h586a2MPDgLTG6&toB0+z-Q&TxnAuT@JojAy`X!r5BR|MdO
Date: Mon, 5 Oct 2026 16:11:55 -0400
Subject: [PATCH 21/22] Start Auto Update Check: Show dialog only if newer
version found
---
StayAwake/StayAwakeAboutDlg.cpp | 2 +-
StayAwake/StayAwakeDlg.cpp | 7 ++++++-
StayAwake/UpdateCheckDlg.cpp | 27 ++++++++++++++++++---------
StayAwake/UpdateCheckDlg.h | 26 ++++++++++++++------------
StayAwake/Utils.cpp | 4 ++++
StayAwake/Utils.h | 1 +
6 files changed, 44 insertions(+), 23 deletions(-)
diff --git a/StayAwake/StayAwakeAboutDlg.cpp b/StayAwake/StayAwakeAboutDlg.cpp
index c326c4e..ae910b8 100644
--- a/StayAwake/StayAwakeAboutDlg.cpp
+++ b/StayAwake/StayAwakeAboutDlg.cpp
@@ -36,7 +36,7 @@ BOOL CAboutDlg::OnInitDialog()
#endif // _WIN64
SetDlgItemText(IDC_ABOUT_NAME, Utils::getVersionInfo(theApp.m_hInstance, L"FileDescription").c_str());
- SetDlgItemText(IDC_ABOUT_VERSION, (L"Version: " + Utils::getVersionInfo(theApp.m_hInstance, L"FileVersion") + buildBit).c_str());
+ SetDlgItemText(IDC_ABOUT_VERSION, (L"Version: " + Utils::getAppVersion(theApp.m_hInstance) + buildBit).c_str());
SetDlgItemText(IDC_ABOUT_BUILD_TIME, (wstring{ L"Last code update: " } + LAST_CODE_UPDATE_TIME).c_str());
SetDlgItemText(IDC_ABOUT_ATTRIBUTION, Utils::getVersionInfo(theApp.m_hInstance, L"LegalCopyright").c_str());
diff --git a/StayAwake/StayAwakeDlg.cpp b/StayAwake/StayAwakeDlg.cpp
index 4cf6354..cc74899 100644
--- a/StayAwake/StayAwakeDlg.cpp
+++ b/StayAwake/StayAwakeDlg.cpp
@@ -136,7 +136,12 @@ afx_msg LRESULT CStayAwakeDlg::OnPostOpen(WPARAM wParam, LPARAM lParam)
InitAwakes();
if (m_AwakeCore.GetPreference(PREF_START_UPDATE_CHECK, L"N") == L"Y")
- OnUpdateCheckClicked();
+ {
+ wstring currentVersion{}, latestVersion{};
+ bool bNewer = CUpdateCheckDlg::IsNewerVersionAvailable(currentVersion, latestVersion);
+
+ if (bNewer) OnUpdateCheckClicked();
+ }
BOOL bMinimized = (m_AwakeCore.GetPreference(PREF_START_MINIMIZED, L"N") == L"Y");
CheckDlgButton(IDC_START_MINIMIZED_CBX, bMinimized ? BST_CHECKED : BST_UNCHECKED);
diff --git a/StayAwake/UpdateCheckDlg.cpp b/StayAwake/UpdateCheckDlg.cpp
index 819844a..06bf3bc 100644
--- a/StayAwake/UpdateCheckDlg.cpp
+++ b/StayAwake/UpdateCheckDlg.cpp
@@ -22,6 +22,19 @@ CUpdateCheckDlg::~CUpdateCheckDlg()
{
}
+bool CUpdateCheckDlg::IsNewerVersionAvailable(wstring& currentVersion, wstring& latestVersion)
+{
+ currentVersion = Utils::getAppVersion(theApp.m_hInstance);
+
+ VersionUpdate versionUpdate;
+ latestVersion = versionUpdate.GetVersion();
+
+ if (latestVersion.empty() || currentVersion.empty())
+ return false;
+
+ return (latestVersion != currentVersion);
+}
+
void CUpdateCheckDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
@@ -41,17 +54,13 @@ BOOL CUpdateCheckDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
- wstring currentVersionInfo{ Utils::getVersionInfo(theApp.m_hInstance, L"FileVersion") };
-
- VersionUpdate versionUpdate;
- wstring latestVersionInfo{ versionUpdate.GetVersion() };
+ wstring currentVersion{}, latestVersion{};
+ bool bNewer = IsNewerVersionAvailable(currentVersion, latestVersion);
- SetDlgItemText(IDC_INSTALLED_VERSION_INFO, currentVersionInfo.c_str());
- SetDlgItemText(IDC_LATEST_VERSION_INFO, latestVersionInfo.c_str());
+ SetDlgItemText(IDC_INSTALLED_VERSION_INFO, currentVersion.c_str());
+ SetDlgItemText(IDC_LATEST_VERSION_INFO, latestVersion.c_str());
SetDlgItemText(IDC_VERSION_CHECK_STATUS,
- (latestVersionInfo == currentVersionInfo) ?
- L"This is the latest available version." :
- L"A newer version is available. Click the link below.");
+ bNewer ? L"A newer version is available. Click the link below." : L"This is the latest available version.");
BOOL bStartUpdateCheck = (m_AwakeCore.GetPreference(PREF_START_UPDATE_CHECK, L"N") == L"Y");
CheckDlgButton(IDC_START_VERSION_CHECK_CBX, bStartUpdateCheck ? BST_CHECKED : BST_UNCHECKED);
diff --git a/StayAwake/UpdateCheckDlg.h b/StayAwake/UpdateCheckDlg.h
index 428d7bc..5a23049 100644
--- a/StayAwake/UpdateCheckDlg.h
+++ b/StayAwake/UpdateCheckDlg.h
@@ -8,27 +8,29 @@
class CUpdateCheckDlg : public CDialogEx
{
- DECLARE_DYNAMIC(CUpdateCheckDlg)
+ DECLARE_DYNAMIC(CUpdateCheckDlg)
public:
- explicit CUpdateCheckDlg(CWnd* pParent, StayAwakeCore& pAwakeCore);
- virtual ~CUpdateCheckDlg();
+ explicit CUpdateCheckDlg(CWnd* pParent, StayAwakeCore& pAwakeCore);
+ virtual ~CUpdateCheckDlg();
+
+ static bool IsNewerVersionAvailable(wstring& currentVersion, wstring& latestVersion);
// Dialog Data
#ifdef AFX_DESIGN_TIME
- enum { IDD = IDD_UPDATE_CHECK_DIALOG };
+ enum { IDD = IDD_UPDATE_CHECK_DIALOG };
#endif
protected:
- StayAwakeCore m_AwakeCore{};
- HICON m_hIcon;
+ StayAwakeCore m_AwakeCore{};
+ HICON m_hIcon;
- virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
- virtual BOOL OnInitDialog();
- afx_msg void OnClickLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult);
- afx_msg void OnClickedStartVersionCheck();
- afx_msg void OnReturnLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult);
+ virtual BOOL OnInitDialog();
+ afx_msg void OnClickLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnClickedStartVersionCheck();
+ afx_msg void OnReturnLatestReleaseUrl(NMHDR* pNMHDR, LRESULT* pResult);
- DECLARE_MESSAGE_MAP()
+ DECLARE_MESSAGE_MAP()
};
diff --git a/StayAwake/Utils.cpp b/StayAwake/Utils.cpp
index a48ef2f..05f8ffa 100644
--- a/StayAwake/Utils.cpp
+++ b/StayAwake/Utils.cpp
@@ -145,3 +145,7 @@ wstring Utils::getVersionInfo(HINSTANCE hApp, LPCWSTR key) {
return sVersionInfo;
}
+
+wstring Utils::getAppVersion(HINSTANCE hApp) {
+ return getVersionInfo(hApp, L"FileVersion");
+}
diff --git a/StayAwake/Utils.h b/StayAwake/Utils.h
index e09f425..e4dff46 100644
--- a/StayAwake/Utils.h
+++ b/StayAwake/Utils.h
@@ -23,4 +23,5 @@ namespace Utils {
int getProcessRunCount(wstring sBaseName);
wstring getVersionInfo(HINSTANCE hApp, LPCWSTR key);
+ wstring getAppVersion(HINSTANCE hApp);
}
From 53e01369dad3e6fd6d38a15876b00a1104978722 Mon Sep 17 00:00:00 2001
From: Shridhar Kumar <45252729+shriprem@users.noreply.github.com>
Date: Sun, 11 Oct 2026 23:12:54 -0400
Subject: [PATCH 22/22] Updating images and documentation for v1.5.0.0 Release
---
InputOptions.md | 44 +++++++++++++++++++----
README.md | 57 ++++++++++++++++++++++++------
VersionHistory.md | 18 ++++++++++
images/CheckForUpdates.png | Bin 0 -> 4352 bytes
images/StayAwakeApp.png | Bin 7491 -> 7720 bytes
images/StayAwakeInputOptions.png | Bin 0 -> 16224 bytes
images/StayAwakeKeySelections.png | Bin 6199 -> 0 bytes
images/StayAwakePaused.png | Bin 7548 -> 7763 bytes
images/StayAwakePausedLaunch.png | Bin 6942 -> 7170 bytes
9 files changed, 102 insertions(+), 17 deletions(-)
create mode 100644 images/CheckForUpdates.png
create mode 100644 images/StayAwakeInputOptions.png
delete mode 100644 images/StayAwakeKeySelections.png
diff --git a/InputOptions.md b/InputOptions.md
index 064c434..a5642ab 100644
--- a/InputOptions.md
+++ b/InputOptions.md
@@ -1,8 +1,12 @@
## Input Simulation Options
-
+
-StayAwake can randomly pick any of the 12 key codes during each simulation. In this popup, users can specify _at least_ one OR a select few OR all of the 12 key codes to be included in the simulation roster.
+StayAwake can randomly pick any of the 26 inputs (24 key codes & 2 mouse moves) during each simulation. In this popup, users can specify _at least_ one OR a select few OR all of the 26 inputs to be included in the simulation roster.
+
+StayAwake simulates events using the `SendInput` API instead of the deprecated `keybd_event` API. `SendInput` API allows batching of multiple key or mouse events in a single atomic call to the Windows OS, thus yielding jitter-free key or mouse simulations.
+
+As can be noted from the screenshot, [Zero-pixel Mouse Move](#zero-pixel-mouse-move) and [One-pixel Mouse Jiggle](#one-pixel-mouse-jiggle) are my two preferred input options.
#### Scroll Lock cycling
Scroll Lock toggling will work just fine for most users. However, some users using Remote Desktop alongside have reported spurious keystrokes being transmitted between the connected Windows devices. Users in such situations should explore other key simulation options.
@@ -10,16 +14,42 @@ Scroll Lock toggling will work just fine for most users. However, some users usi
#### Volume Up & Down
With this option, StayAwake will simulate a _Volume Down_ key press, immediately followed by a _Volume Up_ key press, thereby resulting in no net change in volume level.
-A minor benefit with this option is the visual feedback. Each StayAwake key simulation event will cause the Windows Volume Slider bar to display for a few seconds above the Windows Taskbar.
+Volume Up & Down simulation will preserve the muted sound state. Prior to each Volume Up & Down simulation event, StayAwake will detect if the sound is in a muted state or not. If the sound is found muted, Volume Mute key will also be added after the Volume Down & Up keys in the `SendInput` API call. Since all these key events are sent in a single atomic API call, any sound jitter that may still occur will be the briefest possible.
+
+A minor benefit with this option is the visual feedback. Each StayAwake key simulation event will cause the Windows Volume Slider flyout/toast OSD (on-screen display) to show for a few seconds. While this input option is useful for debugging or verification of StayAwake functionality, it will get too distracting over long term.
-However, if a user has muted the audio, then this option will unmute it. Users wishing to avoid this side effect should explore other key simulation options.
+**Tip**: You can verify StayAwake in action by *temporarily* enabling only the *Volume Up & Down* key code and setting both *Minimum Awake Seconds* and *Maximum Awake Seconds* fields to 10.
#### Unassigned Key (1 to 10)
With these options, StayWake will simulate key press of an unassigned keycode.
These unassigned keycodes have been obtained from this Microsoft article on [Virtual-Key Codes](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
-Microsoft may start utilizing any of these unassigned codes in the future -- but not all of them in one go. Hence all 10 of the currently unassigned codes have been included in the [Select multiple Key Codes](#select-multiple-key-codes) popup so that users can select other still available unassigned keycodes.
+Microsoft may start utilizing any of these unassigned codes in the future -- but not all of them in one go. Hence all 10 of the currently unassigned codes have been included in the [Input Simulation Options](#input-simulation-options) popup so that users can select other still available unassigned keycodes.
+
+See also: [Google Sheets clears active cell content on unassigned Windows virtual-key events](https://github.com/shriprem/StayAwake/discussions/21)
+
+#### Extended Function Keys (F13 to F24)
+These 12 options are safe to use since most applications do not bind F13–F24 keys to any action. However, there are some rare exceptions such as:
+* Some DAWs (digital audio workstations)
+* Some macro software
+* Some enterprise kiosk systems
+* Some accessibility tools
+
+See also: [Google Sheets clears active cell content on unassigned Windows virtual-key events](https://github.com/shriprem/StayAwake/discussions/21)
+
+#### One-pixel Mouse Jiggle
+With this option, StayAwake will simulate a one-pixel mouse jiggle. The mouse pointer will move one pixel diagonally right & down, and then back to its original position. This one-pixel jiggle is jitter-free since the two mouse moves are batched into a single atomic `SendInput` API call. So, the mouse pointer will not seem to move at all with this option.
+
+#### Zero-pixel Mouse Move
+With this option, StayAwake will simulate a zero-pixel mouse move. The mouse pointer will not move at all. At least on the few Windows 11 devices that I have tested, this option was found to be remarkably effective. If this option does not work on your Windows device, use the [One-pixel Mouse Jiggle](#one-pixel-mouse-jiggle) option instead.
+
+
+#### Buttons
+* _Select None_ button will deselect all 26 input options.
+* _Select All Unassigned_ button will select all 10 Unassigned Key options.
+* _Select All Ext. FN Keys_ button will select all 12 Extended Function Key options.
+* _Select All_ button will select all 26 input options.
+* _Apply_ will save the selected input options and close the popup.
+* _Cancel_ will close the popup without saving any changes.
-#### Optional Tip
- You can verify StayAwake in action by *temporarily* enabling only the [Volume Up & Down](#volume-up--down) key code and setting both *Minimum Awake Seconds* and *Maximum Awake Seconds* fields to 10 seconds.
diff --git a/README.md b/README.md
index c0e7410..69323e5 100644
--- a/README.md
+++ b/README.md
@@ -10,24 +10,37 @@
+ 
-[Current Version: 1.3.0.0](https://github.com/shriprem/StayAwake/blob/main/VersionHistory.md)
+[Current Version: 1.5.0.0](VersionHistory.md)
StayAwake is a simple utility that enables you to maintain an _Active_ status on Microsoft Teams (and perhaps other messaging applications such as Slack and Zoom). StayAwake also prevents screen saver activation, screen blanking, and Windows session lockouts.
+## Features at a glance
+* Randomized simulation with mouse or key inputs. Roster with a choice of 24 no/low side-effect keys and 2 invisible mouse moves.
+* Random interval simulation. Intervals within a specified range of seconds.
+* Helpful status information with timestamps of Last and Next simulations.
+* Minimization to System Tray.
+* Start Minimized option.
+* Manual Pause feature when needed.
+* Automatic Pause while Windows in locked state. Reduces disk activity and CPU usage. Conserves battery power.
+* Small, portable application. Runs with no installation or administrator privileges.
+* Digitally signed application executable file.
+* Update Check feature - Manual or automatic (opt-in).
+
If you are using Microsoft Teams, you may have noticed that Teams automatically sets your status to _Away_ after a certain period of inactivity. This can be frustrating if you are still at your desk but perhaps on a business phone call _OR_ outlining a plan on paper _OR_ consulting some reference material.
-StayAwake can be minimized to the Windows System Tray, and will continue to run in the background until you click the _Exit_ button. StayAwake also has the option to automatically minimize to Windows System Tray upon launch. See: [Start Minimized](#start-minimized)
+StayAwake simulates a randomly selected input from a specified roster of jitter-free mouse moves and no/low side-effect keys. Additionally, these simulations are done at random intervals within a specified range of seconds. This is a safe and non-intrusive way to keep your Microsoft Teams status _Active_. The combined randomness of inputs and time intervals eliminates predictable patterns, making StayAwake simulations seem organic.
-StayAwake does its job by simulating key presses of unused or low side-effect buttons at specified time intervals. This is a safe and non-intrusive way to keep your Microsoft Teams status _Active_.
+StayAwake can be minimized to the Windows System Tray, and will continue to run in the background until you click the _Exit_ button. StayAwake also has the option to automatically minimize to Windows System Tray upon launch. See: [Start Minimized](#start-minimized)
StayAwake executable is now digitally signed by the kind folks at [OSSign](https://www.ossign.org). Thanks to the code signing, there is *NO* need for users to override the Windows Defender Smartscreen or [unblock the executable file](https://stackoverflow.com/questions/48946680/how-to-avoid-the-windows-defender-smartscreen-prevented-an-unrecognized-app-fro) to launch StayAwake.
## User Interface

-#### Select multiple Key Codes for random simulation
-StayAwake can randomly pick any of the 12 key codes during each simulation. Users can specify _at least_ one OR a select few OR all of the 12 key codes to be included in the simulation roster. See the [Select multiple Key Codes](https://github.com/shriprem/StayAwake/blob/Version2.0/InputOptions.md) section below for more info.
+#### Select multiple options for random simulation
+StayAwake can randomly pick any of the 26 inputs (24 key codes & 2 mouse moves) during each simulation. Users can specify _at least_ one OR a select few OR all of the 26 inputs to be included in the simulation roster. See [Input Simulation Options](InputOptions.md) for more info.
#### Minimum Awake Seconds
This field allows you to specify the minimum interval between key simulations. The initial default value for this is 240 seconds (_i.e._, 4 minutes). However, you can change this to any value between 10 and 9990 seconds. Your new value will be saved and used the next time you run StayAwake.
@@ -35,12 +48,14 @@ This field allows you to specify the minimum interval between key simulations. T
#### Maximum Awake Seconds
This field allows you to specify the maximum interval between key simulations. The initial default value for this is 240 seconds (_i.e._, 4 minutes). However, you can change this to any value between 10 and 9990 seconds. Your new value will be saved and used the next time you run StayAwake.
+**Tip**: If Microsoft Teams is changing your status to _Away_ after 5 minutes (i.e., 300 seconds) of inactivity, consider setting the *Minimum Awake Seconds* to around 200 and the *Maximum Awake Seconds* to 310 or 320 or even 330 (i.e., a little over 300). This weighted range will cause your Teams status to briefly drift out of _Active_ status once in a while -- a more normal user behavior from your coworkers' perspective than a solid green status through an entire work day.
+
#### Set Timer button
After changing the value for either *Minimum Awake Seconds* or *Maximum Awake Seconds*, click this button to apply the new setting. Clicking this button will also immediately invoke an Awake event.
After every Awake event, a random number of seconds (between the specified minimum and maximum seconds) will be picked to run the next Awake event.
-The clock times of the last and next Awake events will be displayed and continually refreshed on the StayAwake window. The randomly picked key code used for the last Awake will also be displayed below the last Awake time.
+The clock times of the last and next Awake events will be displayed and continually refreshed on the StayAwake window. The randomly picked input option used for the last Awake will also be displayed below the last Awake time.
#### Pause button
Click this button to pause StayAwake Key simulations.
@@ -55,6 +70,8 @@ As seen in the clip above, clicking the *Pause* button will also have these effe

+4. On a related note, if the Windows system has been manually locked, StayAwake will pause key and mouse simulations until the system gets unlocked again. This will help reduce disk activity and CPU usage (thus conserving battery power on laptops) while the system is in locked state.
+
#### Minimize button
Click this button to minimize StayAwake to the Windows System Tray. See the [SystemTray Icon](#system-tray-icon) section below for more info.
@@ -62,10 +79,22 @@ Click this button to minimize StayAwake to the Windows System Tray. See the [Sys
Click this button to exit StayAwake.
#### Start Minimized
-Check the _Launch application in minimized state_ box to have StayAwake automatically minimize to System Tray upon launch.
+Check the _Start minimized_ box to have StayAwake automatically minimize to System Tray upon launch.
When the _Start Minimized_ option is enabled, StayAwake will launch and minimize immediately only on the first launch attempt. If StayAwake is still running in a minimized state, trying to launch the application again will restore the StayAwake dialog from the minimized state. This is by design when `MultipleInstancesAllowed=N`. See under: [Configuration File Details](#configuration-file-details)
+#### Update Check button
+Click this button to manually check if a newer version of StayAwake is available. When clicked, the _Check for Updates_ dialog will popup.
+
+
+
+
+The _Check for Updates_ popup will display the Installed and Latest Version numbers.
+
+If a newer version is available, the popup will display a clickable link that redirects to the [StayAwake - Latest Release](https://github.com/shriprem/StayAwake/releases/latest) page.
+
+In this popup, you can also enable the _Check for updates when StayAwake starts_ option to have StayAwake automatically check for a newer version every time it is launched. This is an opt-in feature that is unchecked by default.
+
### System Tray Icon

@@ -80,7 +109,9 @@ When you minimize StayAwake, it will be represented by the System Tray icon show
## Configuration File Details
StayAwake saves its configuration in a file named `StayAwake.ini`. This file is co-located in the `StayAwake.exe` application file folder. These key-value pairs are stored in this file:
-1. `SelectedKeyCodes`: This key stores a string of 12 characters -- each character indicating if a key code in the [Select multiple Key Codes](https://github.com/shriprem/StayAwake/blob/Version2.0/InputOptions.md) popup is selected or not with either a `1` or `0`, respectively.
+1. `SelectedKeyCodes`: This key stores a string of 26 characters -- each character indicating if a key code or mouse move in the [Input Simulation Options](InputOptions.md) popup is enabled or not with either a `1` or `0`, respectively.
+
+ If an invalid value is detected for this key, the default value of `00111111111111111111111111` (i.e., enabling of all inputs except *Scroll Lock cycle* and *Volume Down & Up*) will be used instead.
2. `MinimumIntervalInSeconds`: This key stores the minimum number of seconds between each StayAwake Key simulation as specified in the [Minimum Awake Seconds](#minimum-awake-seconds) field.
@@ -92,9 +123,11 @@ StayAwake saves its configuration in a file named `StayAwake.ini`. This file is
4. `AwakePaused`: This key stores a flag value (_Y_ or _N_) to indicate if StayAwake is in paused state.
-5. `StartMinimized`: This key stores a flag value (_Y_ or _N_) to indicate if StayAwake will automatically minimize to System Tray upon launch. See: [Start Minimized](#start-minimized)
+5. `StartMinimized`: This key stores a flag value (_Y_ or _N_) to indicate if StayAwake will automatically minimize to System Tray upon launch. See: [Start Minimized](#start-minimized)
-6. `MultipleInstancesAllowed`: This key stores a flag value (_Y_ or _N_) to indicate whether multiple instances of StayAwake are allowed to run simultaneously. The default value is _N_ (i.e., multiple instances are not allowed). Any value that is not _Y_ will be interpreted as _N_.
+6. `StartCheckUpdates`: This key stores a flag value (_Y_ or _N_) to indicate if StayAwake will automatically check for a newer version every time it is launched. See: [Update Check button](#update-check-button)
+
+7. `MultipleInstancesAllowed`: This key stores a flag value (_Y_ or _N_) to indicate whether multiple instances of StayAwake are allowed to run simultaneously. The default value is _N_ (i.e., multiple instances are not allowed). Any value that is not _Y_ will be interpreted as _N_.
## Alternatives
@@ -116,5 +149,9 @@ However, if you are looking for a way to keep your Microsoft Teams status _Activ
StayAwake is a portable application and does NOT require installation with administrator privileges. Simply download the latest release from the [Releases](https://github.com/shriprem/StayAwake/releases) page and unzip the contents to a folder of your choice. Run the `StayAwake.exe` application file to start the utility.
+If you like StayAwake, please star this repo. Thank you for your appreciation.
+
+If you would like to support its development, you can buy me a coffee by clicking the button below. Thank you for your support!
+
diff --git a/VersionHistory.md b/VersionHistory.md
index 760863d..e23ae07 100644
--- a/VersionHistory.md
+++ b/VersionHistory.md
@@ -1,5 +1,23 @@
## Version History
+### Version 1.5.0.0 [October 12, 2026]
+
+1. Two mouse simulation options (Zero-pixel Mouse Move and One-pixel Mouse Jiggle) have been added to the [Input Simulation Options](InputOptions.md) roster.
+
+2. 12 new simulation options for Extended Function Keys: F13 to F24 have been added to the [Input Simulation Options](InputOptions.md) roster.
+
+3. StayAwake will now simulate events using the `SendInput` API instead of the deprecated `keybd_event` API. `SendInput` API allows batching of multiple key or mouse events in a single atomic call to the Windows OS, thus yielding jitter-free key or mouse simulations.
+
+4. Volume Up & Down simulation will now preserve the muted sound state. Prior to each Volume Up & Down simulation event, StayAwake will detect if the sound is in a muted state or not. If the sound is found muted, Volume Mute key will also be added after the Volume Down & Up keys in the `SendInput` API call.
+
+5. If the Windows system has been manually locked, StayAwake will now suppress key and mouse simulations until the system gets unlocked again. This will help reduce disk activity and CPU usage (thus conserving battery power on laptops) while the system is in locked state.
+
+6. Update Check button has been added so that users can manually check if a newer version of StayAwake is available. Additionally, an opt-in feature is also being offered to automatically check for a newer version every time StayAwake is started.
+
+**Release:** [1.5.0.0 Release](https://github.com/shriprem/StayAwake/releases/tag/v1.5.0.0)
+
+---
+
### Version 1.3.0.0 [September 21, 2026]
1. StayAwake will use a random pick from a roster of user-specified key codes for each Awake event. See enhancement request: [#15](https://github.com/shriprem/StayAwake/issues/15).
diff --git a/images/CheckForUpdates.png b/images/CheckForUpdates.png
new file mode 100644
index 0000000000000000000000000000000000000000..55c17793982aa166437a8259061a57e958e79f1f
GIT binary patch
literal 4352
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
literal 0
HcmV?d00001
diff --git a/images/StayAwakeApp.png b/images/StayAwakeApp.png
index 854f4850927374ad09427c71394570877646ddfe..4665694a1d5016985fbaa1b3fedba351104de0c4 100644
GIT binary patch
literal 7720
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
Zx16MhXx*-TL0|o1D8kgBr81_0{|gFDj6(nb
literal 7491
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