aboutsummaryrefslogtreecommitdiff
path: root/platform/win32.c
blob: 0b1da807ee691ae3186324c0dc123757f9695dfb (plain) (blame)
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);
}