xref: /dragonfly/games/grdc/grdc.c (revision 6159ff08)
1 /*
2  * Copyright (c) 2002 Amos Shapir.
3  * Public domain.
4  *
5  * Grand digital clock for curses compatible terminals
6  *
7  * Usage: grdc [-s] [-d msecs] [n]   -- run for n seconds (default infinity)
8  * Flags:	-s: scroll (default scroll duration 120msec)
9  *		-d msecs: specify scroll duration (implies -s)
10  *
11  * modified 10-18-89 for curses (jrl)
12  * 10-18-89 added signal handling
13  * 03-23-04 added centering, scroll delay (cap)
14  *
15  * $FreeBSD: src/games/grdc/grdc.c,v 1.8.2.1 2001/10/02 11:51:49 ru Exp $
16  */
17 
18 #include <sys/time.h>
19 
20 #include <err.h>
21 #include <ncurses.h>
22 #include <poll.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <unistd.h>
27 
28 #define XLENGTH 58
29 #define YDEPTH  7
30 
31 static int hascolor = 0;
32 static int xbase, ybase, xmax, ymax;
33 static long old[6], next[6], new[6], mask;
34 static volatile sig_atomic_t sigtermed;
35 
36 static void cleanup(void);
37 static void draw_row(int, int);
38 static void set(int, int);
39 static void sighndl(int);
40 static int  snooze(long);
41 static void standt(int);
42 static void usage(void) __dead2;
43 
44 static void
45 sighndl(int signo)
46 {
47 	sigtermed = signo;
48 }
49 
50 int
51 main(int argc, char **argv)
52 {
53 	int i, s, k, n, ch;
54 	bool scrol, forever;
55 	long scroll_msecs, delay_msecs;
56 	struct timespec now, scroll_ts;
57 	struct tm *tm;
58 
59 	n = 0;
60 	forever = true;
61 	scrol = false;
62 	scroll_msecs = 120;
63 
64 	while ((ch = getopt(argc, argv, "d:s")) != -1) {
65 		switch (ch) {
66 		case 'd':
67 			scroll_msecs = atol(optarg);
68 			if (scroll_msecs < 0)
69 				errx(1, "scroll duration may not be negative");
70 			/* FALLTHROUGH */
71 		case 's':
72 			scrol = true;
73 			break;
74 		case '?':
75 		default:
76 			usage();
77 			/* NOTREACHED */
78 		}
79 	}
80 	argc -= optind;
81 	argv += optind;
82 
83 	if (argc > 1) {
84 		usage();
85 		/* NOTREACHED */
86 	}
87 
88 	if (argc > 0) {
89 		n = atoi(*argv);
90 		forever = false;
91 	}
92 
93 	initscr();
94 
95 	getmaxyx(stdscr, ymax, xmax);
96 	if (ymax < YDEPTH + 2 || xmax < XLENGTH + 4) {
97 		endwin();
98 		errx(1, "terminal too small");
99 	}
100 	xbase = (xmax - XLENGTH) / 2 + 2;
101 	ybase = (ymax - YDEPTH) / 2 + 1;
102 
103 	signal(SIGINT, sighndl);
104 	signal(SIGTERM, sighndl);
105 	signal(SIGHUP, sighndl);
106 
107 	cbreak();
108 	noecho();
109 	curs_set(0);
110 
111 	hascolor = has_colors();
112 
113 	if (hascolor) {
114 		start_color();
115 		init_pair(1, COLOR_BLACK, COLOR_RED);
116 		init_pair(2, COLOR_RED, COLOR_BLACK);
117 		init_pair(3, COLOR_WHITE, COLOR_BLACK);
118 		attrset(COLOR_PAIR(2));
119 	}
120 
121 	clear();
122 	refresh();
123 
124 	if (hascolor) {
125 		attrset(COLOR_PAIR(3));
126 
127 		mvaddch(ybase - 2, xbase - 3, ACS_ULCORNER);
128 		hline(ACS_HLINE, XLENGTH);
129 		mvaddch(ybase - 2, xbase - 2 + XLENGTH, ACS_URCORNER);
130 
131 		mvaddch(ybase + YDEPTH - 1, xbase - 3, ACS_LLCORNER);
132 		hline(ACS_HLINE, XLENGTH);
133 		mvaddch(ybase + YDEPTH - 1, xbase - 2 + XLENGTH, ACS_LRCORNER);
134 
135 		move(ybase - 1, xbase - 3);
136 		vline(ACS_VLINE, YDEPTH);
137 
138 		move(ybase - 1, xbase - 2 + XLENGTH);
139 		vline(ACS_VLINE, YDEPTH);
140 
141 		attrset(COLOR_PAIR(2));
142 		refresh();
143 	}
144 
145 	scroll_ts.tv_sec = scroll_msecs / 1000;
146 	scroll_ts.tv_nsec = 1000000 * (scroll_msecs % 1000);
147 
148 	do {
149 		clock_gettime(CLOCK_REALTIME_FAST, &now);
150 		if (scrol)
151 			timespecadd(&now, &scroll_ts, &now);
152 		tm = localtime(&now.tv_sec);
153 
154 		mask = 0;
155 		set(tm->tm_sec % 10, 0);
156 		set(tm->tm_sec / 10, 4);
157 		set(tm->tm_min % 10, 10);
158 		set(tm->tm_min / 10, 14);
159 		set(tm->tm_hour % 10, 20);
160 		set(tm->tm_hour / 10, 24);
161 		set(10, 7);
162 		set(10, 17);
163 
164 		for (k = 0; k < 6; k++) {
165 			if (scrol) {
166 				if (snooze(scroll_msecs / 6) == 1)
167 					goto out;
168 				for(i = 0; i < 5; i++) {
169 					new[i] = (new[i] & ~mask) |
170 						 (new[i+1] & mask);
171 				}
172 				new[5] = (new[5] & ~mask) | (next[k] & mask);
173 			} else {
174 				new[k] = (new[k] & ~mask) | (next[k] & mask);
175 			}
176 			next[k] = 0;
177 			for (s = 1; s >= 0; s--) {
178 				standt(s);
179 				for (i = 0; i < 6; i++)
180 					draw_row(i, s);
181 				if (!s) {
182 					move(ybase, 0);
183 					refresh();
184 				}
185 			}
186 		}
187 		move(ybase, 0);
188 		refresh();
189 
190 		clock_gettime(CLOCK_REALTIME_FAST, &now);
191 		delay_msecs = 1000 - now.tv_nsec / 1000000;
192 		/* want scrolling to end on the second */
193 		if (scrol)
194 			delay_msecs -= scroll_msecs;
195 		if (delay_msecs > 0) {
196 			if (snooze(delay_msecs) == 1)
197 				goto out;
198 		}
199 	} while (forever ? 1 : --n);
200 
201 out:
202 	cleanup();
203 
204 	return (0);
205 }
206 
207 static int
208 snooze(long msecs)
209 {
210 	static struct pollfd pfd = {
211 		.fd = STDIN_FILENO,
212 		.events = POLLIN,
213 	};
214 	struct timespec ts;
215 	char c;
216 	int rv;
217 
218 	if (msecs < 1000) {
219 		ts.tv_sec = 0;
220 		ts.tv_nsec = 1000000 * msecs;
221 	} else {
222 		ts.tv_sec = msecs / 1000;
223 		ts.tv_nsec = 1000000 * (msecs % 1000);
224 	}
225 
226 	rv = ppoll(&pfd, 1, &ts, NULL);
227 	if (rv == 1) {
228 		read(pfd.fd, &c, 1);
229 		if (c == 'q')
230 			return (1);
231 	}
232 
233 	if (sigtermed) {
234 		cleanup();
235 		errx(1, "terminated by signal %d", (int)sigtermed);
236 	}
237 
238 	return (0);
239 }
240 
241 static void
242 cleanup(void)
243 {
244 	standend();
245 	clear();
246 	refresh();
247 	endwin();
248 }
249 
250 static void
251 draw_row(int i, int s)
252 {
253 	long a, t;
254 	int j;
255 
256 	if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) {
257 		for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
258 			if (a & t) {
259 				if (!(a & (t << 1))) {
260 					move(ybase + i, xbase + 2 * j);
261 				}
262 				addstr("  ");
263 			}
264 		}
265 	}
266 	if (!s) {
267 		old[i] = new[i];
268 	}
269 }
270 
271 static void
272 set(int t, int n)
273 {
274 	static short disp[11] = {
275 		075557, 011111, 071747, 071717, 055711,
276 		074717, 074757, 071111, 075757, 075717, 002020
277 	};
278 	int i, m;
279 
280 	m = 7 << n;
281 	for (i = 0; i < 5; i++) {
282 		next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n;
283 		mask |= (next[i] ^ old[i]) & m;
284 	}
285 	if (mask & m)
286 		mask |= m;
287 }
288 
289 static void
290 standt(int on)
291 {
292 	if (on) {
293 		if (hascolor) {
294 			attron(COLOR_PAIR(1));
295 		} else {
296 			attron(A_STANDOUT);
297 		}
298 	} else {
299 		if (hascolor) {
300 			attron(COLOR_PAIR(2));
301 		} else {
302 			attroff(A_STANDOUT);
303 		}
304 	}
305 }
306 
307 static void
308 usage(void)
309 {
310 	fprintf(stderr, "usage: grdc [-s] [-d msecs] [n]\n");
311 	exit(1);
312 }
313