aboutsummaryrefslogtreecommitdiff
path: root/stopwatch.c
diff options
context:
space:
mode:
authorGravatar Moon Flower <mforoud86@gmail.com> 2026-02-14 15:45:49 +0330
committerGravatar Moon Flower <mforoud86@gmail.com> 2026-02-14 15:45:49 +0330
commitae2056d2b8ca2f0218ae92825a1529f139e4390d (patch)
tree8c870cb0686838ac5469938c2c91c62520440fa2 /stopwatch.c
first commit.
Diffstat (limited to 'stopwatch.c')
-rw-r--r--stopwatch.c177
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();
+}