/* See LICENSE file for copyright and license details. */ #include "common.h" USAGE("[-2s]"); static volatile sig_atomic_t caught_sigterm = 0; static volatile sig_atomic_t caught_sigwinch = 1; static int with_small = 0; static int with_quad = 0; static const char **large_digits[] = { large_0, large_1, large_2, large_3, large_4, large_5, large_6, large_7, large_8, large_9 }; static const char **small_digits[] = { small_0, small_1, small_2, small_3, small_4, small_5, small_6, small_7, small_8, small_9 }; static void sigterm(int signo) { caught_sigterm = 1; (void) signo; } static void sigwinch(int signo) { caught_sigwinch = 1; (void) signo; } static void print_number(const char ***str, int left, size_t height) { size_t r, c; for (r = 0;;) { printf("%*.s", left, ""); for (c = 0; str[c]; c++) fprintf(stdout, "%s", str[c][r]); if (++r == height) { fprintf(stdout, "\033[K"); break; } else { fprintf(stdout, "\033[K\n"); } } } static int display_counter(void) { union libterminput_input input; struct libterminput_state termctx; char buf[sizeof(intmax_t) * 3 + 2]; const char **digits[sizeof(intmax_t) * 3 + 2]; intmax_t counter = {0}; int r, i, x = 0, y = 0, was_quad = 0, len, width; int redraw = 1; struct winsize winsize; memset(&termctx, 0, sizeof(termctx)); while (!caught_sigterm) { if (caught_sigwinch) { if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { if (errno == EINTR) continue; goto fail; } caught_sigwinch = 0; y = winsize.ws_row; x = winsize.ws_col; if (was_quad) { was_quad = 0; for (r = 0; r < y; r++) printf("%s\033#5", r ? "\n" : "\033[H"); } redraw = 1; } else { r = libterminput_read(STDIN_FILENO, &input, &termctx); if (r <= 0) { if (!r) goto out; if (errno == EAGAIN) break; if (errno == EINTR) continue; goto fail; } if (input.type == LIBTERMINPUT_KEYPRESS) { if (input.keypress.key == LIBTERMINPUT_UP || input.keypress.key == LIBTERMINPUT_RIGHT) { counter += 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_DOWN || input.keypress.key == LIBTERMINPUT_LEFT) { counter -= 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) { goto out; } else if (!strcmp(input.keypress.symbol, "r") || !strcmp(input.keypress.symbol, "R")) { counter = 0; redraw = 1; } else if (!strcmp(input.keypress.symbol, "+")) { counter += 1; redraw = 1; } else if (!strcmp(input.keypress.symbol, "-")) { counter -= 1; redraw = 1; } else if (!strcmp(input.keypress.symbol, "L") && (input.keypress.mods & LIBTERMINPUT_CTRL)) { redraw = 1; } } } else if (input.type == LIBTERMINPUT_MOUSEEVENT && input.mouseevent.event == LIBTERMINPUT_PRESS && input.mouseevent.button == LIBTERMINPUT_BUTTON1) { if (input.mouseevent.y > (size_t)y - 3) { if (input.mouseevent.x <= 10) { goto out; } else if (input.mouseevent.x > (size_t)x - 11) { counter = 0; redraw = 1; } } if (!redraw) { if (input.mouseevent.y - 1 < (size_t)(y - 1) / 2) { counter += 1; redraw = 1; } else { counter -= 1; redraw = 1; } } } } if (!redraw) continue; redraw = 0; len = sprintf(buf, "%ji", counter); printf("\033[H"); if (x >= (int)sizeof("Click on lower half on screen to increase value by 1") - 1) { printf("%*.sClick on upper half on screen to increase value by 1", (x - ((int)sizeof("Click on upper half on screen to increase value by 1") - 1)) / 2, ""); } if (x >= (width = len * LARGE_X + (counter < 0 ? LARGE_XM - LARGE_X : 0)) && y >= LARGE_Y + 5) { for (r = 0; r < (y - LARGE_Y) / 2; r++) printf("\033[K\n"); for (i = 0; i < len; i++) { if (isdigit(buf[i])) digits[i] = large_digits[buf[i] & 15]; else digits[i] = large_minus; } digits[i] = NULL; print_number(digits, (x - width) / 2, LARGE_Y); r += LARGE_Y; } else if (with_small && x >= (width = len * SMALL_X + (counter < 0 ? SMALL_XM - SMALL_X : 0)) && y > SMALL_Y + 6) { for (r = 0; r < (y - SMALL_Y) / 2; r++) printf("\033[K\n"); for (i = 0; i < len; i++) { if (isdigit(buf[i])) digits[i] = small_digits[buf[i] & 15]; else digits[i] = small_minus; } digits[i] = NULL; print_number(digits, (x - width) / 2, SMALL_Y); r += SMALL_Y; } else if (with_quad && x > len * 2 && y > 3) { printf("\033#3%*.s%s\033[K\n", (x / 2 - len) / 2, "", buf); printf("\033#4%*.s%s\033[K\n", (x / 2 - len) / 2, "", buf); printf("\033#5"); was_quad = 1; r = 2; } else { printf("%*.s%s\033[K", (x - len) / 2, "", buf); r = 1; } if (x >= 21 + (int)sizeof("Click on lower half on screen to decrease value by 1") - 1 && r + 3 <= y) { width = 21 + ((int)sizeof("Click on lower half on screen to decrease value by 1") - 1); for (; r <= y - 3; r++) printf("\033[K\n"); printf("┌────────┐%*.s┌─────────┐\033[K\n", x - 21, ""); printf("│ \033[1mQ\033[muit │%*.s│ \033[1mR\033[meset │\033[K\n", x - 21, ""); printf("└────────┘%*.s%s%*.s└─────────┘\033[K", (x - width) / 2, "", "Click on lower half on screen to decrease value by 1", x - (x - width) / 2 - width, ""); } else if (x >= (int)sizeof("Click on lower half on screen to decrease value by 1") - 1 && r + 4 <= y) { for (; r <= y - 4; r++) printf("\033[K\n"); printf("%*.sClick on lower half on screen to decrease value by 1\033[K", (x - ((int)sizeof("Click on lower half on screen to decrease value by 1") - 1)) / 2, ""); printf("\n┌────────┐%*.s┌─────────┐\033[K\n", x - 21, ""); printf("│ \033[1mQ\033[muit │%*.s│ \033[1mR\033[meset │\033[K\n", x - 21, ""); printf("└────────┘%*.s└─────────┘\033[K", x - 21, ""); } else if (x >= 21 && r + 3 <= y) { for (; r <= y - 3; r++) printf("\033[K\n"); printf("┌────────┐%*.s┌─────────┐\033[K\n", x - 21, ""); printf("│ \033[1mQ\033[muit │%*.s│ \033[1mR\033[meset │\033[K\n", x - 21, ""); printf("└────────┘%*.s└─────────┘\033[K", x - 21, ""); } else { printf("\033[J"); fflush(stdout); continue; } printf("\033[D\033[J─┘"); fflush(stdout); } out: return 0; fail: return -1; } int main(int argc, char *argv[]) { int tcset = 0, saved_errno; struct sigaction sigact; struct termios stty, saved_stty; ARGBEGIN { case '2': with_quad = 1; break; case 's': with_small = 1; break; default: usage(); } ARGEND; if (argc) usage(); fprintf(stdout, "\033[?1049h\033[?25l\033[?1049;1003;1006h"); memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = sigterm; sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); sigact.sa_handler = sigwinch; sigaction(SIGWINCH, &sigact, NULL); if (!tcgetattr(STDIN_FILENO, &stty)) { saved_stty = stty; stty.c_lflag &= (tcflag_t)~(ECHO | ICANON); tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty); tcset = 1; } if (display_counter()) goto fail; fprintf(stdout, "\033[?1049;1003;1006l\033[?25h\n\033[?1049l"); fflush(stdout); tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); return 0; fail: saved_errno = errno; fprintf(stdout, "\033[?1049;1003;1006l\033[?25h\n\033[?1049l"); fflush(stdout); errno = saved_errno; perror(argv0 ? argv0 : "counter"); if (tcset) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); return 1; }