ncurses 5.9 - patch 20121229
[ncurses.git] / test / gdc.c
1 /****************************************************************************
2  * Copyright (c) 1998-2010,2012 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * Grand digital clock for curses compatible terminals
30  * Usage: gdc [-s] [n]   -- run for n seconds (default infinity)
31  * Flags: -s: scroll
32  *
33  * modified 10-18-89 for curses (jrl)
34  * 10-18-89 added signal handling
35  *
36  * $Id: gdc.c,v 1.36 2012/12/29 22:51:57 tom Exp $
37  */
38
39 #include <test.priv.h>
40
41 #include <time.h>
42
43 #define YBASE   10
44 #define XBASE   10
45 #define XLENGTH 54
46 #define YDEPTH  5
47
48 #define PAIR_DIGITS 1
49 #define PAIR_OTHERS 2
50 #define PAIR_FRAMES 3
51
52 static short disp[11] =
53 {
54     075557, 011111, 071747, 071717, 055711,
55     074717, 074757, 071111, 075757, 075717, 002020
56 };
57 static long older[6], next[6], newer[6], mask;
58
59 static int sigtermed = 0;
60 static bool redirected = FALSE;
61 static bool hascolor = FALSE;
62
63 static RETSIGTYPE
64 sighndl(int signo)
65 {
66     signal(signo, sighndl);
67     sigtermed = signo;
68     if (redirected) {
69         endwin();
70         ExitProgram(EXIT_FAILURE);
71     }
72 }
73
74 static void
75 drawbox(bool scrolling)
76 {
77     chtype bottom[XLENGTH + 1];
78     int n;
79
80     if (hascolor)
81         (void) attrset((attr_t) COLOR_PAIR(PAIR_FRAMES));
82
83     MvAddCh(YBASE - 1, XBASE - 1, ACS_ULCORNER);
84     hline(ACS_HLINE, XLENGTH);
85     MvAddCh(YBASE - 1, XBASE + XLENGTH, ACS_URCORNER);
86
87     MvAddCh(YBASE + YDEPTH, XBASE - 1, ACS_LLCORNER);
88     if ((mvinchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH)) != ERR) {
89         for (n = 0; n < XLENGTH; n++) {
90             if (!scrolling)
91                 bottom[n] &= ~A_COLOR;
92             bottom[n] = ACS_HLINE | (bottom[n] & (A_ATTRIBUTES | A_COLOR));
93         }
94         (void) mvaddchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH);
95     }
96     MvAddCh(YBASE + YDEPTH, XBASE + XLENGTH, ACS_LRCORNER);
97
98     move(YBASE, XBASE - 1);
99     vline(ACS_VLINE, YDEPTH);
100
101     move(YBASE, XBASE + XLENGTH);
102     vline(ACS_VLINE, YDEPTH);
103
104     if (hascolor)
105         (void) attrset((attr_t) COLOR_PAIR(PAIR_OTHERS));
106 }
107
108 static void
109 standt(int on)
110 {
111     if (on) {
112         if (hascolor) {
113             attron((attr_t) COLOR_PAIR(PAIR_DIGITS));
114         } else {
115             attron(A_STANDOUT);
116         }
117     } else {
118         if (hascolor) {
119             attron((attr_t) COLOR_PAIR(PAIR_OTHERS));
120         } else {
121             attroff(A_STANDOUT);
122         }
123     }
124 }
125
126 static void
127 set(int t, int n)
128 {
129     int i, m;
130
131     m = 7 << n;
132     for (i = 0; i < 5; i++) {
133         next[i] |= ((disp[t] >> ((4 - i) * 3)) & 07) << n;
134         mask |= (next[i] ^ older[i]) & m;
135     }
136     if (mask & m)
137         mask |= m;
138 }
139
140 static void
141 usage(void)
142 {
143     static const char *msg[] =
144     {
145         "Usage: gdc [options] [count]"
146         ,""
147         ,"Options:"
148         ,"  -n  redirect input to /dev/null"
149         ,"  -s  scroll each number into place, rather than flipping"
150         ,""
151         ,"If you specify a count, gdc runs for that number of seconds"
152     };
153     unsigned j;
154     for (j = 0; j < SIZEOF(msg); j++)
155         fprintf(stderr, "%s\n", msg[j]);
156     ExitProgram(EXIT_FAILURE);
157 }
158
159 int
160 main(int argc, char *argv[])
161 {
162     time_t now;
163     struct tm *tm;
164     long t, a;
165     int i, j, s, k;
166     int count = 0;
167     FILE *ofp = stdout;
168     FILE *ifp = stdin;
169     bool scrol = FALSE;
170
171     setlocale(LC_ALL, "");
172
173     CATCHALL(sighndl);
174
175     while ((k = getopt(argc, argv, "sn")) != -1) {
176         switch (k) {
177         case 's':
178             scrol = TRUE;
179             break;
180         case 'n':
181             ifp = fopen("/dev/null", "r");
182             redirected = TRUE;
183             break;
184         default:
185             usage();
186         }
187     }
188     if (optind < argc) {
189         count = atoi(argv[optind++]);
190         assert(count >= 0);
191     }
192     if (optind < argc)
193         usage();
194
195     if (redirected) {
196         char *name = getenv("TERM");
197         if (name == 0
198             || newterm(name, ofp, ifp) == 0) {
199             fprintf(stderr, "cannot open terminal\n");
200             ExitProgram(EXIT_FAILURE);
201         }
202
203     } else {
204         initscr();
205     }
206     cbreak();
207     noecho();
208     nodelay(stdscr, 1);
209     curs_set(0);
210
211     hascolor = has_colors();
212
213     if (hascolor) {
214         short bg = COLOR_BLACK;
215         start_color();
216 #if HAVE_USE_DEFAULT_COLORS
217         if (use_default_colors() == OK)
218             bg = -1;
219 #endif
220         init_pair(PAIR_DIGITS, COLOR_BLACK, COLOR_RED);
221         init_pair(PAIR_OTHERS, COLOR_RED, bg);
222         init_pair(PAIR_FRAMES, COLOR_WHITE, bg);
223         (void) attrset((attr_t) COLOR_PAIR(PAIR_OTHERS));
224     }
225
226   restart:
227     for (j = 0; j < 5; j++)
228         older[j] = newer[j] = next[j] = 0;
229
230     clear();
231     drawbox(FALSE);
232
233     do {
234         char buf[40];
235
236         time(&now);
237         tm = localtime(&now);
238
239         mask = 0;
240         set(tm->tm_sec % 10, 0);
241         set(tm->tm_sec / 10, 4);
242         set(tm->tm_min % 10, 10);
243         set(tm->tm_min / 10, 14);
244         set(tm->tm_hour % 10, 20);
245         set(tm->tm_hour / 10, 24);
246         set(10, 7);
247         set(10, 17);
248
249         for (k = 0; k < 6; k++) {
250             if (scrol) {
251                 for (i = 0; i < 5; i++)
252                     newer[i] = (newer[i] & ~mask) | (newer[i + 1] & mask);
253                 newer[5] = (newer[5] & ~mask) | (next[k] & mask);
254             } else
255                 newer[k] = (newer[k] & ~mask) | (next[k] & mask);
256             next[k] = 0;
257             for (s = 1; s >= 0; s--) {
258                 standt(s);
259                 for (i = 0; i < 6; i++) {
260                     if ((a = (newer[i] ^ older[i]) & (s ? newer : older)[i])
261                         != 0) {
262                         for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
263                             if (a & t) {
264                                 if (!(a & (t << 1))) {
265                                     move(YBASE + i, XBASE + 2 * j);
266                                 }
267                                 addstr("  ");
268                             }
269                         }
270                     }
271                     if (!s) {
272                         older[i] = newer[i];
273                     }
274                 }
275                 if (!s) {
276                     if (scrol)
277                         drawbox(TRUE);
278                     refresh();
279                     /*
280                      * If we're scrolling, space out the refreshes to fake
281                      * movement.  That's 7 frames, or 6 intervals, which would
282                      * be 166 msec if we spread it out over a second.  It looks
283                      * better (but will work on a slow terminal, e.g., less
284                      * than 9600bd) to squeeze that into a half-second, and use
285                      * half of 170 msec to ensure that the program doesn't eat
286                      * a lot of time when asking what time it is, at the top of
287                      * this loop -T.Dickey
288                      */
289                     if (scrol)
290                         napms(85);
291                 }
292             }
293         }
294
295         /* this depends on the detailed format of ctime(3) */
296         (void) strncpy(buf, ctime(&now), 30);
297         (void) strcpy(buf + 10, buf + 19);
298         MvAddStr(16, 30, buf);
299
300         move(6, 0);
301         drawbox(FALSE);
302         refresh();
303
304         /*
305          * If we're not scrolling, wait 1000 msec (1 sec).  Use napms() rather
306          * than sleep() because the latter does odd things on some systems,
307          * e.g., suspending output as well.
308          */
309         if (scrol)
310             napms(500);
311         else
312             napms(1000);
313
314         /*
315          * This is a safe way to check if we're interrupted - making the signal
316          * handler set a flag that we can check.  Since we're running
317          * nodelay(), the wgetch() call returns immediately, and in particular
318          * will return an error if interrupted.  This works only if we can
319          * read from the input, of course.
320          */
321         switch (wgetch(stdscr)) {
322         case 'q':
323             count = 1;
324             break;
325         case 's':
326             nodelay(stdscr, FALSE);
327             break;
328         case ' ':
329             nodelay(stdscr, TRUE);
330             break;
331 #ifdef KEY_RESIZE
332         case KEY_RESIZE:
333 #endif
334         case '?':
335             goto restart;
336         case ERR:
337             if (sigtermed) {
338                 (void) standend();
339                 endwin();
340                 fprintf(stderr, "gdc terminated by signal %d\n", sigtermed);
341                 ExitProgram(EXIT_FAILURE);
342             }
343             /* FALLTHRU */
344         default:
345             continue;
346         }
347     } while (--count);
348     (void) standend();
349     endwin();
350     ExitProgram(EXIT_SUCCESS);
351 }