#include #include #include #include #include #include #include #include #include #define NB_EOF -1 #define NB_AGAIN -2 static char buffer[ BUFSIZ ]; static size_t buf_pos = 0; static size_t buf_fill = 0; #define fold inline __attribute__((always_inline)) static fold void set_stdin_nonblocking(void) { static int done = 0; if (!done) { int flags = fcntl(STDIN_FILENO, F_GETFL); if (flags != -1) { fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); } done = 1; } } fold int nb_getchar(void) { set_stdin_nonblocking(); if (buf_pos < buf_fill) { return (unsigned char) buffer[ buf_pos++ ]; } ssize_t n; do { n = read(STDIN_FILENO, buffer, sizeof(buffer)); } while (n == -1 && errno == EINTR); if (n == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return NB_AGAIN; } return NB_EOF; } if (n == 0) { return NB_EOF; } buf_pos = 0; buf_fill = n; return (unsigned char) buffer[ buf_pos++ ]; } fold uint64_t time_ms(void) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return (uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000; } #define clear() printf("\033[2J\033[H") static struct termios original_tio; static int tio_saved = 0; void enter_state(void) { if (!isatty(STDIN_FILENO)) return; if (!tio_saved) { if (tcgetattr(STDIN_FILENO, &original_tio) == -1) { perror("tcgetattr"); exit(1); } tio_saved = 1; } struct termios raw = original_tio; raw.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); raw.c_oflag &= ~OPOST; raw.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); raw.c_cflag &= ~(CSIZE | PARENB); raw.c_cflag |= CS8; raw.c_cc[ VMIN ] = 1; raw.c_cc[ VTIME ] = 0; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { perror("tcsetattr (raw)"); exit(1); } } void exit_state(void) { if (tio_saved && isatty(STDIN_FILENO)) { if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_tio) == -1) { perror("tcsetattr (restore)"); exit(1); } } } void bye() { printf("\r\n\ngoodbye.\r\n"); exit(0); } fold void print_dur(uint64_t duration) { uint64_t hours = 0, minutes = 0, seconds = 0, ms = 0; ms = duration % 1000; duration /= 1000; seconds = duration % 60; duration /= 60; minutes = duration % 60; duration /= 60; hours = duration; printf("%2" PRIu64 "h ", hours); printf("%2" PRIu64 "m ", minutes); printf("%2" PRIu64 "s ", seconds); printf("%4" PRIu64 "ms", ms); fflush(stdout); } fold void yield(void) { struct timespec ts = { .tv_sec = 0, .tv_nsec = 999000 }; nanosleep(&ts, NULL); } #define unlikely(a) __builtin_expect(!!(a), 0) int main() { enter_state(); atexit(exit_state); printf( "\r\nstopwatch\r\n\n" "instructions:\r\n" " space => pause/resume\r\n" " ctrl+c / ctrl+d / eof => exit\r\n" " press space to start.\r\n" "\r\n"); int c; while ((c = nb_getchar()) != ' ') { if (unlikely(c == 3 || c == 4 || c == NB_EOF)) bye(); yield(); } uint64_t tally = 0; uint64_t start; while (1) { uint64_t cur; start = time_ms(); char parity = 1; while ((c = nb_getchar()) != ' ') { // clear(); cur = time_ms(); if (parity) { printf("\r"); printf("[ ] "); print_dur(cur - start + tally); } parity = !parity; if (unlikely(c == 3 || c == 4 || c == NB_EOF)) bye(); yield(); } tally += time_ms() - start; printf("\r"); printf("[x] "); print_dur(tally); while ((c = nb_getchar()) != ' ') { if (unlikely(c == 3 || c == 4 || c == NB_EOF)) bye(); yield(); } } bye(); }