summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile9
-rw-r--r--clock.c354
-rwxr-xr-xlarge.sh194
4 files changed, 556 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index c68ad4a..abde984 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
*.o
*.su
*.bin
+/large.h
diff --git a/Makefile b/Makefile
index 8aa59db..285576d 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/clock.c b/clock.c
new file mode 100644
index 0000000..317f517
--- /dev/null
+++ b/clock.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