summaryrefslogtreecommitdiffstats
path: root/timer.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2021-03-30 18:17:26 +0200
committerMattias Andrée <maandree@kth.se>2021-03-30 18:17:55 +0200
commitafe89e5f9fc443383c8067ebe870541b73c679da (patch)
tree4a52e9ed502fdd0bace255152f8219d92e089d4b /timer.c
parentAdd dice (diff)
downloadpdatools-afe89e5f9fc443383c8067ebe870541b73c679da.tar.gz
pdatools-afe89e5f9fc443383c8067ebe870541b73c679da.tar.bz2
pdatools-afe89e5f9fc443383c8067ebe870541b73c679da.tar.xz
m + Add timer
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'timer.c')
-rw-r--r--timer.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/timer.c b/timer.c
new file mode 100644
index 0000000..4a6b051
--- /dev/null
+++ b/timer.c
@@ -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;
+}