xref: /freebsd/usr.bin/grdc/grdc.c (revision d6b92ffa)
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$
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 
29 static struct timespec now;
30 static struct tm *tm;
31 
32 static short disp[11] = {
33 	075557, 011111, 071747, 071717, 055711,
34 	074717, 074757, 071111, 075757, 075717, 002020
35 };
36 static long old[6], next[6], new[6], mask;
37 
38 static volatile sig_atomic_t sigtermed;
39 
40 static int hascolor = 0;
41 
42 static void set(int, int);
43 static void standt(int);
44 static void movto(int, int);
45 static void sighndl(int);
46 static void usage(void);
47 
48 static void
49 sighndl(int signo)
50 {
51 
52 	sigtermed = signo;
53 }
54 
55 int
56 main(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 
229 static void
230 set(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 
243 static void
244 standt(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 
261 static void
262 movto(int line, int col)
263 {
264 	move(line, col);
265 }
266 
267 static void
268 usage(void)
269 {
270 
271 	(void)fprintf(stderr, "usage: grdc [-st] [n]\n");
272 	exit(1);
273 }
274