88#include < string>
99#include < unordered_map>
1010#include < regex>
11- #include < cstdlib>
12- #include < ctime>
11+ #include < random>
12+ #include < chrono>
13+ #include < thread>
1314
1415using namespace std ;
1516
@@ -49,16 +50,24 @@ bool isLocked = false; // Variable to track the lock state
4950bool vacBypassAEnabled = false ; // VAC bypass A toggle
5051bool vacBypassBEnabled = false ; // VAC bypass B toggle
5152int vacCounter = 0 ; // counter for imperfect snaptap
53+ int vacAMinDelay = 15 ; // A mode minimum overlap delay
54+ int vacAMaxDelay = 35 ; // A mode maximum overlap delay
55+ int vacBMinDelay = 5 ; // B mode minimum release-press interval
56+ int vacBMaxDelay = 15 ; // B mode maximum release-press interval
57+
58+ static std::mt19937 rng (std::random_device{}());
5259
5360// Function declarations
5461LRESULT CALLBACK KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam);
5562LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
5663void InitNotifyIconData (HWND hwnd);
5764bool LoadConfig (const std::string& filename);
5865void CreateDefaultConfig (const std::string& filename);
59- void RestoreConfigFromBackup (const std::string& backupFilename, const std::string& destinationFilename);
60- std::string GetVersionInfo ();
66+ void RestoreConfigFromBackup (const std::string& backupFilename, const std::string& destinationFilename);
67+ std::string GetVersionInfo ();
6168void SendKey (int target, bool keyDown);
69+ void UpdateTrayIcon ();
70+ void WriteConfigValue (const std::string& filename, const std::string& key, int value);
6271
6372int main ()
6473{
@@ -107,9 +116,7 @@ int main()
107116
108117 // Initialize and add the system tray icon
109118 InitNotifyIconData (hwnd);
110-
111- // Seed RNG for VAC bypass delay
112- srand (static_cast <unsigned int >(time (NULL )));
119+ UpdateTrayIcon ();
113120
114121 // Set the hook
115122 hHook = SetWindowsHookEx (WH_KEYBOARD_LL, KeyboardProc, NULL , 0 );
@@ -159,10 +166,11 @@ void handleKeyDown(int keyCode)
159166 currentGroupInfo.previousKey = currentGroupInfo.activeKey ;
160167 currentGroupInfo.activeKey = keyCode;
161168
162- if (vacBypassBEnabled && ( rand () % 2 == 0 ) )
169+ if (vacBypassBEnabled && std::uniform_int_distribution< int >( 0 , 1 )(rng) == 0 )
163170 {
164171 SendKey (currentGroupInfo.previousKey , false );
165- Sleep ((rand () % 11 ) + 5 ); // 5-15ms delay
172+ std::this_thread::sleep_for (std::chrono::milliseconds (
173+ std::uniform_int_distribution<int >(vacBMinDelay, vacBMaxDelay)(rng)));
166174 SendKey (keyCode, true );
167175 }
168176 else
@@ -172,7 +180,8 @@ void handleKeyDown(int keyCode)
172180 {
173181 if (vacCounter >= 17 )
174182 {
175- Sleep ((rand () % 21 ) + 15 ); // 15-35ms overlap
183+ std::this_thread::sleep_for (std::chrono::milliseconds (
184+ std::uniform_int_distribution<int >(vacAMinDelay, vacAMaxDelay)(rng)));
176185 vacCounter = 0 ;
177186 }
178187 else
@@ -275,6 +284,32 @@ void InitNotifyIconData(HWND hwnd)
275284 Shell_NotifyIcon (NIM_ADD, &nid);
276285}
277286
287+ void UpdateTrayIcon ()
288+ {
289+ const TCHAR* iconFile = TEXT (" icon.ico" );
290+ if (isLocked)
291+ {
292+ iconFile = TEXT (" icon_off.ico" );
293+ }
294+ else if (vacBypassAEnabled || vacBypassBEnabled)
295+ {
296+ iconFile = TEXT (" icon_vac_bypass.ico" );
297+ }
298+
299+ HICON hIcon = (HICON)LoadImage (NULL , iconFile, IMAGE_ICON, 0 , 0 , LR_LOADFROMFILE);
300+ if (!hIcon)
301+ {
302+ hIcon = (HICON)LoadImage (NULL , TEXT (" icon.ico" ), IMAGE_ICON, 0 , 0 , LR_LOADFROMFILE);
303+ }
304+
305+ if (hIcon)
306+ {
307+ nid.hIcon = hIcon;
308+ Shell_NotifyIcon (NIM_MODIFY, &nid);
309+ DestroyIcon (hIcon);
310+ }
311+ }
312+
278313LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
279314{
280315 switch (msg)
@@ -313,28 +348,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
313348 isLocked = !isLocked;
314349
315350 // Update the tray icon
316- if (isLocked)
317- {
318- // Load icon_off.ico (OFF)
319- HICON hIconOff = (HICON)LoadImage (NULL , TEXT (" icon_off.ico" ), IMAGE_ICON, 0 , 0 , LR_LOADFROMFILE);
320- if (hIconOff)
321- {
322- nid.hIcon = hIconOff;
323- Shell_NotifyIcon (NIM_MODIFY, &nid);
324- DestroyIcon (hIconOff);
325- }
326- }
327- else
328- {
329- // Load icon.ico (ON)
330- HICON hIconOn = (HICON)LoadImage (NULL , TEXT (" icon.ico" ), IMAGE_ICON, 0 , 0 , LR_LOADFROMFILE);
331- if (hIconOn)
332- {
333- nid.hIcon = hIconOn;
334- Shell_NotifyIcon (NIM_MODIFY, &nid);
335- DestroyIcon (hIconOn);
336- }
337- }
351+ UpdateTrayIcon ();
338352 }
339353 break ;
340354
@@ -387,25 +401,21 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
387401 case ID_TRAY_LOCK_FUNCTION: // lock sticky keys
388402 {
389403 isLocked = !isLocked;
390- HICON hIcon = isLocked
391- ? (HICON)LoadImage (NULL , TEXT (" icon_off.ico" ), IMAGE_ICON, 0 , 0 , LR_LOADFROMFILE)
392- : (HICON)LoadImage (NULL , TEXT (" icon.ico" ), IMAGE_ICON, 0 , 0 , LR_LOADFROMFILE);
393- if (hIcon)
394- {
395- nid.hIcon = hIcon;
396- Shell_NotifyIcon (NIM_MODIFY, &nid);
397- DestroyIcon (hIcon);
398- }
404+ UpdateTrayIcon ();
399405 }
400406 break ;
401407 case ID_TRAY_VAC_BYPASS_A: // toggle VAC bypass A
402408 {
403409 vacBypassAEnabled = !vacBypassAEnabled;
410+ WriteConfigValue (" config.cfg" , " vac_bypass_a" , vacBypassAEnabled ? 1 : 0 );
411+ UpdateTrayIcon ();
404412 }
405413 break ;
406414 case ID_TRAY_VAC_BYPASS_B: // toggle VAC bypass B
407415 {
408416 vacBypassBEnabled = !vacBypassBEnabled;
417+ WriteConfigValue (" config.cfg" , " vac_bypass_b" , vacBypassBEnabled ? 1 : 0 );
418+ UpdateTrayIcon ();
409419 }
410420 break ;
411421 }
@@ -453,6 +463,35 @@ void CreateDefaultConfig(const std::string& filename)
453463 RestoreConfigFromBackup (backupFilename, filename);
454464}
455465
466+ void WriteConfigValue (const std::string& filename, const std::string& key, int value)
467+ {
468+ std::ifstream inFile (filename);
469+ std::vector<std::string> lines;
470+ bool found = false ;
471+ if (inFile.is_open ()) {
472+ std::string line;
473+ std::regex pat (" ^\\ s*" + key + " \\ s*=" );
474+ while (getline (inFile, line)) {
475+ if (std::regex_search (line, pat)) {
476+ lines.push_back (key + " = " + std::to_string (value));
477+ found = true ;
478+ } else {
479+ lines.push_back (line);
480+ }
481+ }
482+ inFile.close ();
483+ }
484+
485+ if (!found) {
486+ lines.push_back (key + " = " + std::to_string (value));
487+ }
488+
489+ std::ofstream outFile (filename, std::ios::trunc);
490+ for (const auto & l : lines) {
491+ outFile << l << " \n " ;
492+ }
493+ }
494+
456495// Check for config.cfg
457496bool LoadConfig (const std::string& filename)
458497{
@@ -464,6 +503,10 @@ bool LoadConfig(const std::string& filename)
464503
465504 string line; // Check for duplicated keys in the config file
466505 int id = 0 ;
506+ bool foundA = false , foundB = false ;
507+ bool foundAMin = false , foundAMax = false ;
508+ bool foundBMin = false , foundBMax = false ;
509+
467510 while (getline (configFile, line)) {
468511 istringstream iss (line);
469512 string key;
@@ -475,6 +518,7 @@ bool LoadConfig(const std::string& filename)
475518 }
476519 else if (getline (iss, key, ' =' ) && (iss >> value))
477520 {
521+ key = regex_replace (key, regex (" ^\\ s+|\\ s+$" ), " " );
478522 if (key.find (" key" ) != string::npos)
479523 {
480524 if (!KeyInfo[value].registered )
@@ -489,7 +533,43 @@ bool LoadConfig(const std::string& filename)
489533 return false ;
490534 }
491535 }
536+ else if (key == " vac_bypass_a" ) {
537+ vacBypassAEnabled = (value != 0 ); foundA = true ;
538+ }
539+ else if (key == " vac_bypass_b" ) {
540+ vacBypassBEnabled = (value != 0 ); foundB = true ;
541+ }
542+ else if (key == " vac_a_min_delay" ) {
543+ vacAMinDelay = value; foundAMin = true ;
544+ }
545+ else if (key == " vac_a_max_delay" ) {
546+ vacAMaxDelay = value; foundAMax = true ;
547+ }
548+ else if (key == " vac_b_min_delay" ) {
549+ vacBMinDelay = value; foundBMin = true ;
550+ }
551+ else if (key == " vac_b_max_delay" ) {
552+ vacBMaxDelay = value; foundBMax = true ;
553+ }
492554 }
493555 }
556+ configFile.close ();
557+
558+ if (vacAMinDelay < 0 || vacAMaxDelay < 0 || vacAMaxDelay < vacAMinDelay) {
559+ vacAMinDelay = 15 ;
560+ vacAMaxDelay = 35 ;
561+ }
562+ if (vacBMinDelay < 0 || vacBMaxDelay < 0 || vacBMaxDelay < vacBMinDelay) {
563+ vacBMinDelay = 5 ;
564+ vacBMaxDelay = 15 ;
565+ }
566+
567+ if (!foundA) WriteConfigValue (filename, " vac_bypass_a" , vacBypassAEnabled ? 1 : 0 );
568+ if (!foundB) WriteConfigValue (filename, " vac_bypass_b" , vacBypassBEnabled ? 1 : 0 );
569+ if (!foundAMin) WriteConfigValue (filename, " vac_a_min_delay" , vacAMinDelay);
570+ if (!foundAMax) WriteConfigValue (filename, " vac_a_max_delay" , vacAMaxDelay);
571+ if (!foundBMin) WriteConfigValue (filename, " vac_b_min_delay" , vacBMinDelay);
572+ if (!foundBMax) WriteConfigValue (filename, " vac_b_max_delay" , vacBMaxDelay);
573+
494574 return true ;
495- }
575+ }
0 commit comments