83b3fd55a8bf567e639c1146d41bcda9e9bc2f2b
[ncurses.git] / test / gdc.c
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 }