diff options
Diffstat (limited to '')
| -rw-r--r-- | mongotimer.c | 342 | 
1 files changed, 342 insertions, 0 deletions
| diff --git a/mongotimer.c b/mongotimer.c new file mode 100644 index 0000000..692d7a2 --- /dev/null +++ b/mongotimer.c @@ -0,0 +1,342 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/ioctl.h> +#include <sys/timerfd.h> +#include <errno.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "arg.h" + +#define DX 16 +#define DY 12 +#define DC 16 +#define DM 16 + +#include "mongo_0.h" +#include "mongo_1.h" +#include "mongo_2.h" +#include "mongo_3.h" +#include "mongo_4.h" +#include "mongo_5.h" +#include "mongo_6.h" +#include "mongo_7.h" +#include "mongo_8.h" +#include "mongo_9.h" +#include "mongo_c.h" +#include "mongo_m.h" + +static const char **mongo_ds[] = { +	mongo_0, mongo_1, mongo_2, mongo_3, mongo_4, +	mongo_5, mongo_6, mongo_7, mongo_8, mongo_9 +}; + +static volatile sig_atomic_t caught_sigterm = 0; +static volatile sig_atomic_t caught_sigwinch = 1; + +char *argv0; + +static void +usage(void) +{ +	fprintf(stderr, "usage: %s [[-e] [[hh]:mm:]ss]\n", argv0); +	exit(1); +} + +static void +sigterm(int signo) +{ +	caught_sigterm = 1; +	(void) signo; +} + +static void +sigwinch(int signo) +{ +	caught_sigwinch = 1; +	(void) signo; +} + +static void +print_time(const char ***str, size_t y, size_t x) +{ +	size_t r, c; + +	fprintf(stdout, "\033[%zu;1H\033[1J", y + 1); + +	for (r = 0; r < DY; r++) { +		fprintf(stdout, "\033[%zu;%zuH\033[1K", y + r + 1, x + 1); +		for (c = 0; str[c]; c++) +			fprintf(stdout, "%s", str[c][r]); +		fprintf(stdout, "\033[0K"); +	} + +	fprintf(stdout, "\033[0J"); +	fflush(stdout); +} + +static int +display_stopwatch(int timerfd) +{ +	uint64_t overrun, total_overrun = 0; +	struct winsize winsize; +	size_t x = 0, y = 0, width, i; +	const char **digits[23]; +	uint64_t h, m, s, h_div = 1, th; +	size_t h_digits = 0; +	int small = 0; + +	/* TODO pause (yellow text) on <space> */ + +	while (!caught_sigterm) { +		if (caught_sigwinch) { +			if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { +				if (errno == EINTR) +					continue; +				goto fail; +			} +			caught_sigwinch = 0; +		recenter: +			y = winsize.ws_row; +			x = winsize.ws_col; +			width = 4 * DX + DC; +			if (h_digits) { +				width += DC; +				width += h_digits * DX; +			} +			if      (y < DY)    small = 1; +			else if (x < width) small = 1; +			else                small = 0; +			y -= DY; +			x -= width; +			y /= 2; +			x /= 2; +		} + +		if (small) { +			fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small"); +			fflush(stdout); +			pause(); +			continue; +		} + +		s = total_overrun % 60; +		m = total_overrun / 60 % 60; +		h = total_overrun / 60 / 60; +		if (h / h_div) { +			h_div *= 10; +			h_digits++; +			goto recenter; +		} + +		i = 0; +		if (h_digits) { +			for (th = h; th; th /= 10) +				digits[h_digits - ++i] = mongo_ds[th % 10]; +			digits[i++] = mongo_c; +		} +		digits[i++] = mongo_ds[m / 10]; +		digits[i++] = mongo_ds[m % 10]; +		digits[i++] = mongo_c; +		digits[i++] = mongo_ds[s / 10]; +		digits[i++] = mongo_ds[s % 10]; +		digits[i++] = NULL; + +		print_time(digits, y, x); + +		if (read(timerfd, &overrun, sizeof(overrun)) < 0) { +			if (errno != EINTR) +				goto fail; +		} else { +			total_overrun += overrun; +		} +	} + +	return 0; + +fail: +	return -1; +} + +static int +display_timer(int timerfd, int64_t time, int exit_on_zero) +{ +	uint64_t overrun, abstime; +	struct winsize winsize; +	size_t x = 0, y = 0, width; +	const char **digits[24]; +	uint64_t h, m, s, th, h_div; +	size_t h_digits, i; +	int small = 0; + +	/* TODO pause (yellow text) on <space> */ + +	while (!caught_sigterm) { +		if (caught_sigwinch) { +			if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) { +				if (errno == EINTR) +					continue; +				goto fail; +			} +			caught_sigwinch = 0; +		} + +		abstime = time < 0 ? (uint64_t)-time : (uint64_t)time; +		s = abstime % 60; +		m = abstime / 60 % 60; +		h = abstime / 60 / 60; + +		y = winsize.ws_row; +		x = winsize.ws_col; +		for (h_div = 1, h_digits = 0; h / h_div; h_div *= 10, h_digits += 1); +		width = 4 * DX + DC; +		if (time < 0) +			width += DM; +		if (h_digits) +			width += h_digits * DX + DC; +		if      (y < DY)    small = 1; +		else if (x < width) small = 1; +		else                small = 0; +		y -= DY; +		x -= width; +		y /= 2; +		x /= 2; + +		if (small) { +			fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small"); +			fflush(stdout); +			pause(); +			continue; +		} + +		i = 0; +		if (time < 0) +			digits[i++] = mongo_m; +		if (h_digits) { +			for (th = h; th; th /= 10) +				digits[h_digits - ++i] = mongo_ds[th % 10]; +			digits[i++] = mongo_c; +		} +		digits[i++] = mongo_ds[m / 10]; +		digits[i++] = mongo_ds[m % 10]; +		digits[i++] = mongo_c; +		digits[i++] = mongo_ds[s / 10]; +		digits[i++] = mongo_ds[s % 10]; +		digits[i++] = NULL; + +		if (time >= 0) { +			print_time(digits, y, x); +		} else { +			fprintf(stdout, "\033[31m\n"); +			print_time(digits, y, x); +			fprintf(stdout, "\033[39m\n"); +		} + +		if (read(timerfd, &overrun, sizeof(overrun)) < 0) { +			if (errno != EINTR) +				goto fail; +		} else { +			time -= (int64_t)overrun; +			if (time <= 0 && exit_on_zero) +				break; +		} +	} + +	if (caught_sigterm && exit_on_zero) +		return -1; + +	return 0; + +fail: +	return -1; +} + +int +main(int argc, char *argv[]) +{ +	int timerfd = -1; +	int exit_on_zero = 0; +	struct itimerspec itimerspec; +	struct sigaction sigact; +	int64_t time = 0, t = 0; +	size_t colons = 0; +	char *s; + +	ARGBEGIN { +	case 'e': +		exit_on_zero = 1; +		break; +	default: +		usage(); +	} ARGEND; + +	if (argc > 1 || (exit_on_zero && !argc)) +		usage(); + +	fprintf(stdout, "\033[?1049h\033[?25l"); + +	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_BOOTTIME, 0); +	if (timerfd < 0) +		goto fail; +	if (timerfd_settime(timerfd, 0, &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); + +	if (argc) { +		for (s = argv[0]; *s; s++) { +			if ('0' <= *s && *s <= '9') { +				t = t * 10 + (*s & 15); +			} else if (*s == ':' && colons++ < 2) { +				if (s == argv[0] || s[-1] == ':') +					goto fail_usage; +				time = (time + t) * 60; +				t = 0; +			} else { +				goto fail_usage; +			} +		} +		if (s == argv[0] || s[-1] == ':') +			goto fail_usage; +		time += t; +		if (display_timer(timerfd, time, exit_on_zero)) +			goto fail; +	} else { +		if (display_stopwatch(timerfd)) +			goto fail; +	} + +	fprintf(stdout, "\033[?25h\n\033[?1049l"); +	fflush(stdout); +	close(timerfd); +	return 0; + +fail: +	perror(argv0 ? argv0 : "mongotimer"); +	fprintf(stdout, "\033[?25h\n\033[?1049l"); +	fflush(stdout); +	if (timerfd >= 0) +		close(timerfd); +	return 1; + +fail_usage: +	fprintf(stdout, "\033[?25h\n\033[?1049l"); +	fflush(stdout); +	usage(); +} | 
