#include #include #include #include #include #define VOLUME 0.15 #include "board/boards.h" #define BOARD(str, buf) { .name = str, .wav = buf }, typedef struct { char *name; unsigned char* wav; } Board; Board boards[] = { BOARDS }; int const boards_n = sizeof(boards) / sizeof(boards[0]); void sound_init(void) { DWORD channel_volume = VOLUME * 0xFFFF; waveOutSetVolume(NULL, (channel_volume << 16) | channel_volume); } void sound_play(unsigned char *buffer) { PlaySound((const char *) buffer, NULL, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } Board *get_board(char *board_name) { for (int i = 0; i < boards_n; ++i) { if (strcmp(boards[i].name, board_name) == 0) return &boards[i]; } 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) { if (argc < 2 || (board = get_board(argv[1])) == NULL) { printf("Please provide a valid board name.\n"); printf("Valid boards: "); for (int i = 0; i < boards_n; ++i) { printf("%s%s", boards[i].name, (i == boards_n - 1) ? "\n" : ", "); } return 1; } sound_init(); 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; }