diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 9 | ||||
| -rw-r--r-- | clock.c | 354 | ||||
| -rwxr-xr-x | large.sh | 194 |
4 files changed, 556 insertions, 2 deletions
@@ -3,3 +3,4 @@ *.o *.su *.bin +/large.h @@ -4,10 +4,12 @@ CONFIGFILE = config.mk include $(CONFIGFILE) HDR =\ - common.h + common.h\ + large.h BIN_ =\ backlight\ + clock\ stopwatch OBJ =\ @@ -28,6 +30,9 @@ $(BIN): $(@:.bin=.o) common.o .o.bin: $(CC) -o $@ $< common.o $(LDFLAGS) +large.h: large.sh + ./large.sh > $@ + install: $(BIN) mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1" @@ -46,7 +51,7 @@ uninstall: -cd -- "$(DESTDIR)$(MANPREFIX)/man1" && rm -f $(MAN1) clean: - -rm -rf -- *.o *.su *.bin + -rm -rf -- *.o *.su *.bin large.h .SUFFIXES: .SUFFIXES: .bin .o .c @@ -0,0 +1,354 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#include <sys/timex.h> + +USAGE("[-u | -v | -w] [-s2]"); + + +#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 int with_small = 0; +static int with_quad = 0; +static const char *week_fmt = "%V"; + +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; r < height; r++) { + printf("%*.s", left, ""); + for (c = 0; str[c]; c++) + fprintf(stdout, "%s", str[c][r]); + fprintf(stdout, "\033[K\n"); + } +} + + +static int +display_clock(int timerfd) +{ + const char *months = "JanFebMarAprMajJunJulAugSepOctNovDec"; + const char *weekdays = "SunMonTueWedThuFriSat"; + const char **digits[9]; + int r, leap_insert, x = 0, y = 0, was_quad = 0, len1, len2, len3, width; + uint64_t _overrun; + struct winsize winsize; + struct tm *now; + struct timex timex; + char line1[sizeof(int) * 3 + sizeof("+-(00)aaa-00")]; + char line2[sizeof("aaa, week , ") + sizeof(int) * 3 + 32]; + char line3[sizeof("@(+1)") + sizeof(uintmax_t) * 3]; + char week[sizeof(int) * 3], c; + ssize_t rd; + + memset(&timex, 0, sizeof(timex)); + digits[8] = NULL; + + 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; + } + } + + leap_insert = 0; + r = adjtimex(&timex); + if (r == -1) + goto fail; + if (timex.time.tv_sec % (24 * 60 * 60) == 0) { + if (r == TIME_INS) { + timex.time.tv_sec -= 1; + now = localtime(&timex.time.tv_sec); + if (!now) + goto fail; + now->tm_sec += leap_insert = 1; + } else { + timex.time.tv_sec += (r == TIME_DEL); + now = localtime(&timex.time.tv_sec); + if (!now) + goto fail; + } + } else { + now = localtime(&timex.time.tv_sec); + if (!now) + goto fail; + if (r == TIME_OOP) + now->tm_sec += leap_insert = 1; + } + + strftime(week, sizeof(week), week_fmt, now); + len1 = snprintf(line1, sizeof(line1), "%i-(%02i)%.3s-%02i", now->tm_year + 1900, + now->tm_mon + 1, &months[(size_t)now->tm_mon % 12 * 3], now->tm_mday); + len2 = snprintf(line2, sizeof(line2), "%.3s, week %s, %s", + &weekdays[(size_t)now->tm_mday % 7 * 3], week, tzname[now->tm_isdst]); + len3 = snprintf(line3, sizeof(line3), "(@%ji%s)", (intmax_t)timex.time.tv_sec, leap_insert ? "+1" : ""); + + printf("\033[H"); + + if (x >= 6 * LARGE_XD + 2 * LARGE_XC && y > LARGE_Y + 1) { + if (y > LARGE_Y + 6) + printf("\033[K\n"); + digits[0] = large_digits[now->tm_hour / 10]; + digits[1] = large_digits[now->tm_hour % 10]; + digits[2] = large_colon; + digits[3] = large_digits[now->tm_min / 10]; + digits[4] = large_digits[now->tm_min % 10]; + digits[5] = large_colon; + digits[6] = large_digits[now->tm_sec / 10]; + digits[7] = large_digits[now->tm_sec % 10]; + width = 6 * LARGE_XD + 2 * LARGE_XC; + print_time(digits, (x - width) / 2, LARGE_Y); + printf("\033[K\n"); + r = LARGE_Y + 1; + } else if (with_small && x >= 6 * SMALL_XD + 2 * SMALL_XC && y > SMALL_Y + 1) { + if (y > SMALL_Y + 6) + printf("\033[K\n"); + digits[0] = small_digits[now->tm_hour / 10]; + digits[1] = small_digits[now->tm_hour % 10]; + digits[2] = small_colon; + digits[3] = small_digits[now->tm_min / 10]; + digits[4] = small_digits[now->tm_min % 10]; + digits[5] = small_colon; + digits[6] = small_digits[now->tm_sec / 10]; + digits[7] = small_digits[now->tm_sec % 10]; + width = 6 * SMALL_XD + 2 * SMALL_XC; + print_time(digits, (x - width) / 2, SMALL_Y); + printf("\033[K\n"); + r = SMALL_Y + 1; + } else if (with_quad && x > 8 * 2 && y > 2) { + if (y > 6) + printf("\033[K\n"); + printf("\033#3%*.s%02i:%02i:%02i\033[K\n", (x / 2 - 8) / 2, "", now->tm_hour, now->tm_min, now->tm_sec); + printf("\033#4%*.s%02i:%02i:%02i\033[K\n", (x / 2 - 8) / 2, "", now->tm_hour, now->tm_min, now->tm_sec); + printf("\033#5"); + was_quad = 1; + width = 8 * 2; + r = 2; + } else { + printf("%*.s%02i:%02i:%02i\033[K", (x - 8) / 2, "", now->tm_hour, now->tm_min, now->tm_sec); + if (y > 1) + printf("\n"); + width = 8; + r = 1; + } + + if (len1 + len2 + 2 < width && len3 < width && r + 1 < y) { + printf("%*.s%s, %s\033[K\n", (x - len1 - len2 - 2) / 2, "", line1, line2); + printf("%*.s%s\033[K", (x - len3) / 2, "", line3); + } else if (len1 + len2 + len3 + 3 < width && r < y) { + printf("%*.s%s, %s %s\033[K", (x - len1 - len2 - len3 - 3) / 2, "", line1, line2, line3); + } else if (len1 + len2 + 2 < width && len3 < width && r + 1 == y) { + printf("%*.s%s, %s\033[K", (x - len1 - len2 - 2) / 2, "", line1, line2); + } else { + if (len1 < x && r < y) { + printf("%*.s%s\033[K", (x - len1) / 2, "", line1); + r += 1; + if (r < y) + printf("\n"); + } + if (len2 < x && r < y) { + printf("%*.s%s\033[K", (x - len2) / 2, "", line2); + r += 1; + if (r < y) + printf("\n"); + } + if (len3 < x && r < y) { + printf("%*.s%s\033[K", (x - len3) / 2, "", line3); + r += 1; + } + } + + printf("\033[J"); + fflush(stdout); + + if (read(timerfd, &_overrun, sizeof(_overrun)) < 0) + if (errno != EINTR) + goto fail; + } + +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; + struct itimerspec itimerspec; + struct sigaction sigact; + struct termios stty, saved_stty; + struct f_owner_ex old_owner, new_owner; + + ARGBEGIN { + case '2': + with_quad = 1; + break; + case 's': + with_small = 1; + break; + case 'u': + week_fmt = "%U"; + break; + case 'v': + week_fmt = "%V"; + break; + case 'w': + week_fmt = "%W"; + break; + default: + usage(); + } ARGEND; + + if (argc) + usage(); + + tzset(); + + fprintf(stdout, "\033[?1049h\033[?25l"); + + if (clock_gettime(CLOCK_REALTIME, &itimerspec.it_value)) + goto fail; + itimerspec.it_interval.tv_sec = 1; + itimerspec.it_interval.tv_nsec = 0; + itimerspec.it_value.tv_sec += 1; + itimerspec.it_value.tv_nsec = 0; + timerfd = timerfd_create(CLOCK_REALTIME, 0); + if (timerfd < 0) + goto fail; + if (timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &itimerspec, 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_clock(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: + perror(argv0 ? argv0 : "clock"); + fprintf(stdout, "\033[?25h\n\033[?1049l"); + 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; +} diff --git a/large.sh b/large.sh new file mode 100755 index 0000000..248f0d8 --- /dev/null +++ b/large.sh @@ -0,0 +1,194 @@ +#!/bin/sh + +large_0() { + printf '%s\n' \ + ' [XX] ' \ + '[] [] ' \ + '[] [] ' \ + '[] [] ' \ + '[] [] ' \ + '[] [] ' \ + ' [XX] ' +} + +large_1() { + printf '%s\n' \ + ' [XX] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' \ + ' [XXXX] ' +} + +large_2() { + printf '%s\n' \ + ' [XX] ' \ + '[] [] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' \ + '[] ' \ + '[XXXXXX] ' +} + +large_3() { + printf '%s\n' \ + '[XXXX] ' \ + ' [] ' \ + ' [] ' \ + ' [XXX] ' \ + ' [] ' \ + ' [] ' \ + '[XXXX] ' +} + +large_4() { + printf '%s\n' \ + '[] [] ' \ + '[] [] ' \ + '[] [] ' \ + '[XXXXXX] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' +} + +large_5() { + printf '%s\n' \ + '[XXXXX] ' \ + '[] ' \ + '[] ' \ + '[XXXX] ' \ + ' [] ' \ + ' [] ' \ + '[XXXX] ' +} + +large_6() { + printf '%s\n' \ + ' [XXX] ' \ + '[] ' \ + '[] ' \ + '[XXXX] ' \ + '[] [] ' \ + '[] [] ' \ + ' [XX] ' +} + +large_7() { + printf '%s\n' \ + '[XXXXXX] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' \ + ' [] ' +} + +large_8() { + printf '%s\n' \ + ' [XX] ' \ + '[] [] ' \ + '[] [] ' \ + ' [XX] ' \ + '[] [] ' \ + '[] [] ' \ + ' [XX] ' +} + +large_9() { + printf '%s\n' \ + ' [XX] ' \ + '[] [] ' \ + '[] [] ' \ + ' [XXXX] ' \ + ' [] ' \ + ' [] ' \ + ' [XXX] ' +} + +large_colon() { + printf '%s\n' \ + ' ' \ + ' ' \ + ' [] ' \ + ' ' \ + ' ' \ + ' [] ' \ + ' ' +} + +printf '#define LARGE_Y %i\n' $(large_0 | wc -l) +printf '#define LARGE_XD %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 SMALL_Y %i\n' $(expr \( $(large_0 | wc -l) + 1 \) / 2) +printf '#define SMALL_XD %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) + +for c in 0 1 2 3 4 5 6 7 8 9 colon; do + 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/^/"/' -e 's/$/",/' | \ + sed '$s/,$//' + printf '};\n' + + printf 'static const char *small_%s[] = {\n' $c + ( + if expr $(large_$c | wc -l) % 2 >/dev/null; then + printf '%*.s\n' $(expr \( $(large_0 | head -n 1 | tr -d '\n' | wc -c) + 1 \) / 2 \* 2) '' + fi + if expr $(large_$c | head -n 1 | tr -d '\n' | wc -c) % 2 >/dev/null; then + large_$c | tr '][' XX | sed 's/$/ /g' + else + large_$c | tr '][' XX + fi + ) | tr ' \n' ., | sed 's/\([^,]*\),\([^,]*\),/:\1:\2:\n/g' | while read line; do + high="$(printf '%s\n' "$line" | cut -d : -f 2)" + low="$(printf '%s\n' "$line" | cut -d : -f 3)" + while test -n "$high"; do + block="$(printf '%s\n' "$high" | colrm 3)$(printf '%s\n' "$low" | colrm 3)" + high="$(printf '%s\n' "$high" | colrm 1 2)" + low="$(printf '%s\n' "$low" | colrm 1 2)" + if test "$block" = '....'; then + printf ' ' + elif test "$block" = '...X'; then + printf '▗' + elif test "$block" = '..X.'; then + printf '▖' + elif test "$block" = '..XX'; then + printf '▄' + elif test "$block" = '.X..'; then + printf '▝' + elif test "$block" = '.X.X'; then + printf '▐' + elif test "$block" = '.XX.'; then + printf '▞' + elif test "$block" = '.XXX'; then + printf '▟' + elif test "$block" = 'X...'; then + printf '▘' + elif test "$block" = 'X..X'; then + printf '▚' + elif test "$block" = 'X.X.'; then + printf '▌' + elif test "$block" = 'X.XX'; then + printf '▙' + elif test "$block" = 'XX..'; then + printf '▀' + elif test "$block" = 'XX.X'; then + printf '▜' + elif test "$block" = 'XXX.'; then + printf '▛' + elif test "$block" = 'XXXX'; then + printf '█' + fi + done + printf '\n' + done | sed -e 's/^/"/' -e 's/$/",/' | sed '$s/,$//' + printf '};\n' +done |
