1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
#include <stdio.h>
#include <stdbool.h>
#include <windows.h>
#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)
{
DWORD channel_volume = volume * 0xFFFF;
if (waveOutSetVolume(NULL, (channel_volume << 16) | channel_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);
}
|