/* See LICENSE file for copyright and license details. */ #include "libgamepad.h" #include #include #include #include #include #include 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; #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wformat-overflow" #endif sprintf(buffer, "%-*ji%ji", (int)(len - axis->max_len), axis->min, axis->max); #ifdef __GNUC__ # pragma GCC diagnostic pop #endif 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 = (size_t)(axis->value - axis->min) * len; x += (size_t)(axis->max - axis->min) / 2; x /= (size_t)(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(NULL, 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(NULL, 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, 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(NULL, 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; }