diff options
| author | 2026-02-14 15:45:49 +0330 | |
|---|---|---|
| committer | 2026-02-14 15:45:49 +0330 | |
| commit | ae2056d2b8ca2f0218ae92825a1529f139e4390d (patch) | |
| tree | 8c870cb0686838ac5469938c2c91c62520440fa2 | |
first commit.
| -rw-r--r-- | stopwatch.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/stopwatch.c b/stopwatch.c new file mode 100644 index 0000000..bb7a90e --- /dev/null +++ b/stopwatch.c @@ -0,0 +1,177 @@ +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> +#include <time.h> +#include <unistd.h> + +#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(); +} |
