diff options
Diffstat (limited to 'stopwatch.c')
| -rw-r--r-- | stopwatch.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/stopwatch.c b/stopwatch.c new file mode 100644 index 0000000..19aba4f --- /dev/null +++ b/stopwatch.c @@ -0,0 +1,230 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +USAGE("[-2]"); + +static volatile sig_atomic_t caught_sigterm = 0; +static volatile sig_atomic_t caught_sigio = 0; +static struct itimerspec orig_time; +static int quadsize = 0; + +static void +sigterm(int signo) +{ + caught_sigterm = 1; + (void) signo; +} + +static void +sigio(int signo) +{ + caught_sigio = 1; + (void) signo; +} + +static int +display_stopwatch(int timerfd) +{ + uint64_t overrun = 0; + uintmax_t total_overrun = 0, h, m, s, d, first_overrun = 0; + int paused = 0, have_first = 0; + struct itimerspec old_time, zero_time; + ssize_t r; + char c; + + memset(&zero_time, 0, sizeof(zero_time)); + + while (!caught_sigterm) { + if (caught_sigio) { + caught_sigio = 0; + for (;;) { + r = read(STDIN_FILENO, &c, 1); + if (r <= 0) { + if (!r) + goto out; + if (errno == EAGAIN) + break; + if (errno == EINTR) + continue; + goto fail; + } + if (c == 'q') { + goto out; + } else if (c == ' ') { + paused ^= 1; + if (paused) { + if (timerfd_settime(timerfd, 0, &zero_time, &old_time)) + goto fail; + } else { + if (timerfd_settime(timerfd, 0, &old_time, NULL)) + goto fail; + } + } else if (c == '\n') { + if (timerfd_settime(timerfd, 0, &orig_time, NULL)) + goto fail; + d = total_overrun % 10; + s = total_overrun / 10 % 60; + m = total_overrun / 10 / 60 % 60; + h = total_overrun / 10 / 60 / 60; + if (quadsize) + printf("\033#5"); + printf("%ju:%02ju:%02ju.%ju", h, m, s, d); + if (!have_first) { + have_first = 1; + first_overrun = total_overrun; + } else if (total_overrun > first_overrun) { + total_overrun = total_overrun - first_overrun; + d = total_overrun % 10; + s = total_overrun / 10 % 60; + m = total_overrun / 10 / 60 % 60; + h = total_overrun / 10 / 60 / 60; + printf(" \033[31m(+%ju:%02ju:%02ju.%ju)\033[m", h, m, s, d); + } else if (total_overrun < first_overrun) { + total_overrun = first_overrun - total_overrun; + d = total_overrun % 10; + s = total_overrun / 10 % 60; + m = total_overrun / 10 / 60 % 60; + h = total_overrun / 10 / 60 / 60; + printf(" \033[34m(-%ju:%02ju:%02ju.%ju)\033[m", h, m, s, d); + } + printf("\033[K\n"); + total_overrun = 0; + } + } + } + + d = total_overrun % 10; + s = total_overrun / 10 % 60; + m = total_overrun / 10 / 60 % 60; + h = total_overrun / 10 / 60 / 60; + + if (!paused) { + if (quadsize) { + printf("\033""7\033#3%ju:%02ju:%02ju.%ju\033[K\n", h, m, s, d); + printf("\033#4%ju:%02ju:%02ju.%ju\033[K\033""8", h, m, s, d); + } else { + printf("\033""7%ju:%02ju:%02ju.%ju\033[K\033""8", h, m, s, d); + } + fflush(stdout); + } else { + if (quadsize) { + printf("\033""7\033#3\033[33m%ju:%02ju:%02ju.%ju (paused)\033[m\033[K\n", h, m, s, d); + printf("\033#4\033[33m%ju:%02ju:%02ju.%ju (paused)\033[m\033[K\033""8", h, m, s, d); + } else { + printf("\033""7\033[33m%ju:%02ju:%02ju.%ju (paused)\033[m\033[K\033""8", h, m, s, d); + } + fflush(stdout); + pause(); + continue; + } + + if (read(timerfd, &overrun, sizeof(overrun)) < 0) { + if (errno != EINTR) + goto fail; + } else { + total_overrun += (uintmax_t)overrun; + } + } + +out: + return 0; + +fail: + return -1; +} + +int +main(int argc, char *argv[]) +{ + int timerfd = -1, old_flags = -1, tcset = 0, old_sig = 0; + struct sigaction sigact; + int64_t owner_set = 0; + struct f_owner_ex old_owner, new_owner; + struct termios stty, saved_stty; + + ARGBEGIN { + case '2': + quadsize = 1; + break; + default: + usage(); + } ARGEND; + + if (argc) + usage(); + + printf("\033[?25l\033[m"); + + orig_time.it_interval.tv_sec = 0; + orig_time.it_interval.tv_nsec = 100000000L; + orig_time.it_value.tv_sec = 0; + orig_time.it_value.tv_nsec = 100000000L; + timerfd = timerfd_create(CLOCK_BOOTTIME, 0); + if (timerfd < 0) + goto fail; + if (timerfd_settime(timerfd, 0, &orig_time, 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 = sigio; + sigaction(SIGIO, &sigact, NULL); + sigaction(SIGURG, &sigact, NULL); + + if (fcntl(STDIN_FILENO, F_GETOWN_EX, &old_owner)) + goto fail; + memset(&new_owner, 0, sizeof(new_owner)); + new_owner.type = F_OWNER_PID; + new_owner.pid = getpid(); + if (fcntl(STDIN_FILENO, F_SETOWN_EX, &new_owner)) + goto fail; + owner_set = 1; + old_flags = fcntl(STDIN_FILENO, F_GETFL); + fcntl(STDIN_FILENO, F_SETFL, old_flags | FASYNC | O_NONBLOCK); + fcntl(STDIN_FILENO, F_GETSIG, &old_sig); + if (old_sig) + fcntl(STDIN_FILENO, F_SETSIG, 0); + + if (!tcgetattr(STDIN_FILENO, &stty)) { + saved_stty = stty; + stty.c_lflag &= (tcflag_t)~(ECHO | ICANON); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty); + tcset = 1; + } + + if (display_stopwatch(timerfd)) + goto fail; + + if (quadsize) + printf("\n\n\033#5\033[?25h"); + else + printf("\033[?25h\n"); + fflush(stdout); + fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner); + fcntl(STDIN_FILENO, F_SETFL, old_flags); + fcntl(STDIN_FILENO, F_SETSIG, old_sig); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); + close(timerfd); + return 0; + +fail: + printf("\033[?25h\n"); + perror(argv0 ? argv0 : "stopwatch"); + fflush(stdout); + if (owner_set) + fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner); + if (old_flags != -1) + fcntl(STDIN_FILENO, F_SETFL, old_flags); + if (old_sig) + fcntl(STDIN_FILENO, F_SETSIG, old_sig); + if (tcset) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty); + if (timerfd >= 0) + close(timerfd); + return 1; +} |
