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