153537Sbrian/*
280728Sbrian * Grand digital clock for curses compatible terminals
353537Sbrian * Usage: grdc [-st] [n]   -- run for n seconds (default infinity)
453537Sbrian * Flags: -s: scroll
553537Sbrian *        -t: output time in 12-hour format
653537Sbrian *
753537Sbrian *
853537Sbrian * modified 10-18-89 for curses (jrl)
953537Sbrian * 10-18-89 added signal handling
1053537Sbrian *
1153537Sbrian * modified 03-25-03 for 12 hour option
1253537Sbrian *     - Samy Al Bahra <samy@kerneled.com>
1353537Sbrian *
1453537Sbrian * $FreeBSD$
1553537Sbrian */
1653537Sbrian
1753537Sbrian#include <err.h>
1853537Sbrian#include <ncurses.h>
1953537Sbrian#include <signal.h>
2053537Sbrian#include <stdlib.h>
2153537Sbrian#include <time.h>
2253537Sbrian#include <unistd.h>
2353537Sbrian
2453537Sbrian#define YBASE	10
2553537Sbrian#define XBASE	10
2653537Sbrian#define XLENGTH 58
2753537Sbrian#define YDEPTH  7
2853537Sbrian
2953537Sbrianstatic struct timespec now;
3053537Sbrianstatic struct tm *tm;
3153537Sbrian
3253537Sbrianstatic short disp[11] = {
3353537Sbrian	075557, 011111, 071747, 071717, 055711,
3453537Sbrian	074717, 074757, 071111, 075757, 075717, 002020
3553537Sbrian};
3653537Sbrianstatic long old[6], next[6], new[6], mask;
3753537Sbrian
3853537Sbrianstatic volatile sig_atomic_t sigtermed;
3953537Sbrian
4053537Sbrianstatic int hascolor = 0;
4153537Sbrian
4253537Sbrianstatic void set(int, int);
4353537Sbrianstatic void standt(int);
4453537Sbrianstatic void movto(int, int);
4553537Sbrianstatic void sighndl(int);
4653537Sbrianstatic void usage(void);
4753537Sbrian
4866602Sbrianstatic void
4953537Sbriansighndl(int signo)
5053537Sbrian{
5153537Sbrian
5253537Sbrian	sigtermed = signo;
5353537Sbrian}
5453537Sbrian
5553537Sbrianint
5653537Sbrianmain(int argc, char *argv[])
5753537Sbrian{
5853537Sbrian	struct timespec delay;
5953537Sbrian	time_t prev_sec;
6053537Sbrian	long t, a;
6153537Sbrian	int i, j, s, k;
6253537Sbrian	int n;
6353537Sbrian	int ch;
6486705Sbrian	int scrol;
6586705Sbrian	int t12;
6696580Sbrian
6753537Sbrian	t12 = scrol = 0;
6890160Skris
6990160Skris	while ((ch = getopt(argc, argv, "ts")) != -1)
7069582Sbrian	switch (ch) {
7169582Sbrian	case 's':
7253537Sbrian		scrol = 1;
7353537Sbrian		break;
7453537Sbrian	case 't':
7595258Sdes		t12 = 1;
76141589Sru		break;
7753537Sbrian	case '?':
7853537Sbrian	default:
7953537Sbrian		usage();
8053537Sbrian		/* NOTREACHED */
8169582Sbrian	}
8253537Sbrian	argc -= optind;
8369582Sbrian	argv += optind;
8453537Sbrian
8553537Sbrian	if (argc > 1) {
8653537Sbrian		usage();
8753537Sbrian		/* NOTREACHED */
8853537Sbrian	}
8953537Sbrian
9053537Sbrian	if (argc > 0) {
9153537Sbrian		n = atoi(*argv) + 1;
9253537Sbrian		if (n < 1) {
9353537Sbrian			warnx("number of seconds is out of range");
9453537Sbrian			usage();
9553537Sbrian			/* NOTREACHED */
9653537Sbrian		}
9753537Sbrian	} else
9853537Sbrian		n = 0;
9953537Sbrian
10053537Sbrian	initscr();
10153537Sbrian
10253537Sbrian	signal(SIGINT,sighndl);
10353537Sbrian	signal(SIGTERM,sighndl);
10453537Sbrian	signal(SIGHUP,sighndl);
10553537Sbrian
10653537Sbrian	cbreak();
10753537Sbrian	noecho();
10853537Sbrian	curs_set(0);
10953537Sbrian
11053537Sbrian	hascolor = has_colors();
11153537Sbrian
11253537Sbrian	if(hascolor) {
11353537Sbrian		start_color();
11453537Sbrian		init_pair(1, COLOR_BLACK, COLOR_RED);
11553537Sbrian		init_pair(2, COLOR_RED, COLOR_BLACK);
11653537Sbrian		init_pair(3, COLOR_WHITE, COLOR_BLACK);
11753537Sbrian		attrset(COLOR_PAIR(2));
11853537Sbrian	}
11953537Sbrian
12053537Sbrian	clear();
12153537Sbrian	refresh();
12253537Sbrian
12353537Sbrian	if(hascolor) {
12453537Sbrian		attrset(COLOR_PAIR(3));
12553537Sbrian
12653537Sbrian		mvaddch(YBASE - 2,  XBASE - 3, ACS_ULCORNER);
12753537Sbrian		hline(ACS_HLINE, XLENGTH);
12853537Sbrian		mvaddch(YBASE - 2,  XBASE - 2 + XLENGTH, ACS_URCORNER);
12953537Sbrian
13053537Sbrian		mvaddch(YBASE + YDEPTH - 1,  XBASE - 3, ACS_LLCORNER);
13153537Sbrian		hline(ACS_HLINE, XLENGTH);
13253537Sbrian		mvaddch(YBASE + YDEPTH - 1,  XBASE - 2 + XLENGTH, ACS_LRCORNER);
13353537Sbrian
13453537Sbrian		move(YBASE - 1,  XBASE - 3);
13553537Sbrian		vline(ACS_VLINE, YDEPTH);
13653537Sbrian
13753537Sbrian		move(YBASE - 1,  XBASE - 2 + XLENGTH);
13853537Sbrian		vline(ACS_VLINE, YDEPTH);
13953537Sbrian
14053537Sbrian		attrset(COLOR_PAIR(2));
14153537Sbrian	}
14253537Sbrian	clock_gettime(CLOCK_REALTIME_FAST, &now);
14353537Sbrian	prev_sec = now.tv_sec;
14453537Sbrian	do {
14553537Sbrian		mask = 0;
14653537Sbrian		tm = localtime(&now.tv_sec);
14753537Sbrian		set(tm->tm_sec%10, 0);
14882276Sbrian		set(tm->tm_sec/10, 4);
14953537Sbrian		set(tm->tm_min%10, 10);
15053537Sbrian		set(tm->tm_min/10, 14);
15153537Sbrian
15253537Sbrian		if (t12) {
15353537Sbrian			if (tm->tm_hour < 12) {
15453537Sbrian				if (tm->tm_hour == 0)
15553537Sbrian					tm->tm_hour = 12;
15653537Sbrian				mvaddstr(YBASE + 5, XBASE + 52, "AM");
15753537Sbrian			} else {
15853537Sbrian				if (tm->tm_hour > 12)
15953537Sbrian					tm->tm_hour -= 12;
16053537Sbrian				mvaddstr(YBASE + 5, XBASE + 52, "PM");
16153537Sbrian			}
16253537Sbrian		}
16353537Sbrian
16453537Sbrian		set(tm->tm_hour%10, 20);
16553537Sbrian		set(tm->tm_hour/10, 24);
16653537Sbrian		set(10, 7);
16753537Sbrian		set(10, 17);
16853537Sbrian		for(k=0; k<6; k++) {
16953537Sbrian			if(scrol) {
17053537Sbrian				for(i=0; i<5; i++)
17153537Sbrian					new[i] = (new[i]&~mask) | (new[i+1]&mask);
17253537Sbrian				new[5] = (new[5]&~mask) | (next[k]&mask);
17353537Sbrian			} else
17453537Sbrian				new[k] = (new[k]&~mask) | (next[k]&mask);
17553537Sbrian			next[k] = 0;
17653537Sbrian			for(s=1; s>=0; s--) {
17753537Sbrian				standt(s);
17853537Sbrian				for(i=0; i<6; i++) {
17953537Sbrian					if((a = (new[i]^old[i])&(s ? new : old)[i]) != 0) {
18053537Sbrian						for(j=0,t=1<<26; t; t>>=1,j++) {
18153537Sbrian							if(a&t) {
18253537Sbrian								if(!(a&(t<<1))) {
18353537Sbrian									movto(YBASE + i, XBASE + 2*j);
18453537Sbrian								}
18553537Sbrian								addstr("  ");
18653537Sbrian							}
18753537Sbrian						}
18853537Sbrian					}
18953537Sbrian					if(!s) {
19053537Sbrian						old[i] = new[i];
19153537Sbrian					}
19253537Sbrian				}
19353537Sbrian				if(!s) {
19453537Sbrian					refresh();
19553537Sbrian				}
19653537Sbrian			}
19753537Sbrian		}
19853537Sbrian		movto(6, 0);
19953537Sbrian		refresh();
20053537Sbrian		clock_gettime(CLOCK_REALTIME_FAST, &now);
20153537Sbrian		if (now.tv_sec == prev_sec) {
20253537Sbrian			if (delay.tv_nsec > 0) {
20353537Sbrian				delay.tv_sec = 0;
20453537Sbrian				delay.tv_nsec = 1000000000 - now.tv_nsec;
20553537Sbrian			} else {
20653537Sbrian				delay.tv_sec = 1;
20753537Sbrian				delay.tv_nsec = 0;
20853537Sbrian			}
20953537Sbrian			nanosleep(&delay, NULL);
21053537Sbrian			clock_gettime(CLOCK_REALTIME_FAST, &now);
21153537Sbrian		}
21253537Sbrian		n -= now.tv_sec - prev_sec;
21353537Sbrian		prev_sec = now.tv_sec;
21453537Sbrian		if (sigtermed) {
21553537Sbrian			standend();
21653537Sbrian			clear();
21753537Sbrian			refresh();
21853537Sbrian			endwin();
21953537Sbrian			errx(1, "terminated by signal %d", (int)sigtermed);
22053537Sbrian		}
22153537Sbrian	} while (n);
22253537Sbrian	standend();
22353537Sbrian	clear();
22453537Sbrian	refresh();
22568032Sbrian	endwin();
22653537Sbrian	return(0);
22768846Sbrian}
22868846Sbrian
22953537Sbrianstatic void
23053537Sbrianset(int t, int n)
23153537Sbrian{
23253537Sbrian	int i, m;
23353537Sbrian
23453537Sbrian	m = 7<<n;
23553537Sbrian	for(i=0; i<5; i++) {
23653537Sbrian		next[i] |= ((disp[t]>>(4-i)*3)&07)<<n;
23753537Sbrian		mask |= (next[i]^old[i])&m;
23853537Sbrian	}
23953537Sbrian	if(mask&m)
24053537Sbrian		mask |= m;
24153537Sbrian}
24253537Sbrian
24353537Sbrianstatic void
24453537Sbrianstandt(int on)
24553537Sbrian{
24653537Sbrian	if (on) {
24753537Sbrian		if(hascolor) {
24853537Sbrian			attron(COLOR_PAIR(1));
24953537Sbrian		} else {
25053537Sbrian			attron(A_STANDOUT);
25153537Sbrian		}
25253537Sbrian	} else {
25369948Sjulian		if(hascolor) {
25486705Sbrian			attron(COLOR_PAIR(2));
25586705Sbrian		} else {
25653537Sbrian			attroff(A_STANDOUT);
25753537Sbrian		}
25853537Sbrian	}
25953537Sbrian}
26053537Sbrian
261171195Sscfstatic void
26286705Sbrianmovto(int line, int col)
26353537Sbrian{
26453537Sbrian	move(line, col);
26553537Sbrian}
26653537Sbrian
26753537Sbrianstatic void
26853537Sbrianusage(void)
26953537Sbrian{
27053537Sbrian
27153537Sbrian	(void)fprintf(stderr, "usage: grdc [-st] [n]\n");
27253537Sbrian	exit(1);
27353537Sbrian}
27453537Sbrian