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