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