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