diff options
Diffstat (limited to '')
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | board/Makefile | 3 | ||||
| -rw-r--r-- | board/board.h | 4 | ||||
| -rw-r--r-- | board/simple_board.h | 9 | ||||
| -rw-r--r-- | clak.c | 4 | ||||
| -rw-r--r-- | platform/linux.c | 165 | ||||
| -rw-r--r-- | platform/platform.h | 4 | ||||
| -rw-r--r-- | platform/win32.c | 4 | 
8 files changed, 187 insertions, 13 deletions
| @@ -2,7 +2,7 @@ NAME = clak  PREFIX = $(HOME)/.local  CC = gcc -CFLAGS += -std=c99 -Wall -Wextra -Wshadow -Werror -pedantic +CFLAGS += -std=c99 -Wall -Wextra -Wshadow -Werror  ifeq ($(OS),Windows_NT)      LDLIBS = -lWinmm @@ -11,6 +11,9 @@ else      UNAME_S := $(shell uname)      ifeq ($(UNAME_S),Linux)          PLATFORM = linux +		PKG_CONF_LIBS = sdl2 SDL2_mixer x11 xi +		CFLAGS += `pkg-config --cflags $(PKG_CONF_LIBS)` +		LDLIBS += `pkg-config --libs $(PKG_CONF_LIBS)`      endif  endif @@ -21,4 +24,4 @@ $(NAME): $(NAME).c platform/$(PLATFORM).c  clean:  	rm -f *.o -	rm -f $(NAME)
\ No newline at end of file +	rm -f $(NAME) diff --git a/board/Makefile b/board/Makefile index 24cb10c..03449cc 100644 --- a/board/Makefile +++ b/board/Makefile @@ -7,6 +7,7 @@ ifeq ($(OS),Windows_NT)      OUTEXT = dll  else      OUTEXT = so +	CFLAGS += -fPIC  endif  default: all @@ -29,4 +30,4 @@ clean:  	rm -f *.o  	rm -f *.dll  	rm -f *.so -	rm -f */sound.h
\ No newline at end of file +	rm -f */sound.h diff --git a/board/board.h b/board/board.h index c7dfc1f..36d39fb 100644 --- a/board/board.h +++ b/board/board.h @@ -8,9 +8,9 @@ struct board {  };  struct board_data { -	void (*sound_play)(unsigned char *buffer); +	void (*sound_play)(unsigned char *buffer, unsigned int buffer_len);  };  typedef struct board *(*fn_board_init)(struct board_data data); -#endif /* CLAK_BOARD_H_ */
\ No newline at end of file +#endif /* CLAK_BOARD_H_ */ diff --git a/board/simple_board.h b/board/simple_board.h index 9d9836b..f0997c7 100644 --- a/board/simple_board.h +++ b/board/simple_board.h @@ -2,17 +2,20 @@  static struct board_data board_data; +#define WAV_LEN(W) PREPROC_CONCAT(W,_len) +#define PREPROC_CONCAT(A, B) A ## B +  void board_on_down(void)  {  #ifdef BOARD_DOWN_WAV -	board_data.sound_play(BOARD_DOWN_WAV); +	board_data.sound_play(BOARD_DOWN_WAV, WAV_LEN(BOARD_DOWN_WAV));  #endif  }  void board_on_up(void)  {  #ifdef BOARD_UP_WAV -	board_data.sound_play(BOARD_UP_WAV); +	board_data.sound_play(BOARD_UP_WAV, WAV_LEN(BOARD_UP_WAV));  #endif  } @@ -30,4 +33,4 @@ struct board *board_init(struct board_data data)  {  	board_data = data;  	return &board; -}
\ No newline at end of file +} @@ -40,12 +40,14 @@ int main(int argc, char **argv)  		// TODO: List valid boards.  		return 1;  	} +	fprintf(stderr, "Loading board...\n");  	if ((board = load_board(argv[1])) == NULL) {  		printf("Failed to load board, exiting.\n");  		return 1;  	}  	fprintf(stderr, "Loaded board: %s\n", board->name); +	fprintf(stderr, "Initialisating sound system...\n");  	if (!sound_init(VOLUME)) {  		printf("ERROR: Could not initialise sound system.\n");  		return 1; @@ -60,4 +62,4 @@ int main(int argc, char **argv)  	enter_idle();  	return 0; -}
\ No newline at end of file +} 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 +} | 
