summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMuxian Li <muxianli5401@gmail.com>2026-03-02 13:12:21 -0500
committerGitHub <noreply@github.com>2026-03-02 13:12:21 -0500
commit36313ffc56bd454ff8471c57f1ab284e112bd3b9 (patch)
tree5c9b7c27f45217b17822e7085666f0bb82a1573a
parent534410c2139e905563cf963ca1c7725187623d64 (diff)
parentd50660a0a7ae0db68743aa30a1c8ba205448d45e (diff)
Merge pull request #1 from F1shcake-onegai/copilot/monitor-keyboard-activity-serviceHEADmain
Add Win32 keyboard activity monitoring service
l---------_codeql_detected_source_root1
-rw-r--r--dirtytalk.cpp241
2 files changed, 242 insertions, 0 deletions
diff --git a/_codeql_detected_source_root b/_codeql_detected_source_root
new file mode 120000
index 0000000..945c9b4
--- /dev/null
+++ b/_codeql_detected_source_root
@@ -0,0 +1 @@
+. \ No newline at end of file
diff --git a/dirtytalk.cpp b/dirtytalk.cpp
new file mode 100644
index 0000000..fb8ec62
--- /dev/null
+++ b/dirtytalk.cpp
@@ -0,0 +1,241 @@
+// dirtytalk.cpp - Win32 keyboard monitoring service
+// Runs as a Windows service (no window, no console, not in taskbar).
+// Monitors keyboard activity: counts key presses and measures speed.
+// When a threshold is reached, plays a random effect WAV file.
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+#include <cstdlib>
+#include <ctime>
+
+#pragma comment(lib, "winmm.lib")
+
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+// Number of available effect WAV files (effect1.wav … effectN.wav)
+const int EFFECT_FILE_COUNT = 5;
+
+// Threshold for "fast" typing: ~50 rapid key presses trigger the effect
+const int FAST_PRESS_THRESHOLD = 50;
+
+// Threshold for "slow" typing: ~300 key presses trigger the effect
+const int SLOW_PRESS_THRESHOLD = 300;
+
+// A key press is considered "fast" when the inter-key interval is below this
+const DWORD FAST_INTERVAL_MS = 200;
+
+// ---------------------------------------------------------------------------
+// Globals
+// ---------------------------------------------------------------------------
+
+// currentValue records keyboard activity as a running correlation between
+// the number of keys pressed and their typing speed.
+// Higher speed contributions accumulate the value more quickly, allowing fast
+// typists to hit FAST_PRESS_THRESHOLD and slow typists SLOW_PRESS_THRESHOLD.
+double currentValue = 0.0;
+
+// Service infrastructure
+static SERVICE_STATUS g_ServiceStatus = {};
+static SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
+static HANDLE g_StopEvent = INVALID_HANDLE_VALUE;
+static HHOOK g_KeyboardHook = NULL;
+
+// Keyboard tracking state
+static DWORD g_LastKeyTime = 0;
+static int g_PressCount = 0;
+
+// ---------------------------------------------------------------------------
+// Helper: resolve an effect file path from the service's own directory
+// ---------------------------------------------------------------------------
+// Determines the full path of 'filename' by using the module's location as
+// the base directory (stored implicitly in the executable path variable).
+static void GetEffectFilePath(const wchar_t *filename, wchar_t *outPath, DWORD outSize)
+{
+ wchar_t modulePath[MAX_PATH] = {};
+ GetModuleFileNameW(NULL, modulePath, MAX_PATH);
+
+ // Trim to directory portion
+ wchar_t *lastSep = wcsrchr(modulePath, L'\\');
+ if (lastSep)
+ *(lastSep + 1) = L'\0'; // keep trailing backslash
+ else
+ modulePath[0] = L'\0'; // no directory component
+
+ _snwprintf_s(outPath, outSize, _TRUNCATE, L"%s%s", modulePath, filename);
+}
+
+// ---------------------------------------------------------------------------
+// Helper: play a randomly chosen effect WAV file
+// ---------------------------------------------------------------------------
+static void PlayRandomEffect()
+{
+ int index = (rand() % EFFECT_FILE_COUNT) + 1;
+ wchar_t filename[64];
+ swprintf_s(filename, L"effect%d.wav", index);
+
+ wchar_t fullPath[MAX_PATH];
+ GetEffectFilePath(filename, fullPath, MAX_PATH);
+
+ PlaySoundW(fullPath, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
+}
+
+// ---------------------------------------------------------------------------
+// Helper: monitor keyboard activity
+// Updates currentValue and triggers the effect when a threshold is reached.
+// ---------------------------------------------------------------------------
+static void MonitorKeyboardActivity()
+{
+ DWORD now = GetTickCount();
+ DWORD interval = (g_LastKeyTime > 0) ? (now - g_LastKeyTime) : FAST_INTERVAL_MS;
+ g_LastKeyTime = now;
+
+ // Speed factor: fast presses contribute more than 1.0, slow ones contribute 1.0.
+ // Guard against division by near-zero by adding 1 ms.
+ double speedFactor = (interval < FAST_INTERVAL_MS)
+ ? (double)FAST_INTERVAL_MS / (double)(interval + 1)
+ : 1.0;
+
+ g_PressCount++;
+ currentValue += speedFactor; // correlates press count with speed
+
+ // Determine whether to trigger the effect
+ bool isFast = (speedFactor > 1.0);
+ bool triggered = (isFast && g_PressCount >= FAST_PRESS_THRESHOLD)
+ || (!isFast && g_PressCount >= SLOW_PRESS_THRESHOLD);
+
+ if (triggered)
+ {
+ // Reset tracking state before playing so the hook remains responsive
+ g_PressCount = 0;
+ currentValue = 0.0;
+ PlayRandomEffect();
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Low-level keyboard hook procedure
+// ---------------------------------------------------------------------------
+static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ if (nCode == HC_ACTION && wParam == WM_KEYDOWN)
+ MonitorKeyboardActivity();
+
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+// ---------------------------------------------------------------------------
+// Service worker thread: installs the hook and runs a message pump
+// ---------------------------------------------------------------------------
+static DWORD WINAPI ServiceWorkerThread(LPVOID /*lpParam*/)
+{
+ srand((unsigned int)time(NULL));
+
+ g_KeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc,
+ GetModuleHandleW(NULL), 0);
+ if (g_KeyboardHook == NULL)
+ return GetLastError();
+
+ // WH_KEYBOARD_LL requires a message loop on the installing thread
+ MSG msg;
+ while (WaitForSingleObject(g_StopEvent, 0) == WAIT_TIMEOUT)
+ {
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ Sleep(10);
+ }
+
+ UnhookWindowsHookEx(g_KeyboardHook);
+ g_KeyboardHook = NULL;
+ return ERROR_SUCCESS;
+}
+
+// ---------------------------------------------------------------------------
+// Service control handler
+// ---------------------------------------------------------------------------
+static VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode)
+{
+ if (ctrlCode == SERVICE_CONTROL_STOP &&
+ g_ServiceStatus.dwCurrentState == SERVICE_RUNNING)
+ {
+ g_ServiceStatus.dwControlsAccepted = 0;
+ g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ g_ServiceStatus.dwWin32ExitCode = 0;
+ g_ServiceStatus.dwCheckPoint = 4;
+ SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
+ SetEvent(g_StopEvent);
+ }
+}
+
+// ---------------------------------------------------------------------------
+// ServiceMain
+// ---------------------------------------------------------------------------
+static VOID WINAPI ServiceMain(DWORD /*argc*/, LPWSTR * /*argv*/)
+{
+ g_StatusHandle = RegisterServiceCtrlHandlerW(L"DirtyTalk", ServiceCtrlHandler);
+ if (!g_StatusHandle)
+ return;
+
+ ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
+ g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ g_ServiceStatus.dwControlsAccepted = 0;
+ SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
+
+ g_StopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if (!g_StopEvent)
+ {
+ g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ g_ServiceStatus.dwWin32ExitCode = GetLastError();
+ SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
+ return;
+ }
+
+ g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ g_ServiceStatus.dwWin32ExitCode = 0;
+ g_ServiceStatus.dwCheckPoint = 0;
+ SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
+
+ HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
+ if (!hThread)
+ {
+ CloseHandle(g_StopEvent);
+ g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ g_ServiceStatus.dwWin32ExitCode = GetLastError();
+ SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
+ return;
+ }
+
+ WaitForSingleObject(hThread, INFINITE);
+ CloseHandle(hThread);
+ CloseHandle(g_StopEvent);
+
+ g_ServiceStatus.dwControlsAccepted = 0;
+ g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ g_ServiceStatus.dwWin32ExitCode = 0;
+ g_ServiceStatus.dwCheckPoint = 3;
+ SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
+}
+
+// ---------------------------------------------------------------------------
+// Entry point — no console window, not in taskbar (WinMain, not main)
+// ---------------------------------------------------------------------------
+int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/,
+ LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
+{
+ SERVICE_TABLE_ENTRYW serviceTable[] =
+ {
+ { const_cast<LPWSTR>(L"DirtyTalk"),
+ reinterpret_cast<LPSERVICE_MAIN_FUNCTIONW>(ServiceMain) },
+ { NULL, NULL }
+ };
+
+ StartServiceCtrlDispatcherW(serviceTable);
+ return 0;
+}