ncurses 5.6 - patch 20070915
[ncurses.git] / test / worm.c
1 /****************************************************************************
2  * Copyright (c) 1998-2006,2007 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
30          @@@        @@@    @@@@@@@@@@     @@@@@@@@@@@    @@@@@@@@@@@@
31          @@@        @@@   @@@@@@@@@@@@    @@@@@@@@@@@@   @@@@@@@@@@@@@
32          @@@        @@@  @@@@      @@@@   @@@@           @@@@ @@@  @@@@
33          @@@   @@   @@@  @@@        @@@   @@@            @@@  @@@   @@@
34          @@@  @@@@  @@@  @@@        @@@   @@@            @@@  @@@   @@@
35          @@@@ @@@@ @@@@  @@@        @@@   @@@            @@@  @@@   @@@
36           @@@@@@@@@@@@   @@@@      @@@@   @@@            @@@  @@@   @@@
37            @@@@  @@@@     @@@@@@@@@@@@    @@@            @@@  @@@   @@@
38             @@    @@       @@@@@@@@@@     @@@            @@@  @@@   @@@
39
40                                  Eric P. Scott
41                           Caltech High Energy Physics
42                                  October, 1980
43
44                 Hacks to turn this into a test frame for cursor movement:
45                         Eric S. Raymond <esr@snark.thyrsus.com>
46                                 January, 1995
47
48                 July 1995 (esr): worms is now in living color! :-)
49
50 Options:
51         -f                      fill screen with copies of 'WORM' at start.
52         -l <n>                  set worm length
53         -n <n>                  set number of worms
54         -t                      make worms leave droppings
55         -T <start> <end>        set trace interval
56         -S                      set single-stepping during trace interval
57         -N                      suppress cursor-movement optimization
58
59   This program makes a good torture-test for the ncurses cursor-optimization
60   code.  You can use -T to set the worm move interval over which movement
61   traces will be dumped.  The program stops and waits for one character of
62   input at the beginning and end of the interval.
63
64   $Id: worm.c,v 1.48 2007/09/15 21:42:16 tom Exp $
65 */
66
67 #include <test.priv.h>
68
69 static chtype flavor[] =
70 {
71     'O', '*', '#', '$', '%', '0', '@',
72 };
73 static const short xinc[] =
74 {
75     1, 1, 1, 0, -1, -1, -1, 0
76 }, yinc[] =
77 {
78     -1, 0, 1, 1, 1, 0, -1, -1
79 };
80
81 typedef struct worm {
82     int orientation;
83     int head;
84     short *xpos;
85     short *ypos;
86     chtype attrs;
87 } WORM;
88
89 static WORM worm[40];
90 static short **refs;
91
92 static const char *field;
93 static int length = 16, number = 3;
94 static chtype trail = ' ';
95
96 #ifdef TRACE
97 static int generation, trace_start, trace_end;
98 #endif /* TRACE */
99 /* *INDENT-OFF* */
100 static const struct options {
101     int nopts;
102     int opts[3];
103 } normal[8]={
104     { 3, { 7, 0, 1 } },
105     { 3, { 0, 1, 2 } },
106     { 3, { 1, 2, 3 } },
107     { 3, { 2, 3, 4 } },
108     { 3, { 3, 4, 5 } },
109     { 3, { 4, 5, 6 } },
110     { 3, { 5, 6, 7 } },
111     { 3, { 6, 7, 0 } }
112 }, upper[8]={
113     { 1, { 1, 0, 0 } },
114     { 2, { 1, 2, 0 } },
115     { 0, { 0, 0, 0 } },
116     { 0, { 0, 0, 0 } },
117     { 0, { 0, 0, 0 } },
118     { 2, { 4, 5, 0 } },
119     { 1, { 5, 0, 0 } },
120     { 2, { 1, 5, 0 } }
121 }, left[8]={
122     { 0, { 0, 0, 0 } },
123     { 0, { 0, 0, 0 } },
124     { 0, { 0, 0, 0 } },
125     { 2, { 2, 3, 0 } },
126     { 1, { 3, 0, 0 } },
127     { 2, { 3, 7, 0 } },
128     { 1, { 7, 0, 0 } },
129     { 2, { 7, 0, 0 } }
130 }, right[8]={
131     { 1, { 7, 0, 0 } },
132     { 2, { 3, 7, 0 } },
133     { 1, { 3, 0, 0 } },
134     { 2, { 3, 4, 0 } },
135     { 0, { 0, 0, 0 } },
136     { 0, { 0, 0, 0 } },
137     { 0, { 0, 0, 0 } },
138     { 2, { 6, 7, 0 } }
139 }, lower[8]={
140     { 0, { 0, 0, 0 } },
141     { 2, { 0, 1, 0 } },
142     { 1, { 1, 0, 0 } },
143     { 2, { 1, 5, 0 } },
144     { 1, { 5, 0, 0 } },
145     { 2, { 5, 6, 0 } },
146     { 0, { 0, 0, 0 } },
147     { 0, { 0, 0, 0 } }
148 }, upleft[8]={
149     { 0, { 0, 0, 0 } },
150     { 0, { 0, 0, 0 } },
151     { 0, { 0, 0, 0 } },
152     { 0, { 0, 0, 0 } },
153     { 0, { 0, 0, 0 } },
154     { 1, { 3, 0, 0 } },
155     { 2, { 1, 3, 0 } },
156     { 1, { 1, 0, 0 } }
157 }, upright[8]={
158     { 2, { 3, 5, 0 } },
159     { 1, { 3, 0, 0 } },
160     { 0, { 0, 0, 0 } },
161     { 0, { 0, 0, 0 } },
162     { 0, { 0, 0, 0 } },
163     { 0, { 0, 0, 0 } },
164     { 0, { 0, 0, 0 } },
165     { 1, { 5, 0, 0 } }
166 }, lowleft[8]={
167     { 3, { 7, 0, 1 } },
168     { 0, { 0, 0, 0 } },
169     { 0, { 0, 0, 0 } },
170     { 1, { 1, 0, 0 } },
171     { 2, { 1, 7, 0 } },
172     { 1, { 7, 0, 0 } },
173     { 0, { 0, 0, 0 } },
174     { 0, { 0, 0, 0 } }
175 }, lowright[8]={
176     { 0, { 0, 0, 0 } },
177     { 1, { 7, 0, 0 } },
178     { 2, { 5, 7, 0 } },
179     { 1, { 5, 0, 0 } },
180     { 0, { 0, 0, 0 } },
181     { 0, { 0, 0, 0 } },
182     { 0, { 0, 0, 0 } },
183     { 0, { 0, 0, 0 } }
184 };
185 /* *INDENT-ON* */
186
187 static void
188 cleanup(void)
189 {
190     standend();
191     refresh();
192     curs_set(1);
193     endwin();
194 }
195
196 static RETSIGTYPE
197 onsig(int sig GCC_UNUSED)
198 {
199     cleanup();
200     ExitProgram(EXIT_FAILURE);
201 }
202
203 static float
204 ranf(void)
205 {
206     long r = (rand() & 077777);
207     return ((float) r / 32768.);
208 }
209
210 static int
211 draw_worm(WINDOW *win, void *data)
212 {
213     WORM *w = (WORM *) data;
214     const struct options *op;
215
216     int x;
217     int y;
218     int h;
219
220     int bottom = LINES - 1;
221     int last = COLS - 1;
222
223     bool done = FALSE;
224
225     if ((x = w->xpos[h = w->head]) < 0) {
226         wmove(win, y = w->ypos[h] = bottom, x = w->xpos[h] = 0);
227         waddch(win, w->attrs);
228         refs[y][x]++;
229     } else {
230         y = w->ypos[h];
231     }
232
233     if (x > last)
234         x = last;
235     if (y > bottom)
236         y = bottom;
237
238     if (++h == length)
239         h = 0;
240
241     if (w->xpos[w->head = h] >= 0) {
242         int x1, y1;
243         x1 = w->xpos[h];
244         y1 = w->ypos[h];
245         if (y1 < LINES
246             && x1 < COLS
247             && --refs[y1][x1] == 0) {
248             wmove(win, y1, x1);
249             waddch(win, trail);
250         }
251     }
252
253     op = &(x == 0
254            ? (y == 0
255               ? upleft
256               : (y == bottom
257                  ? lowleft
258                  : left))
259            : (x == last
260               ? (y == 0
261                  ? upright
262                  : (y == bottom
263                     ? lowright
264                     : right))
265               : (y == 0
266                  ? upper
267                  : (y == bottom
268                     ? lower
269                     : normal))))[w->orientation];
270
271     switch (op->nopts) {
272     case 0:
273         done = TRUE;
274         break;
275     case 1:
276         w->orientation = op->opts[0];
277         break;
278     default:
279         w->orientation = op->opts[(int) (ranf() * (float) op->nopts)];
280         break;
281     }
282
283     if (!done) {
284         x += xinc[w->orientation];
285         y += yinc[w->orientation];
286         wmove(win, y, x);
287
288         if (y < 0)
289             y = 0;
290         waddch(win, w->attrs);
291
292         w->ypos[h] = y;
293         w->xpos[h] = x;
294         refs[y][x]++;
295     }
296
297     return done;
298 }
299
300 #if !defined(NCURSES_VERSION_PATCH) || (NCURSES_VERSION_PATCH < 20070915)
301 static int
302 use_window(WINDOW *win, int (*func)(WINDOW *, void *), void *data)
303 {
304     return func(win, data);
305 }
306 #endif
307
308 int
309 main(int argc, char *argv[])
310 {
311     int x, y;
312     int n;
313     struct worm *w;
314     short *ip;
315     int last, bottom;
316     bool done = FALSE;
317
318     setlocale(LC_ALL, "");
319
320     for (x = 1; x < argc; x++) {
321         char *p;
322         p = argv[x];
323         if (*p == '-')
324             p++;
325         switch (*p) {
326         case 'f':
327             field = "WORM";
328             break;
329         case 'l':
330             if (++x == argc)
331                 goto usage;
332             if ((length = atoi(argv[x])) < 2 || length > 1024) {
333                 fprintf(stderr, "%s: Invalid length\n", *argv);
334                 ExitProgram(EXIT_FAILURE);
335             }
336             break;
337         case 'n':
338             if (++x == argc)
339                 goto usage;
340             if ((number = atoi(argv[x])) < 1 || number > 40) {
341                 fprintf(stderr, "%s: Invalid number of worms\n", *argv);
342                 ExitProgram(EXIT_FAILURE);
343             }
344             break;
345         case 't':
346             trail = '.';
347             break;
348 #ifdef TRACE
349         case 'T':
350             trace_start = atoi(argv[++x]);
351             trace_end = atoi(argv[++x]);
352             break;
353         case 'N':
354             _nc_optimize_enable ^= OPTIMIZE_ALL;        /* declared by ncurses */
355             break;
356 #endif /* TRACE */
357         default:
358           usage:
359             fprintf(stderr,
360                     "usage: %s [-field] [-length #] [-number #] [-trail]\n", *argv);
361             ExitProgram(EXIT_FAILURE);
362         }
363     }
364
365     signal(SIGINT, onsig);
366     initscr();
367     noecho();
368     cbreak();
369     nonl();
370
371     curs_set(0);
372
373     bottom = LINES - 1;
374     last = COLS - 1;
375
376 #ifdef A_COLOR
377     if (has_colors()) {
378         int bg = COLOR_BLACK;
379         start_color();
380 #if HAVE_USE_DEFAULT_COLORS
381         if (use_default_colors() == OK)
382             bg = -1;
383 #endif
384
385 #define SET_COLOR(num, fg) \
386             init_pair(num+1, fg, bg); \
387             flavor[num] |= COLOR_PAIR(num+1) | A_BOLD
388
389         SET_COLOR(0, COLOR_GREEN);
390         SET_COLOR(1, COLOR_RED);
391         SET_COLOR(2, COLOR_CYAN);
392         SET_COLOR(3, COLOR_WHITE);
393         SET_COLOR(4, COLOR_MAGENTA);
394         SET_COLOR(5, COLOR_BLUE);
395         SET_COLOR(6, COLOR_YELLOW);
396     }
397 #endif /* A_COLOR */
398
399     refs = typeMalloc(short *, LINES);
400     for (y = 0; y < LINES; y++) {
401         refs[y] = typeMalloc(short, COLS);
402         for (x = 0; x < COLS; x++) {
403             refs[y][x] = 0;
404         }
405     }
406
407 #ifdef BADCORNER
408     /* if addressing the lower right corner doesn't work in your curses */
409     refs[bottom][last] = 1;
410 #endif /* BADCORNER */
411
412     for (n = number, w = &worm[0]; --n >= 0; w++) {
413         w->attrs = flavor[n % SIZEOF(flavor)];
414         w->orientation = 0;
415         w->head = 0;
416
417         if (!(ip = typeMalloc(short, (length + 1)))) {
418             fprintf(stderr, "%s: out of memory\n", *argv);
419             ExitProgram(EXIT_FAILURE);
420         }
421         w->xpos = ip;
422         for (x = length; --x >= 0;)
423             *ip++ = -1;
424         if (!(ip = typeMalloc(short, (length + 1)))) {
425             fprintf(stderr, "%s: out of memory\n", *argv);
426             ExitProgram(EXIT_FAILURE);
427         }
428         w->ypos = ip;
429         for (y = length; --y >= 0;)
430             *ip++ = -1;
431     }
432     if (field) {
433         const char *p;
434         p = field;
435         for (y = bottom; --y >= 0;) {
436             for (x = COLS; --x >= 0;) {
437                 addch((chtype) (*p++));
438                 if (!*p)
439                     p = field;
440             }
441         }
442     }
443     napms(10);
444     refresh();
445     nodelay(stdscr, TRUE);
446
447     while (!done) {
448         int ch;
449
450         if ((ch = getch()) > 0) {
451 #ifdef TRACE
452             if (trace_start || trace_end) {
453                 if (generation == trace_start) {
454                     trace(TRACE_CALLS);
455                     getch();
456                 } else if (generation == trace_end) {
457                     trace(0);
458                     getch();
459                 }
460
461                 generation++;
462             }
463 #endif
464 #ifdef KEY_RESIZE
465             if (ch == KEY_RESIZE) {
466                 if (last != COLS - 1) {
467                     for (y = 0; y <= bottom; y++) {
468                         refs[y] = typeRealloc(short, COLS, refs[y]);
469                         for (x = last + 1; x < COLS; x++)
470                             refs[y][x] = 0;
471                     }
472                     last = COLS - 1;
473                 }
474                 if (bottom != LINES - 1) {
475                     for (y = LINES; y <= bottom; y++)
476                         free(refs[y]);
477                     refs = typeRealloc(short *, LINES, refs);
478                     for (y = bottom + 1; y < LINES; y++) {
479                         refs[y] = typeMalloc(short, COLS);
480                         for (x = 0; x < COLS; x++)
481                             refs[y][x] = 0;
482                     }
483                     bottom = LINES - 1;
484                 }
485             }
486 #endif
487             /*
488              * Make it simple to put this into single-step mode, or resume
489              * normal operation -T.Dickey
490              */
491             if (ch == 'q') {
492                 done = TRUE;
493                 continue;
494             } else if (ch == 's') {
495                 nodelay(stdscr, FALSE);
496             } else if (ch == ' ') {
497                 nodelay(stdscr, TRUE);
498             }
499         }
500
501         for (n = 0, w = &worm[0]; n < number; n++, w++) {
502             if (use_window(stdscr, draw_worm, w))
503                 done = TRUE;
504         }
505         napms(10);
506         refresh();
507     }
508
509     cleanup();
510 #ifdef NO_LEAKS
511     for (y = 0; y < LINES; y++) {
512         free(refs[y]);
513     }
514     free(refs);
515     for (n = number, w = &worm[0]; --n >= 0; w++) {
516         free(w->xpos);
517         free(w->ypos);
518     }
519 #endif
520     ExitProgram(EXIT_SUCCESS);
521 }