aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--test-visual.c174
2 files changed, 176 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 0ccc316..67bc8d5 100644
--- a/Makefile
+++ b/Makefile
@@ -43,7 +43,8 @@ TESTS =\
test-attachments\
test-details\
test-input\
- test-list
+ test-list\
+ test-visual
LOBJ = $(OBJ:.o=.lo)
diff --git a/test-visual.c b/test-visual.c
new file mode 100644
index 0000000..58ba20d
--- /dev/null
+++ b/test-visual.c
@@ -0,0 +1,174 @@
+/* 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;
+}