aboutsummaryrefslogblamecommitdiffstats
path: root/test-visual.c
blob: 58ba20d9e52b007e14f0baf520d0f09e8c90132c (plain) (tree)













































































































































































                                                                                                                           
/* See LICENSE file for copyright and license details. */
#include "libgamepad.h"

#include <sys/ioctl.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


struct abs_axis {
	intmax_t min;
	intmax_t max;
	intmax_t value;
	size_t max_len;
	size_t min_len;
};


static const char *
draw_axis(char buffer[], const struct abs_axis *axis, size_t len)
{
	/* TODO axis->value can actually be out of bounds */
	char value_buf[sizeof(intmax_t) * 3 + 2];
	size_t width, x, value_len;
	sprintf(buffer, "%-*ji%ji", (int)(len - axis->max_len), axis->min, axis->max);
	width = len - axis->max_len - axis->min_len;
	value_len = (size_t)sprintf(value_buf, "%ji", axis->value);
	x = axis->max_len + (width - value_len) / 2;
	memcpy(&buffer[x], value_buf, value_len);
	x = ((axis->value - axis->min) * len + (axis->max - axis->min) / 2) / (axis->max - axis->min);
	memmove(&buffer[x + sizeof("\033[m") - 1], &buffer[x], len - x);
	memcpy(&buffer[x], "\033[m", sizeof("\033[m") - 1);
	return buffer;
}


int
main(int argc, char *argv[])
{
	const struct input_absinfo *absinfo;
	struct libgamepad_input_event event;
	struct libgamepad_device gamepad;
	const char **button_names = NULL;
	size_t max_button_name_len = 0;
	size_t len, x, y, i;
	struct winsize size;
	size_t button_columns = 0;
	size_t button_rows = 0;
	size_t max_abs_axis_name_len = 0;
	struct abs_axis *abs_axes = NULL;
	char *buffer;
	ssize_t saxis_len;
	size_t axis_len, req_axis_len;
	size_t max_max_len = 0, max_min_len = 0;
	int r;

	if (argc != 2) {
		fprintf(stderr, "Please provide the path to the subdevice as the only command line argument\n");
		return 1;
	}

	if (libgamepad_open_device(&gamepad, AT_FDCWD, argv[1], O_RDONLY)) {
		perror("libgamepad_open_device");
		return 1;
	}

	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size)) {
		perror("TIOCGWINSZ");
		return 1;
	}

	size.ws_row = size.ws_row < 1 ? 1 : size.ws_row;
	size.ws_col = size.ws_col < 1 ? 1 : size.ws_col;

	if (gamepad.nbuttons) {
		button_names = calloc(gamepad.nbuttons, sizeof(*button_names));
		if (!button_names) {
			perror("calloc");
			return 1;
		}
		for (i = 0; i < gamepad.nbuttons; i++) {
			button_names[i] = libgamepad_get_button_name(gamepad.buttons[i]);
			len = strlen(button_names[i]);
			max_button_name_len = max_button_name_len > len ? max_button_name_len : len;
		}
		button_columns = (size.ws_col + 1) / (max_button_name_len + 3);
		button_columns = button_columns < 1 ? 1 : button_columns;
		button_rows = (gamepad.nbuttons + button_columns - 1) / button_columns;
	}

	if (gamepad.nabsolute_axes) {
		abs_axes = calloc(gamepad.nabsolute_axes, sizeof(*abs_axes));
		if (!abs_axes) {
			perror("calloc");
			return 1;
		}
		for (i = 0; i < gamepad.nabsolute_axes; i++) {
			len = strlen(libgamepad_get_absolute_axis_name(gamepad.absolute_axes[i]));
			max_abs_axis_name_len = max_abs_axis_name_len > len ? max_abs_axis_name_len : len;
			absinfo = libgamepad_get_absolute_axis_info(&gamepad, gamepad.absolute_axes[i]);
			assert(absinfo);
			abs_axes[i].min = (intmax_t)absinfo->minimum;
			abs_axes[i].max = (intmax_t)absinfo->maximum;
			abs_axes[i].value = (intmax_t)absinfo->value;
			abs_axes[i].min_len = (size_t)snprintf(NULL, 0, "%ji", abs_axes[i].min);
			max_min_len = max_min_len > abs_axes[i].min_len ? max_min_len : abs_axes[i].min_len;
			abs_axes[i].max_len = (size_t)snprintf(NULL, 0, "%ji", abs_axes[i].max);
			max_max_len = max_max_len > abs_axes[i].max_len ? max_max_len : abs_axes[i].max_len;
		}
	}

	req_axis_len = max_max_len + max_min_len + 2;
	req_axis_len += max_max_len > max_min_len ? max_max_len : max_min_len;
	saxis_len = (ssize_t)size.ws_col - (ssize_t)max_abs_axis_name_len - 3;
	if (saxis_len < (ssize_t)req_axis_len)
		saxis_len = (ssize_t)req_axis_len;
	axis_len = (size_t)saxis_len;
	buffer = malloc(axis_len + sizeof("\033[m"));
	if (!buffer) {
		perror("malloc");
		return 1;
	}

	printf("\033[m\033[H\033[J\033[?25l");

	for (i = 0; i < gamepad.nbuttons; i++) {
		x = i % button_columns;
		y = i / button_columns;
		printf("\033[%zu;%zuH%s¿%s?\033[m", y + 1, x * (max_button_name_len + 3) + 1,
		       libgamepad_get_button_is_pressed(&gamepad, (unsigned int)gamepad.buttons[i]) ? "\033[7m" : "\033[m",
		       button_names[i]);
	}
	printf("\033[%zuH", button_rows + 1);
	for (i = 0; i < gamepad.nabsolute_axes; i++) {
		printf("\n%-*s ¿\033[7m%s?", (int)max_abs_axis_name_len,
		       libgamepad_get_absolute_axis_name((unsigned int)gamepad.absolute_axes[i]),
		       draw_axis(buffer, &abs_axes[i], axis_len));
	}

	fflush(stdout);

	for (;;) {
		/* TODO use nonblocking; listen for window resize */
		r = libgamepad_next_event(&gamepad, &event);
		if (r <= 0) {
			if (!r || errno == EINTR)
				continue;
			perror("libgamepad_next_event");
			return 1;
		}

		if (event.type == LIBGAMEPAD_BUTTON) {
			i = (size_t)gamepad.button_map[event.code];
			x = i % button_columns;
			y = i / button_columns;
			printf("\033[%zu;%zuH%s[%s]\033[m", y + 1, x * (max_button_name_len + 3) + 1,
			       event.value ? "\033[7m" : "\033[m", button_names[i]);
			fflush(stdout);

		} else if (event.type == LIBGAMEPAD_ABSOLUTE_AXIS) {
			i = (size_t)gamepad.absolute_axis_map[event.code];
			y = button_rows + 1 + i;
			x = max_abs_axis_name_len + 1;
			abs_axes[i].value = (intmax_t)event.value;
			printf("\033[%zu;%zuH[\033[7m%s]", y + 1, x + 1, draw_axis(buffer, &abs_axes[i], axis_len));
			fflush(stdout);
		}
		/* TODO add relative axis */
	}

	return 0;
}