/* See LICENSE file for copyright and license details. */ #include "common.h" USAGE(""); static volatile sig_atomic_t caught_sigterm = 0; static volatile sig_atomic_t caught_sigwinch = 1; static struct termios saved_stty; static void sigterm(int signo) { caught_sigterm = 1; (void) signo; } static void sigwinch(int signo) { caught_sigwinch = 1; (void) signo; } static void restore_terminal(void) { printf("\033[?25h\n\033[?1049;1003;1006l"); fflush(stdout); tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); } int main(int argc, char *argv[]) { union libterminput_input input; struct libterminput_state termctx; struct termios stty; struct sigaction sigact; struct winsize winsize = {.ws_col = 80, .ws_row = 24}; int r, dualplayer = 0, redraw = 1, left = (winsize.ws_col - ((int)sizeof("> Single player") - 1)) / 2; const char *highlight; int confline = 0, line = 0, player = 0, level = 0; uintmax_t wins, loses; ARGBEGIN { default: usage(); break; } ARGEND; if (argc) usage(); memset(&termctx, 0, sizeof(termctx)); 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)) eprintf("tcgetattr :"); saved_stty = stty; stty.c_lflag &= (tcflag_t)~(ECHO | ICANON); if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty)) eprintf("tcsetattr TCSAFLUSH:"); printf("\033[?1049;1003;1006h\033[?25l"); libsimple_eprintf_preprint = restore_terminal; restart: for (;;) { if (caught_sigterm) goto out; if (caught_sigwinch) { if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { if (errno == EINTR) continue; eprintf("ioctl TIOCGWINSZ:"); } caught_sigwinch = 0; left = (winsize.ws_col - ((int)sizeof("> Single player") - 1)) / 2; redraw = 1; } if (redraw) { printf("\033[H\033[m"); for (r = (winsize.ws_row - 2) / 2; r > 0; r--) printf("\033[K\n"); printf("%*.s%s %s\033[m\033[K\n", left, "", dualplayer == 0 ? "\033[1;31m>" : " ", "Single player"); printf("%*.s%s %s\033[m\033[J", left, "", dualplayer == 1 ? "\033[1;31m>" : " ", "Dual player"); fflush(stdout); redraw = 0; } r = libterminput_read(STDIN_FILENO, &input, &termctx); if (r <= 0) { if (r < 0) { if (errno == EINTR) continue; libsimple_eprintf("libterminput_read :"); } break; } if (input.type == LIBTERMINPUT_KEYPRESS) { if (input.keypress.key == LIBTERMINPUT_UP && dualplayer > 0) { dualplayer -= 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_DOWN && dualplayer < 1) { dualplayer += 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_ENTER) { break; } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { if (!strcmp(input.keypress.symbol, " ")) { break; } else if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) { goto out; } 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 && input.mouseevent.x > (left < 0 ? 0 : (size_t)left) && input.mouseevent.x - (left < 0 ? 0 : (size_t)left) <= (int)sizeof("> Single player") - 1) { input.mouseevent.y -= (size_t)((winsize.ws_row < 2 ? 2 : winsize.ws_row) - 2) / 2; switch (input.mouseevent.y) { case 1: dualplayer = 0; goto new_game; case 2: dualplayer = 1; goto new_game; default: break; } } } new_game: redraw = 1; player = 0; highlight = "\033[1;31m"; if (!dualplayer) { left = (winsize.ws_col - ((int)sizeof("> [PLAY] Select mode randomly") - 1)) / 2; for (;;) { if (caught_sigterm) goto out; if (caught_sigwinch) { if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { if (errno == EINTR) continue; eprintf("ioctl TIOCGWINSZ:"); } caught_sigwinch = 0; left = (winsize.ws_col - ((int)sizeof("> [PLAY] Select mode randomly") - 1)) / 2; redraw = 1; } if (redraw) { printf("\033[H\033[m"); for (r = (winsize.ws_row - 5) / 2; r > 0; r--) printf("\033[K\n"); printf("%*.s%s Pattern depth: %i\033[m\033[K\n", left, "", confline == 0 ? "\033[1;31m>" : " ", level); printf("%*.s%s [PLAY] Select mode randomly\033[m\033[K\n", left, "", confline == 1 ? "\033[1;31m>" : " "); printf("%*.s%s [PLAY] Offensive mode\033[m\033[K\n", left, "", confline == 2 ? "\033[1;31m>" : " "); printf("%*.s%s [PLAY] Defensive mode\033[m\033[K\n", left, "", confline == 3 ? "\033[1;31m>" : " "); printf("%*.s%s [back]\033[m\033[J", left, "", confline == 4 ? "\033[1;31m>" : " "); fflush(stdout); redraw = 0; } r = libterminput_read(STDIN_FILENO, &input, &termctx); if (r <= 0) { if (r < 0) { if (errno == EINTR) continue; libsimple_eprintf("libterminput_read :"); } break; } if (input.type == LIBTERMINPUT_KEYPRESS) { if (input.keypress.key == LIBTERMINPUT_UP && confline > 0) { confline -= 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_DOWN && confline < 4) { confline += 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_LEFT && confline == 0 && level > 0) { level -= 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_RIGHT && confline == 0 && level < 101) { level += 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_ENTER) { trigger_conf: if (confline == 1) { /* TODO select mode randomly */ break; } else if (confline == 2) { /* TODO offensive mode */ break; } else if (confline == 3) { /* TODO defensive mode */ break; } else if (confline == 4) { redraw = 1; goto restart; } } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { if (!strcmp(input.keypress.symbol, " ")) { goto trigger_conf; } else if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) { goto out; } 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.x > (left < 0 ? 0 : (size_t)left)) { input.mouseevent.x -= (left < 0 ? 0 : (size_t)left); input.mouseevent.y -= (size_t)((winsize.ws_row < 5 ? 5 : winsize.ws_row) - 5) / 2; if (input.mouseevent.y > 0 && input.mouseevent.y <= 5 && input.mouseevent.x <= (int)sizeof("> [X] CPU's first move is random") - 1) { if (input.mouseevent.button == LIBTERMINPUT_BUTTON1 || ((int)input.mouseevent.y - 1 == 2 && (input.mouseevent.button == LIBTERMINPUT_BUTTON3 || input.mouseevent.button == LIBTERMINPUT_XBUTTON1|| input.mouseevent.button == LIBTERMINPUT_XBUTTON2))) { confline = (int)input.mouseevent.y - 1; if (confline != 0) goto trigger_conf; if (input.mouseevent.button == LIBTERMINPUT_BUTTON1 || input.mouseevent.button == LIBTERMINPUT_XBUTTON2) { level += 1; level %= 101; } else if (level) { level -= 1; } else { level = 100; } redraw = 1; } } } } redraw = 1; } left = (winsize.ws_col - ((int)sizeof("> Scissors") - 1)) / 2; wins = 0; loses = 0; for (;;) { if (caught_sigterm) goto out; if (caught_sigwinch) { if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { if (errno == EINTR) continue; eprintf("ioctl TIOCGWINSZ:"); } caught_sigwinch = 0; left = (winsize.ws_col - ((int)sizeof("> Scissors") - 1)) / 2; redraw = 1; } if (redraw) { printf("\033[H\033[m"); for (r = (winsize.ws_row - 7) / 2; r > 0; r--) printf("\033[K\n"); if (!dualplayer) { printf("%*.s\033[1;31m%ju (wins)\033[0m : \033[1;34m%ju (loses)\033[0m\033[K\n", (winsize.ws_row - 3) / 2 - snprintf(NULL, 0, "%ju (wins)", wins), "", wins, loses); } else { printf("%*.s\033[1;31m%ju (P1)\033[0m : \033[1;34m%ju (P2)\033[0m\033[K\n", (winsize.ws_row - 3) / 2 - snprintf(NULL, 0, "%ju (P1)", wins), "", wins, loses); } printf("%*.s%s%s%s\033[K\n\n", left, "", line == 0 ? highlight : "", "Rock", line == 0 ? "\033[0m" : ""); printf("%*.s%s%s%s\033[K\n\n", left, "", line == 1 ? highlight : "", "Paper", line == 1 ? "\033[0m" : ""); printf("%*.s%s%s%s\033[J", left, "", line == 2 ? highlight : "", "Scissors", line == 2 ? "\033[0m" : ""); fflush(stdout); redraw = 0; } r = libterminput_read(STDIN_FILENO, &input, &termctx); if (r <= 0) { if (r < 0) { if (errno == EINTR) continue; libsimple_eprintf("libterminput_read :"); } break; } if (input.type == LIBTERMINPUT_KEYPRESS) { if (input.keypress.key == LIBTERMINPUT_UP && line > 0) { line -= 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_DOWN && line < 2) { line += 1; redraw = 1; } else if (input.keypress.key == LIBTERMINPUT_ENTER) { select: ;/* TODO */ } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) { if (!strcmp(input.keypress.symbol, " ")) { goto select; } else if (!strcmp(input.keypress.symbol, "q") || !strcmp(input.keypress.symbol, "Q")) { goto out; } else if (!strcmp(input.keypress.symbol, "L") && (input.keypress.mods & LIBTERMINPUT_CTRL)) { redraw = 1; } else if (!strcmp(input.keypress.symbol, "r") || !strcmp(input.keypress.symbol, "R") || !strcmp(input.keypress.symbol, "1")) { line = 0; goto select; } else if (!strcmp(input.keypress.symbol, "p") || !strcmp(input.keypress.symbol, "P") || !strcmp(input.keypress.symbol, "2")) { line = 1; goto select; } else if (!strcmp(input.keypress.symbol, "s") || !strcmp(input.keypress.symbol, "S") || !strcmp(input.keypress.symbol, "3")) { line = 2; goto select; } } } else if (input.type == LIBTERMINPUT_MOUSEEVENT && input.mouseevent.event == LIBTERMINPUT_PRESS && input.mouseevent.button == LIBTERMINPUT_BUTTON1) { /* TODO */ } } out: restore_terminal(); return 0; }