ncurses 6.0 - patch 20150822
[ncurses.git] / test / gdc.c
1 /****************************************************************************
2  * Copyright (c) 1998-2014,2015 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] [-t hh:mm:ss] [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.44 2015/07/04 21:28:28 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 void
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 check_term(void)
76 {
77     if (sigtermed) {
78         (void) standend();
79         endwin();
80         fprintf(stderr, "gdc terminated by signal %d\n", sigtermed);
81         ExitProgram(EXIT_FAILURE);
82     }
83 }
84
85 static void
86 drawbox(bool scrolling)
87 {
88     chtype bottom[XLENGTH + 1];
89     int n;
90
91     if (hascolor)
92         (void) attrset(AttrArg(COLOR_PAIR(PAIR_FRAMES), 0));
93
94     MvAddCh(YBASE - 1, XBASE - 1, ACS_ULCORNER);
95     hline(ACS_HLINE, XLENGTH);
96     MvAddCh(YBASE - 1, XBASE + XLENGTH, ACS_URCORNER);
97
98     MvAddCh(YBASE + YDEPTH, XBASE - 1, ACS_LLCORNER);
99     if ((mvinchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH)) != ERR) {
100         for (n = 0; n < XLENGTH; n++) {
101             if (!scrolling)
102                 bottom[n] &= ~A_COLOR;
103             bottom[n] = ACS_HLINE | (bottom[n] & (A_ATTRIBUTES | A_COLOR));
104         }
105         (void) mvaddchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH);
106     }
107     MvAddCh(YBASE + YDEPTH, XBASE + XLENGTH, ACS_LRCORNER);
108
109     move(YBASE, XBASE - 1);
110     vline(ACS_VLINE, YDEPTH);
111
112     move(YBASE, XBASE + XLENGTH);
113     vline(ACS_VLINE, YDEPTH);
114
115     if (hascolor)
116         (void) attrset(AttrArg(COLOR_PAIR(PAIR_OTHERS), 0));
117 }
118
119 static void
120 standt(int on)
121 {
122     if (on) {
123         if (hascolor) {
124             attron(COLOR_PAIR(PAIR_DIGITS));
125         } else {
126             attron(A_STANDOUT);
127         }
128     } else {
129         if (hascolor) {
130             attron(COLOR_PAIR(PAIR_OTHERS));
131         } else {
132             attroff(A_STANDOUT);
133         }
134     }
135 }
136
137 static void
138 set(int t, int n)
139 {
140     int i, m;
141
142     m = 7 << n;
143     for (i = 0; i < 5; i++) {
144         next[i] |= ((disp[t] >> ((4 - i) * 3)) & 07) << n;
145         mask |= (next[i] ^ older[i]) & m;
146     }
147     if (mask & m)
148         mask |= m;
149 }
150
151 static void
152 usage(void)
153 {
154     static const char *msg[] =
155     {
156         "Usage: gdc [options] [count]"
157         ,""
158         ,"Options:"
159         ,"  -n  redirect input to /dev/null"
160         ,"  -s  scroll each number into place, rather than flipping"
161         ,"  -t hh:mm:ss specify starting time (default is ``now'')"
162         ,""
163         ,"If you specify a count, gdc runs for that number of seconds"
164     };
165     unsigned j;
166     for (j = 0; j < SIZEOF(msg); j++)
167         fprintf(stderr, "%s\n", msg[j]);
168     ExitProgram(EXIT_FAILURE);
169 }
170
171 static time_t
172 parse_time(const char *value)
173 {
174     int hh, mm, ss;
175     int check;
176     time_t result;
177     char c;
178     struct tm *tm;
179
180     if (sscanf(value, "%d:%d:%d%c", &hh, &mm, &ss, &c) != 3) {
181         if (sscanf(value, "%02d%02d%02d%c", &hh, &mm, &ss, &c) != 3) {
182             usage();
183         }
184     }
185
186     if ((hh < 0) || (hh >= 24) ||
187         (mm < 0) || (mm >= 60) ||
188         (ss < 0) || (ss >= 60)) {
189         usage();
190     }
191
192     /* adjust so that the localtime in the main loop will give usable time */
193     result = (hh * 3600) + ((mm * 60) + ss);
194     for (check = 0; check < 24; ++check) {
195         tm = localtime(&result);
196         if (tm->tm_hour == hh)
197             break;
198         result += 3600;
199     }
200
201     if (tm->tm_hour != hh) {
202         fprintf(stderr, "Cannot find local time for %s!\n", value);
203         usage();
204     }
205     return result;
206 }
207
208 int
209 main(int argc, char *argv[])
210 {
211     time_t now;
212     struct tm *tm;
213     long t, a;
214     int i, j, s, k;
215     int count = 0;
216     FILE *ofp = stdout;
217     FILE *ifp = stdin;
218     bool smooth = FALSE;
219     bool stages = FALSE;
220     time_t starts = 0;
221
222     setlocale(LC_ALL, "");
223
224     CATCHALL(sighndl);
225
226     while ((k = getopt(argc, argv, "nst:")) != -1) {
227         switch (k) {
228         case 'n':
229             ifp = fopen("/dev/null", "r");
230             redirected = TRUE;
231             break;
232         case 's':
233             smooth = TRUE;
234             break;
235         case 't':
236             starts = parse_time(optarg);
237             break;
238         default:
239             usage();
240         }
241     }
242     if (optind < argc) {
243         count = atoi(argv[optind++]);
244         assert(count >= 0);
245     }
246     if (optind < argc)
247         usage();
248
249     if (redirected) {
250         char *name = getenv("TERM");
251         if (name == 0
252             || newterm(name, ofp, ifp) == 0) {
253             fprintf(stderr, "cannot open terminal\n");
254             ExitProgram(EXIT_FAILURE);
255         }
256
257     } else {
258         initscr();
259     }
260     cbreak();
261     noecho();
262     nodelay(stdscr, 1);
263     curs_set(0);
264
265     hascolor = has_colors();
266
267     if (hascolor) {
268         short bg = COLOR_BLACK;
269         start_color();
270 #if HAVE_USE_DEFAULT_COLORS
271         if (use_default_colors() == OK)
272             bg = -1;
273 #endif
274         init_pair(PAIR_DIGITS, COLOR_BLACK, COLOR_RED);
275         init_pair(PAIR_OTHERS, COLOR_RED, bg);
276         init_pair(PAIR_FRAMES, COLOR_WHITE, bg);
277         (void) attrset(AttrArg(COLOR_PAIR(PAIR_OTHERS), 0));
278     }
279
280   restart:
281     for (j = 0; j < 5; j++)
282         older[j] = newer[j] = next[j] = 0;
283
284     clear();
285     drawbox(FALSE);
286
287     do {
288         char buf[40];
289
290         if (starts != 0) {
291             now = ++starts;
292         } else {
293             time(&now);
294         }
295         tm = localtime(&now);
296
297         mask = 0;
298         set(tm->tm_sec % 10, 0);
299         set(tm->tm_sec / 10, 4);
300         set(tm->tm_min % 10, 10);
301         set(tm->tm_min / 10, 14);
302         set(tm->tm_hour % 10, 20);
303         set(tm->tm_hour / 10, 24);
304         set(10, 7);
305         set(10, 17);
306
307         for (k = 0; k < 6; k++) {
308             if (smooth) {
309                 for (i = 0; i < 5; i++)
310                     newer[i] = (newer[i] & ~mask) | (newer[i + 1] & mask);
311                 newer[5] = (newer[5] & ~mask) | (next[k] & mask);
312             } else {
313                 newer[k] = (newer[k] & ~mask) | (next[k] & mask);
314             }
315             next[k] = 0;
316             for (s = 1; s >= 0; s--) {
317                 standt(s);
318                 for (i = 0; i < 6; i++) {
319                     if ((a = (newer[i] ^ older[i]) & (s ? newer : older)[i])
320                         != 0) {
321                         for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
322                             if (a & t) {
323                                 if (!(a & (t << 1))) {
324                                     move(YBASE + i, XBASE + 2 * j);
325                                 }
326                                 addstr("  ");
327                             }
328                         }
329                     }
330                     if (!s) {
331                         older[i] = newer[i];
332                     }
333                 }
334                 if (!s) {
335                     if (smooth)
336                         drawbox(TRUE);
337                     refresh();
338                     /*
339                      * If we're scrolling, space out the refreshes to fake
340                      * movement.  That's 7 frames, or 6 intervals, which would
341                      * be 166 msec if we spread it out over a second.  It looks
342                      * better (but will work on a slow terminal, e.g., less
343                      * than 9600bd) to squeeze that into a half-second, and use
344                      * half of 170 msec to ensure that the program doesn't eat
345                      * a lot of time when asking what time it is, at the top of
346                      * this loop -T.Dickey
347                      */
348                     if (smooth)
349                         napms(85);
350                     if (stages) {
351                         stages = FALSE;
352                         switch (wgetch(stdscr)) {
353                         case 'q':
354                             count = 1;
355                             break;
356                         case 'S':
357                             stages = TRUE;
358                             /* FALLTHRU */
359                         case 's':
360                             nodelay(stdscr, FALSE);
361                             break;
362                         case ' ':
363                             nodelay(stdscr, TRUE);
364                             break;
365 #ifdef KEY_RESIZE
366                         case KEY_RESIZE:
367 #endif
368                         case '?':
369                             goto restart;
370                         case ERR:
371                             check_term();
372                             /* FALLTHRU */
373                         default:
374                             continue;
375                         }
376                     }
377                 }
378             }
379         }
380
381         /* this depends on the detailed format of ctime(3) */
382         (void) strncpy(buf, ctime(&now), (size_t) 30);
383         {
384             char *d2 = buf + 10;
385             char *s2 = buf + 19;
386             while ((*d2++ = *s2++) != '\0') ;
387         }
388         MvAddStr(16, 30, buf);
389
390         move(6, 0);
391         drawbox(FALSE);
392         refresh();
393
394         /*
395          * If we're not smooth-scrolling, wait 1000 msec (1 sec).  Use napms()
396          * rather than sleep() because the latter does odd things on some
397          * systems, e.g., suspending output as well.
398          */
399         if (smooth)
400             napms(500);
401         else
402             napms(1000);
403
404         /*
405          * This is a safe way to check if we're interrupted - making the signal
406          * handler set a flag that we can check.  Since we're running
407          * nodelay(), the wgetch() call returns immediately, and in particular
408          * will return an error if interrupted.  This works only if we can
409          * read from the input, of course.
410          */
411         stages = FALSE;
412         switch (wgetch(stdscr)) {
413         case 'q':
414             count = 1;
415             break;
416         case 'S':
417             stages = TRUE;
418             /* FALLTHRU */
419         case 's':
420             nodelay(stdscr, FALSE);
421             break;
422         case ' ':
423             nodelay(stdscr, TRUE);
424             break;
425 #ifdef KEY_RESIZE
426         case KEY_RESIZE:
427 #endif
428         case '?':
429             goto restart;
430         case ERR:
431             check_term();
432             /* FALLTHRU */
433         default:
434             continue;
435         }
436     } while (--count);
437     (void) standend();
438     endwin();
439     ExitProgram(EXIT_SUCCESS);
440 }