#include #include #include #include "platform.h" #include "../board/board.h" /* Required callbacks */ extern void keyboard_on_down(void); extern void keyboard_on_up(void); static HHOOK keyboard_hook_windows; bool sound_init(float volume) { /* Single channel volume (max = 0xFFFF) */ DWORD channel_volume = volume * 0xFFFF; /* Format is each channel is 16 bits */ DWORD stereo_volume = (channel_volume << 16) | channel_volume; if (waveOutSetVolume(NULL, stereo_volume) != MMSYSERR_NOERROR) return false; return true; } void sound_play(unsigned char *buffer, unsigned int) { PlaySound((const char *) buffer, NULL, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644985(v=vs.85) */ static 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); } bool 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) { return false; } return true; } 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 enter_idle(void) { MSG msg; BOOL status; while ((status = GetMessage(&msg, NULL, 0, 0))) { if (status == -1) { // error case printf("ERROR: Windows error, code %lu\n", GetLastError()); keyboard_unhook(); exit(1); } } } struct board *load_board(char *board_name) { char dll_name[100]; int r = snprintf(dll_name, 100, "./board/%s.dll", board_name); if (r < 0 || r >= 100) { printf("ERROR: Invalid board name.\n"); return false; } HMODULE board_module = LoadLibrary(dll_name); if (board_module == NULL) { printf("ERROR: Could not load board module.\n"); return false; } // HACK: Cast to void function ptr then cast back, otherwise types are incompatible to the compiler. fn_board_init board_init = (fn_board_init)(void (*)(void)) GetProcAddress( board_module, "board_init"); if (board_init == NULL) { printf("ERROR: No board initialisation function could be loaded.\n"); return false; } struct board_data data = { .sound_play = &sound_play }; return board_init(data); }