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