missing ncurses sources
[tomato.git] / release / src / router / libncurses / test / worm.c
blob98f099cb9cb6e94deb2af7b602705a4e9631dc69
1 /****************************************************************************
2 * Copyright (c) 1998-2007,2008 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 ****************************************************************************/
30 @@@ @@@ @@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@
31 @@@ @@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@
32 @@@ @@@ @@@@ @@@@ @@@@ @@@@ @@@ @@@@
33 @@@ @@ @@@ @@@ @@@ @@@ @@@ @@@ @@@
34 @@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@
35 @@@@ @@@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@
36 @@@@@@@@@@@@ @@@@ @@@@ @@@ @@@ @@@ @@@
37 @@@@ @@@@ @@@@@@@@@@@@ @@@ @@@ @@@ @@@
38 @@ @@ @@@@@@@@@@ @@@ @@@ @@@ @@@
40 Eric P. Scott
41 Caltech High Energy Physics
42 October, 1980
44 Hacks to turn this into a test frame for cursor movement:
45 Eric S. Raymond <esr@snark.thyrsus.com>
46 January, 1995
48 July 1995 (esr): worms is now in living color! :-)
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
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.
64 $Id: worm.c,v 1.60 2010/11/13 20:21:21 tom Exp $
67 #include <test.priv.h>
69 #ifdef USE_PTHREADS
70 #include <pthread.h>
71 #endif
73 WANT_USE_WINDOW();
75 #define MAX_WORMS 40
76 #define MAX_LENGTH 1024
78 static chtype flavor[] =
80 'O', '*', '#', '$', '%', '0', '@',
82 static const int xinc[] =
84 1, 1, 1, 0, -1, -1, -1, 0
85 }, yinc[] =
87 -1, 0, 1, 1, 1, 0, -1, -1
90 typedef struct worm {
91 int orientation;
92 int head;
93 int *xpos;
94 int *ypos;
95 chtype attrs;
96 #ifdef USE_PTHREADS
97 pthread_t thread;
98 #endif
99 } WORM;
101 static unsigned long sequence = 0;
102 static bool quitting = FALSE;
104 static WORM worm[MAX_WORMS];
105 static int **refs;
106 static int last_x, last_y;
108 static const char *field;
109 static int length = 16, number = 3;
110 static chtype trail = ' ';
112 static unsigned pending;
113 #ifdef TRACE
114 static int generation, trace_start, trace_end;
115 #endif /* TRACE */
116 /* *INDENT-OFF* */
117 static const struct options {
118 int nopts;
119 int opts[3];
120 } normal[8]={
121 { 3, { 7, 0, 1 } },
122 { 3, { 0, 1, 2 } },
123 { 3, { 1, 2, 3 } },
124 { 3, { 2, 3, 4 } },
125 { 3, { 3, 4, 5 } },
126 { 3, { 4, 5, 6 } },
127 { 3, { 5, 6, 7 } },
128 { 3, { 6, 7, 0 } }
129 }, upper[8]={
130 { 1, { 1, 0, 0 } },
131 { 2, { 1, 2, 0 } },
132 { 0, { 0, 0, 0 } },
133 { 0, { 0, 0, 0 } },
134 { 0, { 0, 0, 0 } },
135 { 2, { 4, 5, 0 } },
136 { 1, { 5, 0, 0 } },
137 { 2, { 1, 5, 0 } }
138 }, left[8]={
139 { 0, { 0, 0, 0 } },
140 { 0, { 0, 0, 0 } },
141 { 0, { 0, 0, 0 } },
142 { 2, { 2, 3, 0 } },
143 { 1, { 3, 0, 0 } },
144 { 2, { 3, 7, 0 } },
145 { 1, { 7, 0, 0 } },
146 { 2, { 7, 0, 0 } }
147 }, right[8]={
148 { 1, { 7, 0, 0 } },
149 { 2, { 3, 7, 0 } },
150 { 1, { 3, 0, 0 } },
151 { 2, { 3, 4, 0 } },
152 { 0, { 0, 0, 0 } },
153 { 0, { 0, 0, 0 } },
154 { 0, { 0, 0, 0 } },
155 { 2, { 6, 7, 0 } }
156 }, lower[8]={
157 { 0, { 0, 0, 0 } },
158 { 2, { 0, 1, 0 } },
159 { 1, { 1, 0, 0 } },
160 { 2, { 1, 5, 0 } },
161 { 1, { 5, 0, 0 } },
162 { 2, { 5, 6, 0 } },
163 { 0, { 0, 0, 0 } },
164 { 0, { 0, 0, 0 } }
165 }, upleft[8]={
166 { 0, { 0, 0, 0 } },
167 { 0, { 0, 0, 0 } },
168 { 0, { 0, 0, 0 } },
169 { 0, { 0, 0, 0 } },
170 { 0, { 0, 0, 0 } },
171 { 1, { 3, 0, 0 } },
172 { 2, { 1, 3, 0 } },
173 { 1, { 1, 0, 0 } }
174 }, upright[8]={
175 { 2, { 3, 5, 0 } },
176 { 1, { 3, 0, 0 } },
177 { 0, { 0, 0, 0 } },
178 { 0, { 0, 0, 0 } },
179 { 0, { 0, 0, 0 } },
180 { 0, { 0, 0, 0 } },
181 { 0, { 0, 0, 0 } },
182 { 1, { 5, 0, 0 } }
183 }, lowleft[8]={
184 { 3, { 7, 0, 1 } },
185 { 0, { 0, 0, 0 } },
186 { 0, { 0, 0, 0 } },
187 { 1, { 1, 0, 0 } },
188 { 2, { 1, 7, 0 } },
189 { 1, { 7, 0, 0 } },
190 { 0, { 0, 0, 0 } },
191 { 0, { 0, 0, 0 } }
192 }, lowright[8]={
193 { 0, { 0, 0, 0 } },
194 { 1, { 7, 0, 0 } },
195 { 2, { 5, 7, 0 } },
196 { 1, { 5, 0, 0 } },
197 { 0, { 0, 0, 0 } },
198 { 0, { 0, 0, 0 } },
199 { 0, { 0, 0, 0 } },
200 { 0, { 0, 0, 0 } }
202 /* *INDENT-ON* */
204 static void
205 cleanup(void)
207 USING_WINDOW(stdscr, wrefresh);
208 curs_set(1);
209 endwin();
212 static RETSIGTYPE
213 onsig(int sig GCC_UNUSED)
215 cleanup();
216 ExitProgram(EXIT_FAILURE);
219 static double
220 ranf(void)
222 long r = (rand() & 077777);
223 return ((double) r / 32768.);
226 static int
227 draw_worm(WINDOW *win, void *data)
229 WORM *w = (WORM *) data;
230 const struct options *op;
231 unsigned mask = (unsigned) (~(1 << (w - worm)));
232 chtype attrs = w->attrs | ((mask & pending) ? A_REVERSE : 0);
234 int x;
235 int y;
236 int h;
238 bool done = FALSE;
240 if ((x = w->xpos[h = w->head]) < 0) {
241 wmove(win, y = w->ypos[h] = last_y, x = w->xpos[h] = 0);
242 waddch(win, attrs);
243 refs[y][x]++;
244 } else {
245 y = w->ypos[h];
248 if (x > last_x)
249 x = last_x;
250 if (y > last_y)
251 y = last_y;
253 if (++h == length)
254 h = 0;
256 if (w->xpos[w->head = h] >= 0) {
257 int x1, y1;
258 x1 = w->xpos[h];
259 y1 = w->ypos[h];
260 if (y1 < LINES
261 && x1 < COLS
262 && --refs[y1][x1] == 0) {
263 wmove(win, y1, x1);
264 waddch(win, trail);
268 op = &(x == 0
269 ? (y == 0
270 ? upleft
271 : (y == last_y
272 ? lowleft
273 : left))
274 : (x == last_x
275 ? (y == 0
276 ? upright
277 : (y == last_y
278 ? lowright
279 : right))
280 : (y == 0
281 ? upper
282 : (y == last_y
283 ? lower
284 : normal))))[w->orientation];
286 switch (op->nopts) {
287 case 0:
288 done = TRUE;
289 break;
290 case 1:
291 w->orientation = op->opts[0];
292 break;
293 default:
294 w->orientation = op->opts[(int) (ranf() * (double) op->nopts)];
295 break;
298 if (!done) {
299 x += xinc[w->orientation];
300 y += yinc[w->orientation];
301 wmove(win, y, x);
303 if (y < 0)
304 y = 0;
305 waddch(win, attrs);
307 w->ypos[h] = y;
308 w->xpos[h] = x;
309 refs[y][x]++;
312 return done;
315 #ifdef USE_PTHREADS
316 static bool
317 quit_worm(int bitnum)
319 pending |= (1 << bitnum);
320 napms(10); /* let the other thread(s) have a chance */
321 pending &= ~(1 << bitnum);
322 return quitting;
325 static void *
326 start_worm(void *arg)
328 unsigned long compare = 0;
329 Trace(("start_worm"));
330 while (!quit_worm(((struct worm *) arg) - worm)) {
331 while (compare < sequence) {
332 ++compare;
333 #if HAVE_USE_WINDOW
334 use_window(stdscr, draw_worm, arg);
335 #else
336 draw_worm(stdscr, arg);
337 #endif
340 Trace(("...start_worm (done)"));
341 return NULL;
343 #endif
345 static bool
346 draw_all_worms(void)
348 bool done = FALSE;
349 int n;
350 struct worm *w;
352 #ifdef USE_PTHREADS
353 static bool first = TRUE;
354 if (first) {
355 first = FALSE;
356 for (n = 0, w = &worm[0]; n < number; n++, w++) {
357 int rc;
358 rc = pthread_create(&(w->thread), NULL, start_worm, w);
361 #else
362 for (n = 0, w = &worm[0]; n < number; n++, w++) {
363 if (
364 #if HAVE_USE_WINDOW
365 USING_WINDOW2(stdscr, draw_worm, w)
366 #else
367 draw_worm(stdscr, w)
368 #endif
370 done = TRUE;
372 #endif
373 return done;
376 static int
377 get_input(void)
379 int ch;
380 ch = USING_WINDOW(stdscr, wgetch);
381 return ch;
384 #ifdef KEY_RESIZE
385 static int
386 update_refs(WINDOW *win)
388 int x, y;
390 (void) win;
391 if (last_x != COLS - 1) {
392 for (y = 0; y <= last_y; y++) {
393 refs[y] = typeRealloc(int, (size_t) COLS, refs[y]);
394 for (x = last_x + 1; x < COLS; x++)
395 refs[y][x] = 0;
397 last_x = COLS - 1;
399 if (last_y != LINES - 1) {
400 for (y = LINES; y <= last_y; y++)
401 free(refs[y]);
402 refs = typeRealloc(int *, (size_t) LINES, refs);
403 for (y = last_y + 1; y < LINES; y++) {
404 refs[y] = typeMalloc(int, (size_t) COLS);
405 for (x = 0; x < COLS; x++)
406 refs[y][x] = 0;
408 last_y = LINES - 1;
410 return OK;
412 #endif
415 main(int argc, char *argv[])
417 int x, y;
418 int n;
419 struct worm *w;
420 int *ip;
421 bool done = FALSE;
423 setlocale(LC_ALL, "");
425 for (x = 1; x < argc; x++) {
426 char *p;
427 p = argv[x];
428 if (*p == '-')
429 p++;
430 switch (*p) {
431 case 'f':
432 field = "WORM";
433 break;
434 case 'l':
435 if (++x == argc)
436 goto usage;
437 if ((length = atoi(argv[x])) < 2 || length > MAX_LENGTH) {
438 fprintf(stderr, "%s: Invalid length\n", *argv);
439 ExitProgram(EXIT_FAILURE);
441 break;
442 case 'n':
443 if (++x == argc)
444 goto usage;
445 if ((number = atoi(argv[x])) < 1 || number > MAX_WORMS) {
446 fprintf(stderr, "%s: Invalid number of worms\n", *argv);
447 ExitProgram(EXIT_FAILURE);
449 break;
450 case 't':
451 trail = '.';
452 break;
453 #ifdef TRACE
454 case 'T':
455 trace_start = atoi(argv[++x]);
456 trace_end = atoi(argv[++x]);
457 break;
458 case 'N':
459 _nc_optimize_enable ^= OPTIMIZE_ALL; /* declared by ncurses */
460 break;
461 #endif /* TRACE */
462 default:
463 usage:
464 fprintf(stderr,
465 "usage: %s [-field] [-length #] [-number #] [-trail]\n", *argv);
466 ExitProgram(EXIT_FAILURE);
470 signal(SIGINT, onsig);
471 initscr();
472 noecho();
473 cbreak();
474 nonl();
476 curs_set(0);
478 last_y = LINES - 1;
479 last_x = COLS - 1;
481 #ifdef A_COLOR
482 if (has_colors()) {
483 int bg = COLOR_BLACK;
484 start_color();
485 #if HAVE_USE_DEFAULT_COLORS
486 if (use_default_colors() == OK)
487 bg = -1;
488 #endif
490 #define SET_COLOR(num, fg) \
491 init_pair(num+1, (short) fg, (short) bg); \
492 flavor[num] |= COLOR_PAIR(num+1) | A_BOLD
494 SET_COLOR(0, COLOR_GREEN);
495 SET_COLOR(1, COLOR_RED);
496 SET_COLOR(2, COLOR_CYAN);
497 SET_COLOR(3, COLOR_WHITE);
498 SET_COLOR(4, COLOR_MAGENTA);
499 SET_COLOR(5, COLOR_BLUE);
500 SET_COLOR(6, COLOR_YELLOW);
502 #endif /* A_COLOR */
504 refs = typeMalloc(int *, (size_t) LINES);
505 for (y = 0; y < LINES; y++) {
506 refs[y] = typeMalloc(int, (size_t) COLS);
507 for (x = 0; x < COLS; x++) {
508 refs[y][x] = 0;
512 #ifdef BADCORNER
513 /* if addressing the lower right corner doesn't work in your curses */
514 refs[last_y][last_x] = 1;
515 #endif /* BADCORNER */
517 for (n = number, w = &worm[0]; --n >= 0; w++) {
518 w->attrs = flavor[(unsigned) n % SIZEOF(flavor)];
519 w->orientation = 0;
520 w->head = 0;
522 if (!(ip = typeMalloc(int, (size_t) (length + 1)))) {
523 fprintf(stderr, "%s: out of memory\n", *argv);
524 ExitProgram(EXIT_FAILURE);
526 w->xpos = ip;
527 for (x = length; --x >= 0;)
528 *ip++ = -1;
529 if (!(ip = typeMalloc(int, (size_t) (length + 1)))) {
530 fprintf(stderr, "%s: out of memory\n", *argv);
531 ExitProgram(EXIT_FAILURE);
533 w->ypos = ip;
534 for (y = length; --y >= 0;)
535 *ip++ = -1;
537 if (field) {
538 const char *p;
539 p = field;
540 for (y = last_y; --y >= 0;) {
541 for (x = COLS; --x >= 0;) {
542 addch((chtype) (*p++));
543 if (!*p)
544 p = field;
548 USING_WINDOW(stdscr, wrefresh);
549 nodelay(stdscr, TRUE);
551 while (!done) {
552 int ch;
554 ++sequence;
555 if ((ch = get_input()) > 0) {
556 #ifdef TRACE
557 if (trace_start || trace_end) {
558 if (generation == trace_start) {
559 trace(TRACE_CALLS);
560 get_input();
561 } else if (generation == trace_end) {
562 trace(0);
563 get_input();
566 generation++;
568 #endif
570 #ifdef KEY_RESIZE
571 if (ch == KEY_RESIZE) {
572 USING_WINDOW(stdscr, update_refs);
574 #endif
577 * Make it simple to put this into single-step mode, or resume
578 * normal operation -T.Dickey
580 if (ch == 'q') {
581 quitting = TRUE;
582 done = TRUE;
583 continue;
584 } else if (ch == 's') {
585 nodelay(stdscr, FALSE);
586 } else if (ch == ' ') {
587 nodelay(stdscr, TRUE);
591 done = draw_all_worms();
592 napms(10);
593 USING_WINDOW(stdscr, wrefresh);
596 Trace(("Cleanup"));
597 cleanup();
598 #ifdef NO_LEAKS
599 for (y = 0; y < LINES; y++) {
600 free(refs[y]);
602 free(refs);
603 for (n = number, w = &worm[0]; --n >= 0; w++) {
604 free(w->xpos);
605 free(w->ypos);
607 #endif
608 #ifdef USE_PTHREADS
610 * Do this just in case one of the threads did not really exit.
612 Trace(("join all threads"));
613 for (n = 0; n < number; n++) {
614 pthread_join(worm[n].thread, NULL);
616 #endif
617 ExitProgram(EXIT_SUCCESS);