From ecb4cff6f34783fd345419c0069c685793521e80 Mon Sep 17 00:00:00 2001 From: Nicholas Tay Date: Sat, 7 May 2022 23:00:13 +1000 Subject: Windows low-level hook working Had to do some stuff with the previous VK tracking, just like what I had to do in C# with Meccha, since it keeps firing events. GetMessage part also just blocks to ensure we keep receiving Windows events, but not sure what to do otherwise. --- clak.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/clak.c b/clak.c index 5203032..99b1482 100644 --- a/clak.c +++ b/clak.c @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -25,9 +26,9 @@ void sound_init(void) waveOutSetVolume(NULL, (channel_volume << 16) | channel_volume); } -void sound_play(unsigned char* buffer) +void sound_play(unsigned char *buffer) { - PlaySound((const char *) buffer, NULL, SND_MEMORY | SND_SYNC | SND_NODEFAULT); + PlaySound((const char *) buffer, NULL, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } Board *get_board(char *board_name) @@ -39,9 +40,77 @@ Board *get_board(char *board_name) return NULL; } +HHOOK keyboard_hook_windows; + +void keyboard_unhook(void) +{ + fprintf(stderr, "Cleaning up keyboard hook...\n"); + if (!UnhookWindowsHookEx(keyboard_hook_windows)) + printf("WARN: Windows keyboard hook could not be cleaned up! Error code: %lu\n", GetLastError()); +} + +void do_exit(int code) +{ + keyboard_unhook(); + fprintf(stderr, "Goodbye.\n"); + exit(code); +} +void on_clean_exit(void) { do_exit(0); } + +Board *board = NULL; + +void keyboard_on_down(void) +{ + if (board == NULL) + return; + sound_play(board->wav); +} + +void keyboard_on_up(void) +{ + // if (board == NULL) + // return; +} + +/* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644985(v=vs.85) */ +LRESULT CALLBACK keyboard_windows_callback(int nCode, WPARAM wParam, LPARAM lParam) +{ + /* Needed to prevent repeat fires */ + static DWORD prev_vk = 0; + + /* Do not handle unless nCode >= 0, pass to next hook right away */ + if (nCode >= 0) { + BOOL down = wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN; + KBDLLHOOKSTRUCT *hook_struct = (KBDLLHOOKSTRUCT *) lParam; + DWORD vk = hook_struct->vkCode; + if (down && vk != prev_vk) { + keyboard_on_down(); + prev_vk = vk; + } else { + keyboard_on_up(); + /* Seems like repeat strokes are 0x0 or 0x1, and 'real' ones have 0x80 flag?? + Not really sure how to handle this one, can't find many docs... + Saw this though: https://github.com/pyglet/pyglet/blob/838d004d68fcc5c3ce83b733e3d088fad0643859/pyglet/window/win32/__init__.py#L794= */ + if (hook_struct->flags & 0x80) + prev_vk = 0; + } + } + + return CallNextHookEx(NULL, nCode, wParam, lParam); +} + +void keyboard_hook(void) +{ + fprintf(stderr, "Setting up keyboard hook...\n"); + keyboard_hook_windows = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_windows_callback, NULL, 0); + if (keyboard_hook_windows == NULL) { + printf("ERROR: Could not set up Windows keyboard hook.\n"); + do_exit(1); + } +} + int main(int argc, char **argv) { - Board *board; if (argc < 2 || (board = get_board(argv[1])) == NULL) { printf("Please provide a valid board name.\n"); printf("Valid boards: "); @@ -52,8 +121,19 @@ int main(int argc, char **argv) } sound_init(); - printf("name: %s\n", board->name); - sound_play(board->wav); + keyboard_hook(); + atexit(on_clean_exit); + fprintf(stderr, "Hooks set up, welcome to Clak!\n"); + + MSG msg; + BOOL status; + while ((status = GetMessage(&msg, NULL, 0, 0))) { + if (status == -1) { + // error case + printf("ERROR: Windows error, code %lu\n", GetLastError()); + exit(1); + } + } return 0; } \ No newline at end of file -- cgit