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
sighndl(int signo)49 sighndl(int signo)
50 {
51
52 sigtermed = signo;
53 }
54
55 int
main(int argc,char * argv[])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
set(int t,int n)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
standt(int on)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
movto(int line,int col)262 movto(int line, int col)
263 {
264 move(line, col);
265 }
266
267 static void
usage(void)268 usage(void)
269 {
270
271 (void)fprintf(stderr, "usage: grdc [-st] [n]\n");
272 exit(1);
273 }
274