1 | /*
|
---|
2 | * Grand digital clock for curses compatible terminals
|
---|
3 | * Usage: gdc [-s] [n] -- run for n seconds (default infinity)
|
---|
4 | * Flags: -s: scroll
|
---|
5 | *
|
---|
6 | * modified 10-18-89 for curses (jrl)
|
---|
7 | * 10-18-89 added signal handling
|
---|
8 | *
|
---|
9 | * $Id: gdc.c,v 1.26 2005/05/28 21:39:39 tom Exp $
|
---|
10 | */
|
---|
11 |
|
---|
12 | #include <test.priv.h>
|
---|
13 |
|
---|
14 | #include <time.h>
|
---|
15 |
|
---|
16 | #define YBASE 10
|
---|
17 | #define XBASE 10
|
---|
18 | #define XLENGTH 54
|
---|
19 | #define YDEPTH 5
|
---|
20 |
|
---|
21 | #define PAIR_DIGITS 1
|
---|
22 | #define PAIR_OTHERS 2
|
---|
23 | #define PAIR_FRAMES 3
|
---|
24 |
|
---|
25 | static short disp[11] =
|
---|
26 | {
|
---|
27 | 075557, 011111, 071747, 071717, 055711,
|
---|
28 | 074717, 074757, 071111, 075757, 075717, 002020
|
---|
29 | };
|
---|
30 | static long older[6], next[6], newer[6], mask;
|
---|
31 |
|
---|
32 | static int sigtermed = 0;
|
---|
33 | static bool redirected = FALSE;
|
---|
34 | static bool hascolor = FALSE;
|
---|
35 |
|
---|
36 | static RETSIGTYPE
|
---|
37 | sighndl(int signo)
|
---|
38 | {
|
---|
39 | signal(signo, sighndl);
|
---|
40 | sigtermed = signo;
|
---|
41 | if (redirected) {
|
---|
42 | endwin();
|
---|
43 | ExitProgram(EXIT_FAILURE);
|
---|
44 | }
|
---|
45 | }
|
---|
46 |
|
---|
47 | static void
|
---|
48 | drawbox(bool scrolling)
|
---|
49 | {
|
---|
50 | chtype bottom[XLENGTH + 1];
|
---|
51 | int n;
|
---|
52 |
|
---|
53 | if (hascolor)
|
---|
54 | attrset(COLOR_PAIR(PAIR_FRAMES));
|
---|
55 |
|
---|
56 | mvaddch(YBASE - 1, XBASE - 1, ACS_ULCORNER);
|
---|
57 | hline(ACS_HLINE, XLENGTH);
|
---|
58 | mvaddch(YBASE - 1, XBASE + XLENGTH, ACS_URCORNER);
|
---|
59 |
|
---|
60 | mvaddch(YBASE + YDEPTH, XBASE - 1, ACS_LLCORNER);
|
---|
61 | mvinchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH);
|
---|
62 | for (n = 0; n < XLENGTH; n++) {
|
---|
63 | if (!scrolling)
|
---|
64 | bottom[n] &= ~A_COLOR;
|
---|
65 | bottom[n] = ACS_HLINE | (bottom[n] & (A_ATTRIBUTES | A_COLOR));
|
---|
66 | }
|
---|
67 | mvaddchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH);
|
---|
68 | mvaddch(YBASE + YDEPTH, XBASE + XLENGTH, ACS_LRCORNER);
|
---|
69 |
|
---|
70 | move(YBASE, XBASE - 1);
|
---|
71 | vline(ACS_VLINE, YDEPTH);
|
---|
72 |
|
---|
73 | move(YBASE, XBASE + XLENGTH);
|
---|
74 | vline(ACS_VLINE, YDEPTH);
|
---|
75 |
|
---|
76 | if (hascolor)
|
---|
77 | attrset(COLOR_PAIR(PAIR_OTHERS));
|
---|
78 | }
|
---|
79 |
|
---|
80 | static void
|
---|
81 | standt(int on)
|
---|
82 | {
|
---|
83 | if (on) {
|
---|
84 | if (hascolor) {
|
---|
85 | attron(COLOR_PAIR(PAIR_DIGITS));
|
---|
86 | } else {
|
---|
87 | attron(A_STANDOUT);
|
---|
88 | }
|
---|
89 | } else {
|
---|
90 | if (hascolor) {
|
---|
91 | attron(COLOR_PAIR(PAIR_OTHERS));
|
---|
92 | } else {
|
---|
93 | attroff(A_STANDOUT);
|
---|
94 | }
|
---|
95 | }
|
---|
96 | }
|
---|
97 |
|
---|
98 | static void
|
---|
99 | set(int t, int n)
|
---|
100 | {
|
---|
101 | int i, m;
|
---|
102 |
|
---|
103 | m = 7 << n;
|
---|
104 | for (i = 0; i < 5; i++) {
|
---|
105 | next[i] |= ((disp[t] >> ((4 - i) * 3)) & 07) << n;
|
---|
106 | mask |= (next[i] ^ older[i]) & m;
|
---|
107 | }
|
---|
108 | if (mask & m)
|
---|
109 | mask |= m;
|
---|
110 | }
|
---|
111 |
|
---|
112 | static void
|
---|
113 | usage(void)
|
---|
114 | {
|
---|
115 | static const char *msg[] =
|
---|
116 | {
|
---|
117 | "Usage: gdc [options] [count]"
|
---|
118 | ,""
|
---|
119 | ,"Options:"
|
---|
120 | ," -n redirect input to /dev/null"
|
---|
121 | ," -s scroll each number into place, rather than flipping"
|
---|
122 | ,""
|
---|
123 | ,"If you specify a count, gdc runs for that number of seconds"
|
---|
124 | };
|
---|
125 | unsigned j;
|
---|
126 | for (j = 0; j < SIZEOF(msg); j++)
|
---|
127 | fprintf(stderr, "%s\n", msg[j]);
|
---|
128 | ExitProgram(EXIT_FAILURE);
|
---|
129 | }
|
---|
130 |
|
---|
131 | int
|
---|
132 | main(int argc, char *argv[])
|
---|
133 | {
|
---|
134 | time_t now;
|
---|
135 | struct tm *tm;
|
---|
136 | long t, a;
|
---|
137 | int i, j, s, k;
|
---|
138 | int count = 0;
|
---|
139 | FILE *ofp = stdout;
|
---|
140 | FILE *ifp = stdin;
|
---|
141 | bool scrol = FALSE;
|
---|
142 |
|
---|
143 | setlocale(LC_ALL, "");
|
---|
144 |
|
---|
145 | signal(SIGINT, sighndl);
|
---|
146 | signal(SIGTERM, sighndl);
|
---|
147 |
|
---|
148 | while ((k = getopt(argc, argv, "sn")) != EOF) {
|
---|
149 | switch (k) {
|
---|
150 | case 's':
|
---|
151 | scrol = TRUE;
|
---|
152 | break;
|
---|
153 | case 'n':
|
---|
154 | ifp = fopen("/dev/null", "r");
|
---|
155 | redirected = TRUE;
|
---|
156 | break;
|
---|
157 | default:
|
---|
158 | usage();
|
---|
159 | }
|
---|
160 | }
|
---|
161 | if (optind < argc) {
|
---|
162 | count = atoi(argv[optind++]);
|
---|
163 | }
|
---|
164 | if (optind < argc)
|
---|
165 | usage();
|
---|
166 |
|
---|
167 | if (redirected) {
|
---|
168 | char *name = getenv("TERM");
|
---|
169 | if (name == 0
|
---|
170 | || newterm(name, ofp, ifp) == 0) {
|
---|
171 | fprintf(stderr, "cannot open terminal\n");
|
---|
172 | ExitProgram(EXIT_FAILURE);
|
---|
173 | }
|
---|
174 |
|
---|
175 | } else {
|
---|
176 | initscr();
|
---|
177 | }
|
---|
178 | cbreak();
|
---|
179 | noecho();
|
---|
180 | nodelay(stdscr, 1);
|
---|
181 | curs_set(0);
|
---|
182 |
|
---|
183 | hascolor = has_colors();
|
---|
184 |
|
---|
185 | if (hascolor) {
|
---|
186 | int bg = COLOR_BLACK;
|
---|
187 | start_color();
|
---|
188 | #if HAVE_USE_DEFAULT_COLORS
|
---|
189 | if (use_default_colors() == OK)
|
---|
190 | bg = -1;
|
---|
191 | #endif
|
---|
192 | init_pair(PAIR_DIGITS, COLOR_BLACK, COLOR_RED);
|
---|
193 | init_pair(PAIR_OTHERS, COLOR_RED, bg);
|
---|
194 | init_pair(PAIR_FRAMES, COLOR_WHITE, bg);
|
---|
195 | attrset(COLOR_PAIR(PAIR_OTHERS));
|
---|
196 | }
|
---|
197 |
|
---|
198 | restart:
|
---|
199 | for (j = 0; j < 5; j++)
|
---|
200 | older[j] = newer[j] = next[j] = 0;
|
---|
201 |
|
---|
202 | clear();
|
---|
203 | drawbox(FALSE);
|
---|
204 |
|
---|
205 | do {
|
---|
206 | char buf[30];
|
---|
207 |
|
---|
208 | time(&now);
|
---|
209 | tm = localtime(&now);
|
---|
210 |
|
---|
211 | mask = 0;
|
---|
212 | set(tm->tm_sec % 10, 0);
|
---|
213 | set(tm->tm_sec / 10, 4);
|
---|
214 | set(tm->tm_min % 10, 10);
|
---|
215 | set(tm->tm_min / 10, 14);
|
---|
216 | set(tm->tm_hour % 10, 20);
|
---|
217 | set(tm->tm_hour / 10, 24);
|
---|
218 | set(10, 7);
|
---|
219 | set(10, 17);
|
---|
220 |
|
---|
221 | for (k = 0; k < 6; k++) {
|
---|
222 | if (scrol) {
|
---|
223 | for (i = 0; i < 5; i++)
|
---|
224 | newer[i] = (newer[i] & ~mask) | (newer[i + 1] & mask);
|
---|
225 | newer[5] = (newer[5] & ~mask) | (next[k] & mask);
|
---|
226 | } else
|
---|
227 | newer[k] = (newer[k] & ~mask) | (next[k] & mask);
|
---|
228 | next[k] = 0;
|
---|
229 | for (s = 1; s >= 0; s--) {
|
---|
230 | standt(s);
|
---|
231 | for (i = 0; i < 6; i++) {
|
---|
232 | if ((a = (newer[i] ^ older[i]) & (s ? newer : older)[i])
|
---|
233 | != 0) {
|
---|
234 | for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
|
---|
235 | if (a & t) {
|
---|
236 | if (!(a & (t << 1))) {
|
---|
237 | move(YBASE + i, XBASE + 2 * j);
|
---|
238 | }
|
---|
239 | addstr(" ");
|
---|
240 | }
|
---|
241 | }
|
---|
242 | }
|
---|
243 | if (!s) {
|
---|
244 | older[i] = newer[i];
|
---|
245 | }
|
---|
246 | }
|
---|
247 | if (!s) {
|
---|
248 | if (scrol)
|
---|
249 | drawbox(TRUE);
|
---|
250 | refresh();
|
---|
251 | /*
|
---|
252 | * If we're scrolling, space out the refreshes to fake
|
---|
253 | * movement. That's 7 frames, or 6 intervals, which would
|
---|
254 | * be 166 msec if we spread it out over a second. It looks
|
---|
255 | * better (but will work on a slow terminal, e.g., less
|
---|
256 | * than 9600bd) to squeeze that into a half-second, and use
|
---|
257 | * half of 170 msec to ensure that the program doesn't eat
|
---|
258 | * a lot of time when asking what time it is, at the top of
|
---|
259 | * this loop -T.Dickey
|
---|
260 | */
|
---|
261 | if (scrol)
|
---|
262 | napms(85);
|
---|
263 | }
|
---|
264 | }
|
---|
265 | }
|
---|
266 |
|
---|
267 | /* this depends on the detailed format of ctime(3) */
|
---|
268 | (void) strcpy(buf, ctime(&now));
|
---|
269 | (void) strcpy(buf + 10, buf + 19);
|
---|
270 | mvaddstr(16, 30, buf);
|
---|
271 |
|
---|
272 | move(6, 0);
|
---|
273 | drawbox(FALSE);
|
---|
274 | refresh();
|
---|
275 |
|
---|
276 | /*
|
---|
277 | * If we're not scrolling, wait 1000 msec (1 sec). Use napms() rather
|
---|
278 | * than sleep() because the latter does odd things on some systems,
|
---|
279 | * e.g., suspending output as well.
|
---|
280 | */
|
---|
281 | if (scrol)
|
---|
282 | napms(500);
|
---|
283 | else
|
---|
284 | napms(1000);
|
---|
285 |
|
---|
286 | /*
|
---|
287 | * This is a safe way to check if we're interrupted - making the signal
|
---|
288 | * handler set a flag that we can check. Since we're running
|
---|
289 | * nodelay(), the wgetch() call returns immediately, and in particular
|
---|
290 | * will return an error if interrupted. This works only if we can
|
---|
291 | * read from the input, of course.
|
---|
292 | */
|
---|
293 | switch (wgetch(stdscr)) {
|
---|
294 | case 'q':
|
---|
295 | count = 1;
|
---|
296 | break;
|
---|
297 | case 's':
|
---|
298 | nodelay(stdscr, FALSE);
|
---|
299 | break;
|
---|
300 | case ' ':
|
---|
301 | nodelay(stdscr, TRUE);
|
---|
302 | break;
|
---|
303 | #ifdef KEY_RESIZE
|
---|
304 | case KEY_RESIZE:
|
---|
305 | #endif
|
---|
306 | case '?':
|
---|
307 | goto restart;
|
---|
308 | case ERR:
|
---|
309 | if (sigtermed) {
|
---|
310 | standend();
|
---|
311 | endwin();
|
---|
312 | fprintf(stderr, "gdc terminated by signal %d\n", sigtermed);
|
---|
313 | ExitProgram(EXIT_FAILURE);
|
---|
314 | }
|
---|
315 | /* FALLTHRU */
|
---|
316 | default:
|
---|
317 | continue;
|
---|
318 | }
|
---|
319 | } while (--count);
|
---|
320 | standend();
|
---|
321 | endwin();
|
---|
322 | ExitProgram(EXIT_SUCCESS);
|
---|
323 | }
|
---|