diff options
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | clock.c | 5 | ||||
| -rw-r--r-- | counter.c | 5 | ||||
| -rwxr-xr-x | large.sh | 25 | ||||
| -rw-r--r-- | stopwatch.c | 10 | ||||
| -rw-r--r-- | timer.c | 362 |
6 files changed, 400 insertions, 10 deletions
@@ -12,7 +12,8 @@ BIN_ =\ clock\ counter\ dice\ - stopwatch + stopwatch\ + timer OBJ =\ common.o\ @@ -246,6 +246,7 @@ int main(int argc, char *argv[]) { int timerfd = -1, tcset = 0, old_flags = -1, old_sig = 0, owner_set = 0; + int saved_errno; struct itimerspec itimerspec; struct sigaction sigact; struct termios stty, saved_stty; @@ -338,9 +339,11 @@ main(int argc, char *argv[]) return 0; fail: - perror(argv0 ? argv0 : "clock"); + saved_errno = errno; fprintf(stdout, "\033[?25h\n\033[?1049l"); fflush(stdout); + errno = saved_errno; + perror(argv0 ? argv0 : "clock"); if (owner_set) fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner); if (old_flags != -1) @@ -174,6 +174,7 @@ int main(int argc, char *argv[]) { int tcset = 0, old_flags = -1, old_sig = 0, owner_set = 0; + int saved_errno; struct sigaction sigact; struct termios stty, saved_stty; struct f_owner_ex old_owner, new_owner; @@ -241,9 +242,11 @@ main(int argc, char *argv[]) return 0; fail: - perror(argv0 ? argv0 : "counter"); + saved_errno = errno; fprintf(stdout, "\033[?25h\n\033[?1049l"); fflush(stdout); + errno = saved_errno; + perror(argv0 ? argv0 : "counter"); if (owner_set) fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner); if (old_flags != -1) @@ -121,6 +121,17 @@ large_colon() { ' ' } +large_dot() { + printf '%s\n' \ + ' ' \ + ' ' \ + ' ' \ + ' ' \ + ' ' \ + ' ' \ + ' [] ' +} + large_minus() { printf '%s\n' \ ' ' \ @@ -133,24 +144,28 @@ large_minus() { } printf '#define LARGE_Y %i\n' $(large_0 | wc -l) -printf '#define LARGE_X %i\n' $(large_0 | head -n 1 | tr -d '\n' | wc -c) +printf '#define LARGE_X %i\n' $(large_0 | head -n 1 | tr -d '\n' | wc -c) printf '#define LARGE_XC %i\n' $(large_colon | head -n 1 | tr -d '\n' | wc -c) +printf '#define LARGE_XD %i\n' $(large_dot | head -n 1 | tr -d '\n' | wc -c) printf '#define LARGE_XM %i\n' $(large_minus | head -n 1 | tr -d '\n' | wc -c) printf '#define SMALL_Y %i\n' $(expr \( $(large_0 | wc -l) + 1 \) / 2) -printf '#define SMALL_X %i\n' $(expr \( $(large_0 | head -n 1 | tr -d '\n' | wc -c) + 1 \) / 2) +printf '#define SMALL_X %i\n' $(expr \( $(large_0 | head -n 1 | tr -d '\n' | wc -c) + 1 \) / 2) printf '#define SMALL_XC %i\n' $(expr \( $(large_colon | head -n 1 | tr -d '\n' | wc -c) + 1 \) / 2) +printf '#define SMALL_XD %i\n' $(expr \( $(large_dot | head -n 1 | tr -d '\n' | wc -c) + 1 \) / 2) printf '#define SMALL_XM %i\n' $(expr \( $(large_minus | head -n 1 | tr -d '\n' | wc -c) + 1 \) / 2) -for c in 0 1 2 3 4 5 6 7 8 9 colon minus; do +for c in 0 1 2 3 4 5 6 7 8 9 colon dot minus; do if test $c = colon; then printf '%s\n' '#ifdef INCLUDE_LARGE_COLON' + elif test $c = dot; then + printf '%s\n' '#ifdef INCLUDE_LARGE_DOT' elif test $c = minus; then printf '%s\n' '#ifdef INCLUDE_LARGE_MINUS' fi printf 'static const char *large_%s[] = {\n' $c large_$c | \ - sed -e 's/X/ /g' -e 's/\[/\\033\[7m /g' -e 's/\]/ \\033\[m/g' | \ + sed -e 's/X/ /g' -e 's/\[/\\033\[7m /g' -e 's/\]/ \\033\[27m/g' | \ sed -e 's/^/"/' -e 's/$/",/' | \ sed '$s/,$//' printf '};\n' @@ -209,7 +224,7 @@ for c in 0 1 2 3 4 5 6 7 8 9 colon minus; do printf '\n' done | sed -e 's/^/"/' -e 's/$/",/' | sed '$s/,$//' printf '};\n' - if test $c = colon || test $c = minus; then + if test $c = colon || test $c = dot || test $c = minus; then printf '%s\n' '#endif' fi done diff --git a/stopwatch.c b/stopwatch.c index 5bfa69c..dcb36dd 100644 --- a/stopwatch.c +++ b/stopwatch.c @@ -142,6 +142,7 @@ int main(int argc, char *argv[]) { int timerfd = -1, old_flags = -1, tcset = 0, old_sig = 0; + int saved_errno; struct sigaction sigact; int64_t owner_set = 0; struct f_owner_ex old_owner, new_owner; @@ -218,9 +219,14 @@ main(int argc, char *argv[]) return 0; fail: - printf("\033[?25h\n"); - perror(argv0 ? argv0 : "stopwatch"); + saved_errno = errno; + if (quadsize) + printf("\n\n\033#5\033[?25h"); + else + printf("\033[?25h\n"); fflush(stdout); + errno = saved_errno; + perror(argv0 ? argv0 : "stopwatch"); if (owner_set) fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner); if (old_flags != -1) @@ -0,0 +1,362 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +/* TODO add support for in application entering of time */ +/* TODO add support for rat input */ +/* TODO add support for audible alert */ +/* TODO add support for vibration alert */ +/* TODO add support for flash/led/screen alert */ +/* TODO add support for messages */ +/* TODO add support for multiple timers */ +/* TODO add support for CLOCK_BOOTTIME_ALARM */ +/* TODO add support for ptuting timer in the background */ + +USAGE("[-2s] [[hours:]minutes:]second[.deciseconds]"); + + +#define INCLUDE_LARGE_COLON +#define INCLUDE_LARGE_DOT +#define INCLUDE_LARGE_MINUS +#include "large.h" + + +static volatile sig_atomic_t caught_sigterm = 0; +static volatile sig_atomic_t caught_sigwinch = 1; +static volatile sig_atomic_t caught_sigio = 1; +static struct itimerspec orig_time; +static int64_t initial = 0; +static int with_small = 0; +static int with_quad = 0; + +static const char **large_digits[] = { + large_0, large_1, large_2, large_3, large_4, + large_5, large_6, large_7, large_8, large_9 +}; + +static const char **small_digits[] = { + small_0, small_1, small_2, small_3, small_4, + small_5, small_6, small_7, small_8, small_9 +}; + + + +static void +sigterm(int signo) +{ + caught_sigterm = 1; + (void) signo; +} + +static void +sigwinch(int signo) +{ + caught_sigwinch = 1; + (void) signo; +} + +static void +sigio(int signo) +{ + caught_sigio = 1; + (void) signo; +} + + +static void +print_time(const char ***str, int left, size_t height) +{ + size_t r, c; + + for (r = 0;;) { + printf("%*.s", left, ""); + for (c = 0; str[c]; c++) + fprintf(stdout, "%s", str[c][r]); + if (++r == height) { + fprintf(stdout, "\033[K"); + break; + } else { + fprintf(stdout, "\033[K\n"); + } + } +} + + +static int +display_timer(int timerfd) +{ + const char **digits[sizeof(int64_t) * 3 + sizeof("-:00:00.0")]; + char buffer[sizeof(int64_t) * 3 + sizeof("-:00:00.0")]; + int r, x = 0, y = 0, was_quad = 0, paused = 0, large_width, small_width, len, neg; + int64_t overrun, now = initial, h, m, s, ds; + struct itimerspec zero_time, old_time; + struct winsize winsize; + char c; + ssize_t rd; + size_t i; + + memset(&zero_time, 0, sizeof(zero_time)); + + while (!caught_sigterm) { + if (caught_sigwinch) { + if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { + if (errno == EINTR) + continue; + goto fail; + } + caught_sigwinch = 0; + y = winsize.ws_row; + x = winsize.ws_col; + if (was_quad) { + was_quad = 0; + for (r = 0; r < y; r++) + printf("%s\033#5", r ? "\n" : "\033[H"); + } + } + + if (caught_sigio) { + caught_sigio = 0; + for (;;) { + rd = read(STDIN_FILENO, &c, 1); + if (rd <= 0) { + if (!rd) + goto out; + if (errno == EAGAIN) + break; + if (errno == EINTR) + continue; + goto fail; + } + if (c == 'q') { + goto out; + } else if (c == 'r') { + now = initial; + if (paused) + memcpy(&old_time, &orig_time, sizeof(orig_time)); + else if (timerfd_settime(timerfd, 0, &orig_time, NULL)) + goto fail; + } else if (c == ' ' || c == 'p') { + 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; + } + } + } + } + + neg = now < 0; + ds = neg ? -now : now; + s = ds / 10; + ds %= 10; + m = s / 60; + s %= 60; + h = m / 60; + m %= 60; + + if (h) + len = sprintf(buffer, "%s%02ji:%02ji:%02ji.%ji", neg ? "-" : "", h, m, s, ds); + else + len = sprintf(buffer, "%s%02ji:%02ji.%ji", neg ? "-" : "", m, s, ds); + + large_width = neg * LARGE_XM + (1 + (h > 0)) * LARGE_XC + LARGE_XD + (len - neg - 2 - (h > 0)) * LARGE_X; + small_width = neg * SMALL_XM + (1 + (h > 0)) * SMALL_XC + SMALL_XD + (len - neg - 2 - (h > 0)) * SMALL_X; + + printf("\033[H"); + if (paused) + printf("\033[33m"); + else if (neg) + printf("\033[31m"); + + if (x >= large_width && y >= LARGE_Y + paused) { + for (i = 0; buffer[i]; i++) { + if (buffer[i] == '-') + digits[i] = large_minus; + else if (buffer[i] == ':') + digits[i] = large_colon; + else if (buffer[i] == '.') + digits[i] = large_dot; + else + digits[i] = large_digits[buffer[i] & 15]; + } + digits[i] = NULL; + print_time(digits, (x - large_width) / 2, LARGE_Y); + } else if (with_small && x >= small_width && y >= SMALL_Y + paused) { + for (i = 0; buffer[i]; i++) { + if (buffer[i] == '-') + digits[i] = small_minus; + else if (buffer[i] == ':') + digits[i] = small_colon; + else if (buffer[i] == '.') + digits[i] = small_dot; + else + digits[i] = small_digits[buffer[i] & 15]; + } + digits[i] = NULL; + print_time(digits, (x - small_width) / 2, SMALL_Y); + } else if (with_quad && x > 8 * 2 && y > 2) { + printf("\033#3%*.s%s\033[K\n", (x / 2 - len) / 2, "", buffer); + printf("\033#4%*.s%s\033[K\n", (x / 2 - len) / 2, "", buffer); + printf("\033#5"); + if (paused) + printf("%*.s(paused)\033[K", (x - 8) / 2, ""); + was_quad = 1; + goto paused_printed; + } else { + printf("%*.s%s\033[K", (x - len) / 2, "", buffer); + } + if (paused) + printf("\n%*.s(paused)\033[K", (x - 8) / 2, ""); + paused_printed: + + if (paused || neg) + printf("\033[m"); + printf("\033[J"); + fflush(stdout); + + if (read(timerfd, &overrun, sizeof(overrun)) < 0) { + if (errno != EINTR) + goto fail; + } else { + now -= overrun; + } + } + +out: + return 0; + +fail: + return -1; +} + + +int +main(int argc, char *argv[]) +{ + int timerfd = -1, tcset = 0, old_flags = -1, old_sig = 0, owner_set = 0; + int saved_errno, colons = 0, dots = 0; + int64_t part = 0; + struct sigaction sigact; + struct termios stty, saved_stty; + struct f_owner_ex old_owner, new_owner; + char *p; + + ARGBEGIN { + case '2': + with_quad = 1; + break; + case 's': + with_small = 1; + break; + default: + usage(); + } ARGEND; + + if (argc != 1) + usage(); + + if (!isdigit(argv[0][0])) + usage(); + for (p = argv[0]; *p; p++) { + if (isdigit(*p)) { + part *= 10; + part += (int64_t)(*p & 15); + } else if (*p == ':') { + if (dots || ++colons > 2 || !isdigit(p[1])) + usage(); + initial += part; + initial *= 60; + part = 0; + } else if (*p == '.') { + if (dots++ || !isdigit(p[1]) || p[2]) + usage(); + initial += part; + initial *= 10; + part = 0; + } else { + usage(); + } + } + initial += part; + if (!dots) + initial *= 10; + + fprintf(stdout, "\033[?1049h\033[?25l"); + + 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 = sigwinch; + sigaction(SIGWINCH, &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_timer(timerfd)) + goto fail; + + fprintf(stdout, "\033[?25h\n\033[?1049l"); + fflush(stdout); + close(timerfd); + 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); + return 0; + +fail: + saved_errno = errno; + fprintf(stdout, "\033[?25h\n\033[?1049l"); + fflush(stdout); + errno = saved_errno; + perror(argv0 ? argv0 : "timer"); + 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; +} |
