1/*
2 * Grand digital clock for curses compatible terminals
3 * Usage: grdc [-st] [n]   -- run for n seconds (default infinity)
4 *        grdc -c n        -- countdown n seconds
5 * Flags: -c: Countdown timer mode
6 *        -s: scroll
7 *        -t: output time in 12-hour format
8 *
9 *
10 * modified 10-18-89 for curses (jrl)
11 * 10-18-89 added signal handling
12 * 02-18-02 added countdown timer mode
13 *
14 * modified 03-25-03 for 12 hour option
15 *     - Samy Al Bahra <samy@kerneled.com>
16 */
17
18#include <err.h>
19#include <ncurses.h>
20#include <signal.h>
21#include <stdlib.h>
22#include <time.h>
23#include <unistd.h>
24
25#define YBASE	10
26#define XBASE	10
27#define XLENGTH 58
28#define YDEPTH  7
29
30static struct timespec now;
31static struct tm *tm;
32static struct timespec end;
33
34static short disp[11] = {
35	075557, 011111, 071747, 071717, 055711,
36	074717, 074757, 071111, 075757, 075717, 002020
37};
38static long old[6], next[6], new[6], mask;
39
40static volatile sig_atomic_t sigtermed;
41
42static int hascolor = 0;
43
44static void set(int, int);
45static void standt(int);
46static void movto(int, int);
47static void sighndl(int);
48static void usage(void) __dead2;
49
50static void
51sighndl(int signo)
52{
53
54	sigtermed = signo;
55}
56
57int
58main(int argc, char *argv[])
59{
60	struct timespec delay;
61	time_t prev_sec;
62	long t, a;
63	int i, j, s, k;
64	int n;
65	int ch;
66	bool scrol = false, t12 = false, timer = false;
67	int hour, minute, second;
68
69	while ((ch = getopt(argc, argv, "cst")) != -1)
70	switch (ch) {
71	case 'c':
72		timer = true;
73		break;
74	case 's':
75		scrol = true;
76		break;
77	case 't':
78		t12 = true;
79		break;
80	case '?':
81	default:
82		usage();
83		/* NOTREACHED */
84	}
85	argc -= optind;
86	argv += optind;
87
88	if ((argc > 1) || (argc == 0 && timer)) {
89		usage();
90		/* NOTREACHED */
91	}
92
93	if (argc > 0) {
94		n = atoi(*argv) + 1;
95		if (n < 1) {
96			warnx("number of seconds is out of range");
97			usage();
98			/* NOTREACHED */
99		}
100	} else
101		n = 0;
102
103	if (timer && n == 0)
104		return(0);
105
106	initscr();
107
108	signal(SIGINT,sighndl);
109	signal(SIGTERM,sighndl);
110	signal(SIGHUP,sighndl);
111
112	cbreak();
113	noecho();
114	curs_set(0);
115
116	hascolor = has_colors();
117
118	if (hascolor) {
119		start_color();
120		init_pair(1, COLOR_BLACK, COLOR_RED);
121		init_pair(2, COLOR_RED, COLOR_BLACK);
122		init_pair(3, COLOR_WHITE, COLOR_BLACK);
123		attrset(COLOR_PAIR(2));
124	}
125
126	clear();
127	refresh();
128
129	if (hascolor) {
130		attrset(COLOR_PAIR(3));
131
132		mvaddch(YBASE - 2,  XBASE - 3, ACS_ULCORNER);
133		hline(ACS_HLINE, XLENGTH);
134		mvaddch(YBASE - 2,  XBASE - 2 + XLENGTH, ACS_URCORNER);
135
136		mvaddch(YBASE + YDEPTH - 1,  XBASE - 3, ACS_LLCORNER);
137		hline(ACS_HLINE, XLENGTH);
138		mvaddch(YBASE + YDEPTH - 1,  XBASE - 2 + XLENGTH, ACS_LRCORNER);
139
140		move(YBASE - 1,  XBASE - 3);
141		vline(ACS_VLINE, YDEPTH);
142
143		move(YBASE - 1,  XBASE - 2 + XLENGTH);
144		vline(ACS_VLINE, YDEPTH);
145
146		attrset(COLOR_PAIR(2));
147	}
148	clock_gettime(CLOCK_REALTIME_FAST, &now);
149	prev_sec = now.tv_sec;
150	if (timer) {
151		end = now;
152		end.tv_sec += n;
153	}
154	do {
155		mask = 0;
156		if (!timer) {
157			tm = localtime(&now.tv_sec);
158			if (t12) {
159				if (tm->tm_hour < 12) {
160					if (tm->tm_hour == 0)
161						tm->tm_hour = 12;
162					mvaddstr(YBASE + 5, XBASE + 52, "AM");
163				} else {
164					if (tm->tm_hour > 12)
165						tm->tm_hour -= 12;
166					mvaddstr(YBASE + 5, XBASE + 52, "PM");
167				}
168			}
169			hour = tm->tm_hour;
170			minute = tm->tm_min;
171			second = tm->tm_sec;
172		} else {
173			n = end.tv_sec - now.tv_sec;
174			if (n <= 0)
175				break;
176			hour = (n / 3600) % 100;
177			minute = (n / 60) % 60;
178			second = n % 60;
179		}
180		set(second % 10, 0);
181		set(second / 10, 4);
182		set(minute % 10, 10);
183		set(minute / 10, 14);
184		set(hour % 10, 20);
185		set(hour / 10, 24);
186		set(10, 7);
187		set(10, 17);
188		for(k=0; k<6; k++) {
189			if(scrol) {
190				for(i=0; i<5; i++)
191					new[i] = (new[i]&~mask) | (new[i+1]&mask);
192				new[5] = (new[5]&~mask) | (next[k]&mask);
193			} else
194				new[k] = (new[k]&~mask) | (next[k]&mask);
195			next[k] = 0;
196			for(s=1; s>=0; s--) {
197				standt(s);
198				for(i=0; i<6; i++) {
199					if((a = (new[i]^old[i])&(s ? new : old)[i]) != 0) {
200						for(j=0,t=1<<26; t; t>>=1,j++) {
201							if(a&t) {
202								if(!(a&(t<<1))) {
203									movto(YBASE + i, XBASE + 2*j);
204								}
205								addstr("  ");
206							}
207						}
208					}
209					if(!s) {
210						old[i] = new[i];
211					}
212				}
213				if(!s) {
214					refresh();
215				}
216			}
217		}
218		movto(6, 0);
219		refresh();
220		clock_gettime(CLOCK_REALTIME_FAST, &now);
221		if (now.tv_sec == prev_sec) {
222			if (delay.tv_nsec > 0) {
223				delay.tv_sec = 0;
224				delay.tv_nsec = 1000000000 - now.tv_nsec;
225			} else {
226				delay.tv_sec = 1;
227				delay.tv_nsec = 0;
228			}
229			nanosleep(&delay, NULL);
230			clock_gettime(CLOCK_REALTIME_FAST, &now);
231		}
232		n -= now.tv_sec - prev_sec;
233		prev_sec = now.tv_sec;
234		if (sigtermed) {
235			standend();
236			clear();
237			refresh();
238			endwin();
239			errx(1, "terminated by signal %d", (int)sigtermed);
240		}
241	} while (n);
242	standend();
243	clear();
244	refresh();
245	endwin();
246	return(0);
247}
248
249static void
250set(int t, int n)
251{
252	int i, m;
253
254	m = 7<<n;
255	for(i=0; i<5; i++) {
256		next[i] |= ((disp[t]>>(4-i)*3)&07)<<n;
257		mask |= (next[i]^old[i])&m;
258	}
259	if(mask&m)
260		mask |= m;
261}
262
263static void
264standt(int on)
265{
266	if (on) {
267		if(hascolor) {
268			attron(COLOR_PAIR(1));
269		} else {
270			attron(A_STANDOUT);
271		}
272	} else {
273		if(hascolor) {
274			attron(COLOR_PAIR(2));
275		} else {
276			attroff(A_STANDOUT);
277		}
278	}
279}
280
281static void
282movto(int line, int col)
283{
284	move(line, col);
285}
286
287static void
288usage(void)
289{
290
291	(void)fprintf(stderr, "usage: grdc [-st] [n]\n"
292	    "      grdc -c n\n");
293	exit(1);
294}
295