/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include "arg.h" #define DX 16 #define DY 12 #define DC 16 #define DM 16 #include "mongo_0.h" #include "mongo_1.h" #include "mongo_2.h" #include "mongo_3.h" #include "mongo_4.h" #include "mongo_5.h" #include "mongo_6.h" #include "mongo_7.h" #include "mongo_8.h" #include "mongo_9.h" #include "mongo_c.h" #include "mongo_m.h" static const char **mongo_ds[] = { mongo_0, mongo_1, mongo_2, mongo_3, mongo_4, mongo_5, mongo_6, mongo_7, mongo_8, mongo_9 }; static volatile sig_atomic_t caught_sigterm = 0; static volatile sig_atomic_t caught_sigwinch = 1; char *argv0; static void usage(void) { fprintf(stderr, "usage: %s [[-e] [[hh]:mm:]ss]\n", argv0); exit(1); } static void sigterm(int signo) { caught_sigterm = 1; (void) signo; } static void sigwinch(int signo) { caught_sigwinch = 1; (void) signo; } static void print_time(const char ***str, size_t y, size_t x) { size_t r, c; fprintf(stdout, "\033[%zu;1H\033[1J", y + 1); for (r = 0; r < DY; r++) { fprintf(stdout, "\033[%zu;%zuH\033[1K", y + r + 1, x + 1); for (c = 0; str[c]; c++) fprintf(stdout, "%s", str[c][r]); fprintf(stdout, "\033[0K"); } fprintf(stdout, "\033[0J"); fflush(stdout); } static int display_stopwatch(int timerfd) { uint64_t overrun, total_overrun = 0; struct winsize winsize; size_t x = 0, y = 0, width, i; const char **digits[23]; uint64_t h, m, s, h_div = 1, th; size_t h_digits = 0; int small = 0; /* TODO pause (yellow text) on */ while (!caught_sigterm) { if (caught_sigwinch) { if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { if (errno == EINTR) continue; goto fail; } caught_sigwinch = 0; recenter: y = winsize.ws_row; x = winsize.ws_col; width = 4 * DX + DC; if (h_digits) { width += DC; width += h_digits * DX; } if (y < DY) small = 1; else if (x < width) small = 1; else small = 0; y -= DY; x -= width; y /= 2; x /= 2; } if (small) { fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small"); fflush(stdout); pause(); continue; } s = total_overrun % 60; m = total_overrun / 60 % 60; h = total_overrun / 60 / 60; if (h / h_div) { h_div *= 10; h_digits++; goto recenter; } i = 0; if (h_digits) { for (th = h; th; th /= 10) digits[h_digits - ++i] = mongo_ds[th % 10]; digits[i++] = mongo_c; } digits[i++] = mongo_ds[m / 10]; digits[i++] = mongo_ds[m % 10]; digits[i++] = mongo_c; digits[i++] = mongo_ds[s / 10]; digits[i++] = mongo_ds[s % 10]; digits[i++] = NULL; print_time(digits, y, x); if (read(timerfd, &overrun, sizeof(overrun)) < 0) { if (errno != EINTR) goto fail; } else { total_overrun += overrun; } } return 0; fail: return -1; } static int display_timer(int timerfd, int64_t time, int exit_on_zero) { uint64_t overrun, abstime; struct winsize winsize; size_t x = 0, y = 0, width; const char **digits[24]; uint64_t h, m, s, th, h_div; size_t h_digits, i; int small = 0; /* TODO pause (yellow text) on */ while (!caught_sigterm) { if (caught_sigwinch) { if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { if (errno == EINTR) continue; goto fail; } caught_sigwinch = 0; } abstime = time < 0 ? (uint64_t)-time : (uint64_t)time; s = abstime % 60; m = abstime / 60 % 60; h = abstime / 60 / 60; y = winsize.ws_row; x = winsize.ws_col; for (h_div = 1, h_digits = 0; h / h_div; h_div *= 10, h_digits += 1); width = 4 * DX + DC; if (time < 0) width += DM; if (h_digits) width += h_digits * DX + DC; if (y < DY) small = 1; else if (x < width) small = 1; else small = 0; y -= DY; x -= width; y /= 2; x /= 2; if (small) { fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small"); fflush(stdout); pause(); continue; } i = 0; if (time < 0) digits[i++] = mongo_m; if (h_digits) { for (th = h; th; th /= 10) digits[h_digits - ++i] = mongo_ds[th % 10]; digits[i++] = mongo_c; } digits[i++] = mongo_ds[m / 10]; digits[i++] = mongo_ds[m % 10]; digits[i++] = mongo_c; digits[i++] = mongo_ds[s / 10]; digits[i++] = mongo_ds[s % 10]; digits[i++] = NULL; if (time >= 0) { print_time(digits, y, x); } else { fprintf(stdout, "\033[31m\n"); print_time(digits, y, x); fprintf(stdout, "\033[39m\n"); } if (read(timerfd, &overrun, sizeof(overrun)) < 0) { if (errno != EINTR) goto fail; } else { time -= (int64_t)overrun; if (time <= 0 && exit_on_zero) break; } } if (caught_sigterm && exit_on_zero) return -1; return 0; fail: return -1; } int main(int argc, char *argv[]) { int timerfd = -1; int exit_on_zero = 0; struct itimerspec itimerspec; struct sigaction sigact; int64_t time = 0, t = 0; size_t colons = 0; char *s; ARGBEGIN { case 'e': exit_on_zero = 1; break; default: usage(); } ARGEND; if (argc > 1 || (exit_on_zero && !argc)) usage(); fprintf(stdout, "\033[?1049h\033[?25l"); itimerspec.it_interval.tv_sec = 1; itimerspec.it_interval.tv_nsec = 0; itimerspec.it_value.tv_sec = 1; itimerspec.it_value.tv_nsec = 0; timerfd = timerfd_create(CLOCK_BOOTTIME, 0); if (timerfd < 0) goto fail; if (timerfd_settime(timerfd, 0, &itimerspec, NULL)) goto fail; 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 (argc) { for (s = argv[0]; *s; s++) { if ('0' <= *s && *s <= '9') { t = t * 10 + (*s & 15); } else if (*s == ':' && colons++ < 2) { if (s == argv[0] || s[-1] == ':') goto fail_usage; time = (time + t) * 60; t = 0; } else { goto fail_usage; } } if (s == argv[0] || s[-1] == ':') goto fail_usage; time += t; if (display_timer(timerfd, time, exit_on_zero)) goto fail; } else { if (display_stopwatch(timerfd)) goto fail; } fprintf(stdout, "\033[?25h\n\033[?1049l"); fflush(stdout); close(timerfd); return 0; fail: perror(argv0 ? argv0 : "mongotimer"); fprintf(stdout, "\033[?25h\n\033[?1049l"); fflush(stdout); if (timerfd >= 0) close(timerfd); return 1; fail_usage: fprintf(stdout, "\033[?25h\n\033[?1049l"); fflush(stdout); usage(); }