14974Sache/*
24974Sache * Grand digital clock for curses compatible terminals
3112749Smux * Usage: grdc [-st] [n]   -- run for n seconds (default infinity)
44974Sache * Flags: -s: scroll
5112749Smux *        -t: output time in 12-hour format
64974Sache *
7112749Smux *
84974Sache * modified 10-18-89 for curses (jrl)
94974Sache * 10-18-89 added signal handling
1051358Sgreen *
11112749Smux * modified 03-25-03 for 12 hour option
12112749Smux *     - Samy Al Bahra <samy@kerneled.com>
13112749Smux *
1451358Sgreen * $FreeBSD$
154974Sache */
164974Sache
1783928Sru#include <err.h>
18210809Suqs#include <ncurses.h>
194974Sache#include <signal.h>
204974Sache#include <stdlib.h>
21210809Suqs#include <time.h>
224974Sache#include <unistd.h>
234974Sache
244974Sache#define YBASE	10
254974Sache#define XBASE	10
264974Sache#define XLENGTH 58
274974Sache#define YDEPTH  7
284974Sache
29249840Seadlerstatic struct timespec now;
30249840Seadlerstatic struct tm *tm;
314974Sache
32249840Seadlerstatic short disp[11] = {
334974Sache	075557, 011111, 071747, 071717, 055711,
344974Sache	074717, 074757, 071111, 075757, 075717, 002020
354974Sache};
36249840Seadlerstatic long old[6], next[6], new[6], mask;
374974Sache
38249840Seadlerstatic volatile sig_atomic_t sigtermed;
394974Sache
40249840Seadlerstatic int hascolor = 0;
414974Sache
42249840Seadlerstatic void set(int, int);
43249840Seadlerstatic void standt(int);
44249840Seadlerstatic void movto(int, int);
45249840Seadlerstatic void sighndl(int);
46249840Seadlerstatic void usage(void);
474974Sache
48249840Seadlerstatic void
49203920Suqssighndl(int signo)
504974Sache{
51249840Seadler
52249840Seadler	sigtermed = signo;
534974Sache}
544974Sache
554974Sacheint
56203920Suqsmain(int argc, char *argv[])
574974Sache{
58210755Suqs	struct timespec delay;
59210827Suqs	time_t prev_sec;
60203920Suqs	long t, a;
61203920Suqs	int i, j, s, k;
62203920Suqs	int n;
63203920Suqs	int ch;
64203920Suqs	int scrol;
65203920Suqs	int t12;
664974Sache
67112749Smux	t12 = scrol = 0;
6883928Sru
69112749Smux	while ((ch = getopt(argc, argv, "ts")) != -1)
7083928Sru	switch (ch) {
7183928Sru	case 's':
7283928Sru		scrol = 1;
7383928Sru		break;
74112749Smux	case 't':
75112749Smux		t12 = 1;
76112749Smux		break;
7783928Sru	case '?':
7883928Sru	default:
7983928Sru		usage();
8083928Sru		/* NOTREACHED */
8183928Sru	}
8283928Sru	argc -= optind;
8383928Sru	argv += optind;
8483928Sru
8583928Sru	if (argc > 1) {
8683928Sru		usage();
8783928Sru		/* NOTREACHED */
8883928Sru	}
8983928Sru
90210809Suqs	if (argc > 0) {
91210755Suqs		n = atoi(*argv) + 1;
92210809Suqs		if (n < 1) {
93210809Suqs			warnx("number of seconds is out of range");
94210809Suqs			usage();
95210809Suqs			/* NOTREACHED */
96210809Suqs		}
97210809Suqs	} else
9883928Sru		n = 0;
9983928Sru
1004974Sache	initscr();
1014974Sache
1024974Sache	signal(SIGINT,sighndl);
1034974Sache	signal(SIGTERM,sighndl);
1044974Sache	signal(SIGHUP,sighndl);
1054974Sache
1064974Sache	cbreak();
1074974Sache	noecho();
10851358Sgreen	curs_set(0);
1098856Srgrimes
1104974Sache	hascolor = has_colors();
1114974Sache
1128856Srgrimes	if(hascolor) {
1134974Sache		start_color();
1144974Sache		init_pair(1, COLOR_BLACK, COLOR_RED);
1154974Sache		init_pair(2, COLOR_RED, COLOR_BLACK);
1164974Sache		init_pair(3, COLOR_WHITE, COLOR_BLACK);
1174974Sache		attrset(COLOR_PAIR(2));
1184974Sache	}
1198856Srgrimes
1204974Sache	clear();
1214974Sache	refresh();
1224974Sache
1238856Srgrimes	if(hascolor) {
1244974Sache		attrset(COLOR_PAIR(3));
1254974Sache
1264974Sache		mvaddch(YBASE - 2,  XBASE - 3, ACS_ULCORNER);
1274974Sache		hline(ACS_HLINE, XLENGTH);
1284974Sache		mvaddch(YBASE - 2,  XBASE - 2 + XLENGTH, ACS_URCORNER);
1294974Sache
1304974Sache		mvaddch(YBASE + YDEPTH - 1,  XBASE - 3, ACS_LLCORNER);
1314974Sache		hline(ACS_HLINE, XLENGTH);
1324974Sache		mvaddch(YBASE + YDEPTH - 1,  XBASE - 2 + XLENGTH, ACS_LRCORNER);
1334974Sache
1344974Sache		move(YBASE - 1,  XBASE - 3);
1354974Sache		vline(ACS_VLINE, YDEPTH);
1364974Sache
1374974Sache		move(YBASE - 1,  XBASE - 2 + XLENGTH);
1384974Sache		vline(ACS_VLINE, YDEPTH);
1394974Sache
1404974Sache		attrset(COLOR_PAIR(2));
1414974Sache	}
142210755Suqs	clock_gettime(CLOCK_REALTIME_FAST, &now);
143210827Suqs	prev_sec = now.tv_sec;
1444974Sache	do {
1454974Sache		mask = 0;
146210755Suqs		tm = localtime(&now.tv_sec);
1474974Sache		set(tm->tm_sec%10, 0);
1484974Sache		set(tm->tm_sec/10, 4);
1494974Sache		set(tm->tm_min%10, 10);
1504974Sache		set(tm->tm_min/10, 14);
151112749Smux
152112749Smux		if (t12) {
153112749Smux			if (tm->tm_hour > 12) {
154112749Smux				tm->tm_hour -= 12;
155112749Smux				mvaddstr(YBASE + 5, XBASE + 52, "PM");
156116739Swill			} else {
157116739Swill				if (tm->tm_hour == 0)
158116739Swill					tm->tm_hour = 12;
159116739Swill
160112749Smux				mvaddstr(YBASE + 5, XBASE + 52, "AM");
161116739Swill			}
162112749Smux		}
163112749Smux
1644974Sache		set(tm->tm_hour%10, 20);
1654974Sache		set(tm->tm_hour/10, 24);
1664974Sache		set(10, 7);
1674974Sache		set(10, 17);
1684974Sache		for(k=0; k<6; k++) {
1694974Sache			if(scrol) {
1704974Sache				for(i=0; i<5; i++)
1714974Sache					new[i] = (new[i]&~mask) | (new[i+1]&mask);
1724974Sache				new[5] = (new[5]&~mask) | (next[k]&mask);
1734974Sache			} else
1744974Sache				new[k] = (new[k]&~mask) | (next[k]&mask);
1754974Sache			next[k] = 0;
1764974Sache			for(s=1; s>=0; s--) {
1774974Sache				standt(s);
1784974Sache				for(i=0; i<6; i++) {
1794974Sache					if((a = (new[i]^old[i])&(s ? new : old)[i]) != 0) {
1804974Sache						for(j=0,t=1<<26; t; t>>=1,j++) {
1814974Sache							if(a&t) {
1824974Sache								if(!(a&(t<<1))) {
1834974Sache									movto(YBASE + i, XBASE + 2*j);
1844974Sache								}
1854974Sache								addstr("  ");
1864974Sache							}
1874974Sache						}
1884974Sache					}
1894974Sache					if(!s) {
1904974Sache						old[i] = new[i];
1914974Sache					}
1924974Sache				}
1934974Sache				if(!s) {
1944974Sache					refresh();
1954974Sache				}
1964974Sache			}
1974974Sache		}
1984974Sache		movto(6, 0);
1994974Sache		refresh();
200210827Suqs		clock_gettime(CLOCK_REALTIME_FAST, &now);
201210827Suqs		if (now.tv_sec == prev_sec) {
202210809Suqs			if (delay.tv_nsec > 0) {
203210809Suqs				delay.tv_sec = 0;
204210827Suqs				delay.tv_nsec = 1000000000 - now.tv_nsec;
205210809Suqs			} else {
206210809Suqs				delay.tv_sec = 1;
207210809Suqs				delay.tv_nsec = 0;
208210809Suqs			}
209210809Suqs			nanosleep(&delay, NULL);
210210827Suqs			clock_gettime(CLOCK_REALTIME_FAST, &now);
211210755Suqs		}
212210827Suqs		n -= now.tv_sec - prev_sec;
213210827Suqs		prev_sec = now.tv_sec;
2144974Sache		if (sigtermed) {
2154974Sache			standend();
2164974Sache			clear();
2174974Sache			refresh();
2184974Sache			endwin();
21983928Sru			errx(1, "terminated by signal %d", (int)sigtermed);
2204974Sache		}
221210809Suqs	} while (n);
2224974Sache	standend();
2234974Sache	clear();
2244974Sache	refresh();
2254974Sache	endwin();
2264974Sache	return(0);
2274974Sache}
2284974Sache
229249840Seadlerstatic void
2304974Sacheset(int t, int n)
2314974Sache{
232203920Suqs	int i, m;
2334974Sache
2344974Sache	m = 7<<n;
2354974Sache	for(i=0; i<5; i++) {
2364974Sache		next[i] |= ((disp[t]>>(4-i)*3)&07)<<n;
2374974Sache		mask |= (next[i]^old[i])&m;
2384974Sache	}
2394974Sache	if(mask&m)
2404974Sache		mask |= m;
2414974Sache}
2424974Sache
243249840Seadlerstatic void
2444974Sachestandt(int on)
2454974Sache{
2464974Sache	if (on) {
2474974Sache		if(hascolor) {
2484974Sache			attron(COLOR_PAIR(1));
2494974Sache		} else {
2508856Srgrimes			attron(A_STANDOUT);
2518856Srgrimes		}
2524974Sache	} else {
2534974Sache		if(hascolor) {
2544974Sache			attron(COLOR_PAIR(2));
2554974Sache		} else {
2564974Sache			attroff(A_STANDOUT);
2574974Sache		}
2584974Sache	}
2594974Sache}
2604974Sache
261249840Seadlerstatic void
2624974Sachemovto(int line, int col)
2634974Sache{
2644974Sache	move(line, col);
2654974Sache}
2664974Sache
267249840Seadlerstatic void
26883928Sruusage(void)
26983928Sru{
27083928Sru
271112749Smux	(void)fprintf(stderr, "usage: grdc [-st] [n]\n");
27283928Sru	exit(1);
27383928Sru}
274