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