diff options
Diffstat (limited to 'platform')
| -rw-r--r-- | platform/linux.c | 165 | ||||
| -rw-r--r-- | platform/platform.h | 4 | ||||
| -rw-r--r-- | platform/win32.c | 4 | 
3 files changed, 169 insertions, 4 deletions
| diff --git a/platform/linux.c b/platform/linux.c new file mode 100644 index 0000000..6793abc --- /dev/null +++ b/platform/linux.c @@ -0,0 +1,165 @@ +#include <stdio.h> +#include <stdbool.h> +#include <signal.h> +#include <dlfcn.h> + +/* Audio with SDL for now (would prefer something more direct but it's proving hard) */ +#include <SDL2/SDL.h> +#include <SDL2/SDL_mixer.h> + +/* Capture input with XInput Extensions 2 */ +#include <X11/Xlib.h> +#include <X11/extensions/XInput2.h> + +#include "platform.h" +#include "../board/board.h" + +#define MIXER_FREQ 44100 +#define MIXER_FORMAT AUDIO_S16LSB +#define MIXER_CHANNELS 2 +#define MIXER_CHUNKSIZE 64 + +extern void keyboard_on_down(void); +extern void keyboard_on_up(void); + +static bool running = true; +static Display *display; +static int xi_opcode; + +bool sound_init(float volume) +{ +	if (SDL_Init(SDL_INIT_AUDIO) < 0) { +		printf("ERROR: Could not initialise SDL for audio.\n"); +		return false; +	} + +	if (Mix_OpenAudio(MIXER_FREQ, MIXER_FORMAT, MIXER_CHANNELS, MIXER_CHUNKSIZE)) { +		printf("ERROR: SDL mixer audio device open fail.\n"); +		return false; +	} + +	Mix_Volume(-1, volume * MIX_MAX_VOLUME); // set for all channels +	return true; +} + +// TODO: get rid of this with proper cache thing - only supports 1 sound for now +static Mix_Chunk *loaded_chunk = NULL; + +void sound_play(unsigned char *buffer, unsigned int buffer_len) +{ +	/* Load sample into SDL memory thing then into the mixer */ +	// TODO: Cache these samples as loaded into SDL +	if (loaded_chunk == NULL) { +		SDL_RWops *sample_rw = SDL_RWFromMem(buffer, buffer_len); +		loaded_chunk = Mix_LoadWAV_RW(sample_rw, 1); +		if (loaded_chunk == NULL) { +			printf("ERROR: Sample load fail - %s\n", Mix_GetError()); +			running = false; +			return; +		} +	} +	 +	/* -1 means not-in-use channel */ +	Mix_PlayChannel(-1, loaded_chunk, 0); +} + +/*  + * Uses XInput2 extensions to hook into the keyboard. + * Noticed this was possible when I used: + *   $ xinput test-xi2 --root + * Resources used: + *   - https://github.com/freedesktop/xorg-xinput/blob/master/src/test_xi2.c  + *   - https://cgit.freedesktop.org/xorg/proto/inputproto/commit/?id=f4f09d40e0fd94d267b280f2a82385dca1141347 + */ +bool keyboard_hook(void) +{ +	fprintf(stderr, "Setting up keyboard hook...\n"); + +	/* Connect to X, then use XI2 to grab events */ +	display = XOpenDisplay(NULL); +	if (display == NULL) { +		printf("ERROR: Could not open X display.\n"); +		return false; +	} +	Window root_window = DefaultRootWindow(display); + +	int xi_event, xi_error; +	if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &xi_event, &xi_error)) { +		printf("ERROR: Could not find XInputExtension.\n"); +		return false; +	} + +	fprintf(stderr, "Setting up XInput2 filter...\n"); +	unsigned char mask[XIMaskLen(XI_RawKeyPress) + XIMaskLen(XI_RawKeyRelease)] = {0}; +	XISetMask(mask, XI_RawKeyPress); +	XISetMask(mask, XI_RawKeyRelease); +	XIEventMask event_mask = { +		.deviceid = XIAllMasterDevices, +		.mask_len = sizeof(mask)/sizeof(mask[0]), +		.mask = mask, +	}; +	XISelectEvents(display, root_window, &event_mask, 1); +	return true; +} + +void keyboard_unhook(void) +{ +	fprintf(stderr, "Cleaning up audio system and hooks...\n"); +	Mix_Quit(); +	SDL_Quit(); +	XCloseDisplay(display); +} + +void handle_signal(int) +{ +	running = false; +} +void enter_idle(void) +{ +	struct sigaction sa = { .sa_handler = handle_signal }; +	sigaction(SIGINT, &sa, NULL); + +	XGenericEventCookie event; +	while (running) { +		XNextEvent(display, (XEvent *) &event); +		/* XGenEventData fails if it isn't a XGenericEventCookie anyway */ +		if (XGetEventData(display, &event) && event.type == GenericEvent && event.extension == xi_opcode) { +			/* char *ur = event.evtype == XI_RawKeyPress ? "press" : "release"; */ +			/* unsigned int kc = ((XIDeviceEvent *) event.data)->detail; */ + +			if (event.evtype == XI_RawKeyPress) { +				keyboard_on_down(); +			} else if (event.evtype == XI_RawKeyRelease) { +				keyboard_on_up(); +			} +		} +		XFreeEventData(display, &event); +	} +} + +struct board *load_board(char *board_name) +{ +	char so_name[100]; +	int r = snprintf(so_name, 100, "./board/%s.so", board_name); +	if (r < 0 || r >= 100) { +		printf("ERROR: Invalid board name.\n"); +		return false; +	} + +	void *board_module = dlopen(so_name, RTLD_LAZY); +	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 = dlsym(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); +} diff --git a/platform/platform.h b/platform/platform.h index a5e5db4..ca07789 100644 --- a/platform/platform.h +++ b/platform/platform.h @@ -8,11 +8,11 @@  struct board *load_board(char *board_name);  bool sound_init(float volume); -void sound_play(unsigned char *buffer); +void sound_play(unsigned char *buffer, unsigned int buffer_len);  void keyboard_unhook(void);  bool keyboard_hook(void);  void enter_idle(void); -#endif /* CLAK_PLATFORM_H_ */
\ No newline at end of file +#endif /* CLAK_PLATFORM_H_ */ diff --git a/platform/win32.c b/platform/win32.c index 1dccc7b..0b1da80 100644 --- a/platform/win32.c +++ b/platform/win32.c @@ -19,7 +19,7 @@ bool sound_init(float volume)  	return true;  } -void sound_play(unsigned char *buffer) +void sound_play(unsigned char *buffer, unsigned int)  {  	PlaySound((const char *) buffer, NULL, SND_MEMORY | SND_ASYNC | SND_NODEFAULT);  } @@ -107,4 +107,4 @@ struct board *load_board(char *board_name)  		.sound_play = &sound_play  	};  	return board_init(data); -}
\ No newline at end of file +} | 
