diff --git a/src/capsblock/main.c b/src/capsblock/main.c index af7dcb3..64da55d 100644 --- a/src/capsblock/main.c +++ b/src/capsblock/main.c @@ -7,62 +7,107 @@ #endif #include -// state to suppress hook events when generating new caps-lock events -volatile bool gSkip = false; +// semaphore used to signal the toggle-key thread +HANDLE gCapsEventSem; + +// shutdown flag for the thread +volatile bool gShutdown = false; // invoked when a low-level keyboard event occurs LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM msg, LPARAM ctx) { // listen for key-up events of the caps-lock key - if (!gSkip && !code >= 0 && msg == WM_KEYUP) { + if (!code >= 0 && msg == WM_KEYUP) { KBDLLHOOKSTRUCT* kbd = (KBDLLHOOKSTRUCT*)ctx; if (kbd->vkCode == VK_CAPITAL) { // check if we have caps-lock enabled in our current state bool hasCapsLock = ((GetKeyState(VK_CAPITAL) & 0x0001) != 0); if (hasCapsLock) { - // before we attempt to toggle off the caps-lock state, - // flag our hook as disabled, to ensure we do not attempt - // to process the state of any caps-lock keys issued from - // our hook - gSkip = true; + // signal to the toggle-key thread to check/enforce + // the key state + ReleaseSemaphore(gCapsEventSem, 1, NULL); + } + } + } + + return CallNextHookEx(NULL, code, msg, ctx); +} +// thread used to wait for events to check/enforce the key state +// of the caps lock key +#pragma warning(disable: 4100) +DWORD WINAPI ToggleKeyThread(void* data) +{ + while (!gShutdown) { + DWORD signal = WaitForSingleObject(gCapsEventSem, INFINITE); + switch (signal) { + case WAIT_OBJECT_0: + // check (again) if we have caps-lock enabled in our current state + bool hasCapsLock = ((GetKeyState(VK_CAPITAL) & 0x0001) != 0); + if (hasCapsLock) { // generate two additional key events: /// - // 1) a key-up event to "complete" the toggled on state - // 2) a key-down event to trigger a new caps-lock event - // - // do not consume this active event, to complete the final - // key-up event - INPUT input[2] = {0}; + // 1) a key-down event to trigger a new caps-lock event + // 2) a key-up event to "complete" the event + INPUT input[2] = { 0 }; input[0].type = input[1].type = INPUT_KEYBOARD; - input[0].ki.wVk = input[1].ki.wVk = VK_CAPITAL; - input[0].ki.dwFlags = KEYEVENTF_KEYUP; + input[0].ki.wVk = input[1].ki.wVk = VK_CAPITAL; + input[1].ki.dwFlags = KEYEVENTF_KEYUP; SendInput(2, input, sizeof(INPUT)); - - // after issuing our key changes, re-enable this hook - gSkip = false; } + break; + + case WAIT_TIMEOUT: + // spurious wakeup + break; + + default: + // broken state + gShutdown = true; + break; } } - return CallNextHookEx(NULL, code, msg, ctx); + return 0; } #pragma warning(disable: 4100) int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { + // create a signal between that can be issued by the keyboard hook to + // out toggle-key thread + gCapsEventSem = CreateSemaphore(NULL, 1, 1, NULL); + if (!gCapsEventSem) { + fprintf(stderr, "unable to create keyboard hook\n"); + return 1; + } + // create a hook to listen for keyboard events HHOOK hook = SetWindowsHookEx( WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0); if (!hook) { fprintf(stderr, "unable to create keyboard hook\n"); - return 1; + return 2; + } + + // build a thread reposible for toggling the cap-lock key off + HANDLE tkt = CreateThread(NULL, 0, ToggleKeyThread, NULL, 0, NULL); + if (!tkt) { + fprintf(stderr, "unable to create toggle-key thread\n"); + return 3; } // consume all messages MSG msg; while(GetMessage(&msg, NULL, 0, 0) != 0); + // cleanup + gShutdown = true; + ReleaseSemaphore(gCapsEventSem, 1, NULL); + WaitForSingleObject(tkt, INFINITE); + CloseHandle(tkt); + CloseHandle(gCapsEventSem); + return 0; }