-- cgit v1.2.3 From d50660a0a7ae0db68743aa30a1c8ba205448d45e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 06:42:55 +0000 Subject: Add Win32 keyboard monitoring service (dirtytalk.cpp) Co-authored-by: F1shcake-onegai <202318185+F1shcake-onegai@users.noreply.github.com> --- _codeql_detected_source_root | 1 + dirtytalk.cpp | 241 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 120000 _codeql_detected_source_root create mode 100644 dirtytalk.cpp 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 +#include +#include +#include + +#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(L"DirtyTalk"), + reinterpret_cast(ServiceMain) }, + { NULL, NULL } + }; + + StartServiceCtrlDispatcherW(serviceTable); + return 0; +} -- cgit v1.2.3