blob: c3fc0da822ef2619246c5ed831c0d9eb9bee48af [file] [log] [blame]
Steve Kondikae271bc2015-11-15 02:50:53 +01001/****************************************************************************
2 * Copyright (c) 1998-2012,2013 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
50Options:
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.65 2013/06/22 20:01:41 tom Exp $
65*/
66
67#include <test.priv.h>
68
69#ifdef USE_PTHREADS
70#include <pthread.h>
71#endif
72
73WANT_USE_WINDOW();
74
75#define MAX_WORMS 40
76#define MAX_LENGTH 1024
77
78static chtype flavor[] =
79{
80 'O', '*', '#', '$', '%', '0', '@',
81};
82static const int xinc[] =
83{
84 1, 1, 1, 0, -1, -1, -1, 0
85}, yinc[] =
86{
87 -1, 0, 1, 1, 1, 0, -1, -1
88};
89
90typedef 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;
100
101static unsigned long sequence = 0;
102static bool quitting = FALSE;
103
104static WORM worm[MAX_WORMS];
105static int **refs;
106static int last_x, last_y;
107
108static const char *field;
109static int length = 16, number = 3;
110static chtype trail = ' ';
111
112static unsigned pending;
113#ifdef TRACE
114static int generation, trace_start, trace_end;
115#endif /* TRACE */
116/* *INDENT-OFF* */
117static 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 } }
201};
202/* *INDENT-ON* */
203
204static void
205failed(const char *s)
206{
207 perror(s);
208 endwin();
209 ExitProgram(EXIT_FAILURE);
210}
211
212static void
213cleanup(void)
214{
215 USING_WINDOW(stdscr, wrefresh);
216 curs_set(1);
217 endwin();
218}
219
220static void
221onsig(int sig GCC_UNUSED)
222{
223 cleanup();
224 ExitProgram(EXIT_FAILURE);
225}
226
227static double
228ranf(void)
229{
230 long r = (rand() & 077777);
231 return ((double) r / 32768.);
232}
233
234static int
235draw_worm(WINDOW *win, void *data)
236{
237 WORM *w = (WORM *) data;
238 const struct options *op;
239 unsigned mask = (unsigned) (~(1 << (w - worm)));
240 chtype attrs = w->attrs | ((mask & pending) ? A_REVERSE : 0);
241
242 int x;
243 int y;
244 int h;
245
246 bool done = FALSE;
247
248 if ((x = w->xpos[h = w->head]) < 0) {
249 wmove(win, y = w->ypos[h] = last_y, x = w->xpos[h] = 0);
250 waddch(win, attrs);
251 refs[y][x]++;
252 } else {
253 y = w->ypos[h];
254 }
255
256 if (x > last_x)
257 x = last_x;
258 if (y > last_y)
259 y = last_y;
260
261 if (++h == length)
262 h = 0;
263
264 if (w->xpos[w->head = h] >= 0) {
265 int x1, y1;
266 x1 = w->xpos[h];
267 y1 = w->ypos[h];
268 if (y1 < LINES
269 && x1 < COLS
270 && --refs[y1][x1] == 0) {
271 wmove(win, y1, x1);
272 waddch(win, trail);
273 }
274 }
275
276 op = &(x == 0
277 ? (y == 0
278 ? upleft
279 : (y == last_y
280 ? lowleft
281 : left))
282 : (x == last_x
283 ? (y == 0
284 ? upright
285 : (y == last_y
286 ? lowright
287 : right))
288 : (y == 0
289 ? upper
290 : (y == last_y
291 ? lower
292 : normal))))[w->orientation];
293
294 switch (op->nopts) {
295 case 0:
296 done = TRUE;
297 break;
298 case 1:
299 w->orientation = op->opts[0];
300 break;
301 default:
302 w->orientation = op->opts[(int) (ranf() * (double) op->nopts)];
303 break;
304 }
305
306 if (!done) {
307 x += xinc[w->orientation];
308 y += yinc[w->orientation];
309 wmove(win, y, x);
310
311 if (y < 0)
312 y = 0;
313 waddch(win, attrs);
314
315 w->ypos[h] = y;
316 w->xpos[h] = x;
317 refs[y][x]++;
318 }
319
320 return done;
321}
322
323#ifdef USE_PTHREADS
324static bool
325quit_worm(int bitnum)
326{
327 pending |= (1 << bitnum);
328 napms(10); /* let the other thread(s) have a chance */
329 pending &= ~(1 << bitnum);
330 return quitting;
331}
332
333static void *
334start_worm(void *arg)
335{
336 unsigned long compare = 0;
337 Trace(("start_worm"));
338 while (!quit_worm(((struct worm *) arg) - worm)) {
339 while (compare < sequence) {
340 ++compare;
341#if HAVE_USE_WINDOW
342 use_window(stdscr, draw_worm, arg);
343#else
344 draw_worm(stdscr, arg);
345#endif
346 }
347 }
348 Trace(("...start_worm (done)"));
349 return NULL;
350}
351#endif
352
353static bool
354draw_all_worms(void)
355{
356 bool done = FALSE;
357 int n;
358 struct worm *w;
359
360#ifdef USE_PTHREADS
361 static bool first = TRUE;
362 if (first) {
363 first = FALSE;
364 for (n = 0, w = &worm[0]; n < number; n++, w++) {
365 (void) pthread_create(&(w->thread), NULL, start_worm, w);
366 }
367 }
368#else
369 for (n = 0, w = &worm[0]; n < number; n++, w++) {
370 if (
371#if HAVE_USE_WINDOW
372 USING_WINDOW2(stdscr, draw_worm, w)
373#else
374 draw_worm(stdscr, w)
375#endif
376 )
377 done = TRUE;
378 }
379#endif
380 return done;
381}
382
383static int
384get_input(void)
385{
386 int ch;
387 ch = USING_WINDOW(stdscr, wgetch);
388 return ch;
389}
390
391#ifdef KEY_RESIZE
392static int
393update_refs(WINDOW *win)
394{
395 int x, y;
396
397 (void) win;
398 if (last_x != COLS - 1) {
399 for (y = 0; y <= last_y; y++) {
400 refs[y] = typeRealloc(int, (size_t) COLS, refs[y]);
401 if (!refs[y])
402 failed("update_refs");
403 for (x = last_x + 1; x < COLS; x++)
404 refs[y][x] = 0;
405 }
406 last_x = COLS - 1;
407 }
408 if (last_y != LINES - 1) {
409 for (y = LINES; y <= last_y; y++)
410 free(refs[y]);
411 refs = typeRealloc(int *, (size_t) LINES, refs);
412 for (y = last_y + 1; y < LINES; y++) {
413 refs[y] = typeMalloc(int, (size_t) COLS);
414 if (!refs[y])
415 failed("update_refs");
416 for (x = 0; x < COLS; x++)
417 refs[y][x] = 0;
418 }
419 last_y = LINES - 1;
420 }
421 return OK;
422}
423#endif
424
425int
426main(int argc, char *argv[])
427{
428 int x, y;
429 int n;
430 struct worm *w;
431 int *ip;
432 bool done = FALSE;
433 int max_refs;
434
435 setlocale(LC_ALL, "");
436
437 for (x = 1; x < argc; x++) {
438 char *p;
439 p = argv[x];
440 if (*p == '-')
441 p++;
442 switch (*p) {
443 case 'f':
444 field = "WORM";
445 break;
446 case 'l':
447 if (++x == argc)
448 goto usage;
449 if ((length = atoi(argv[x])) < 2 || length > MAX_LENGTH) {
450 fprintf(stderr, "%s: Invalid length\n", *argv);
451 ExitProgram(EXIT_FAILURE);
452 }
453 break;
454 case 'n':
455 if (++x == argc)
456 goto usage;
457 if ((number = atoi(argv[x])) < 1 || number > MAX_WORMS) {
458 fprintf(stderr, "%s: Invalid number of worms\n", *argv);
459 ExitProgram(EXIT_FAILURE);
460 }
461 break;
462 case 't':
463 trail = '.';
464 break;
465#ifdef TRACE
466 case 'T':
467 trace_start = atoi(argv[++x]);
468 trace_end = atoi(argv[++x]);
469 break;
470 case 'N':
471 _nc_optimize_enable ^= OPTIMIZE_ALL; /* declared by ncurses */
472 break;
473#endif /* TRACE */
474 default:
475 usage:
476 fprintf(stderr,
477 "usage: %s [-field] [-length #] [-number #] [-trail]\n", *argv);
478 ExitProgram(EXIT_FAILURE);
479 }
480 }
481
482 signal(SIGINT, onsig);
483 initscr();
484 noecho();
485 cbreak();
486 nonl();
487
488 curs_set(0);
489
490 last_y = LINES - 1;
491 last_x = COLS - 1;
492
493#ifdef A_COLOR
494 if (has_colors()) {
495 int bg = COLOR_BLACK;
496 start_color();
497#if HAVE_USE_DEFAULT_COLORS
498 if (use_default_colors() == OK)
499 bg = -1;
500#endif
501
502#define SET_COLOR(num, fg) \
503 init_pair(num+1, (short) fg, (short) bg); \
504 flavor[num] |= (chtype) COLOR_PAIR(num+1) | A_BOLD
505
506 SET_COLOR(0, COLOR_GREEN);
507 SET_COLOR(1, COLOR_RED);
508 SET_COLOR(2, COLOR_CYAN);
509 SET_COLOR(3, COLOR_WHITE);
510 SET_COLOR(4, COLOR_MAGENTA);
511 SET_COLOR(5, COLOR_BLUE);
512 SET_COLOR(6, COLOR_YELLOW);
513 }
514#endif /* A_COLOR */
515
516 max_refs = LINES;
517 refs = typeMalloc(int *, (size_t) max_refs);
518 for (y = 0; y < max_refs; y++) {
519 refs[y] = typeMalloc(int, (size_t) COLS);
520 for (x = 0; x < COLS; x++) {
521 refs[y][x] = 0;
522 }
523 }
524
525#ifdef BADCORNER
526 /* if addressing the lower right corner doesn't work in your curses */
527 refs[last_y][last_x] = 1;
528#endif /* BADCORNER */
529
530 for (n = number, w = &worm[0]; --n >= 0; w++) {
531 w->attrs = flavor[(unsigned) n % SIZEOF(flavor)];
532 w->orientation = 0;
533 w->head = 0;
534
535 if (!(ip = typeMalloc(int, (size_t) (length + 1)))) {
536 fprintf(stderr, "%s: out of memory\n", *argv);
537 ExitProgram(EXIT_FAILURE);
538 }
539 w->xpos = ip;
540 for (x = length; --x >= 0;)
541 *ip++ = -1;
542 if (!(ip = typeMalloc(int, (size_t) (length + 1)))) {
543 fprintf(stderr, "%s: out of memory\n", *argv);
544 ExitProgram(EXIT_FAILURE);
545 }
546 w->ypos = ip;
547 for (y = length; --y >= 0;)
548 *ip++ = -1;
549 }
550 if (field) {
551 const char *p;
552 p = field;
553 for (y = last_y; --y >= 0;) {
554 for (x = COLS; --x >= 0;) {
555 addch((chtype) (*p++));
556 if (!*p)
557 p = field;
558 }
559 }
560 }
561 USING_WINDOW(stdscr, wrefresh);
562 nodelay(stdscr, TRUE);
563
564 while (!done) {
565 int ch;
566
567 ++sequence;
568 if ((ch = get_input()) > 0) {
569#ifdef TRACE
570 if (trace_start || trace_end) {
571 if (generation == trace_start) {
572 trace(TRACE_CALLS);
573 get_input();
574 } else if (generation == trace_end) {
575 trace(0);
576 get_input();
577 }
578
579 generation++;
580 }
581#endif
582
583#ifdef KEY_RESIZE
584 if (ch == KEY_RESIZE) {
585 USING_WINDOW(stdscr, update_refs);
586 }
587#endif
588
589 /*
590 * Make it simple to put this into single-step mode, or resume
591 * normal operation -T.Dickey
592 */
593 if (ch == 'q') {
594 quitting = TRUE;
595 done = TRUE;
596 continue;
597 } else if (ch == 's') {
598 nodelay(stdscr, FALSE);
599 } else if (ch == ' ') {
600 nodelay(stdscr, TRUE);
601 }
602 }
603
604 done = draw_all_worms();
605 napms(10);
606 USING_WINDOW(stdscr, wrefresh);
607 }
608
609 Trace(("Cleanup"));
610 cleanup();
611#ifdef NO_LEAKS
612 for (y = 0; y < max_refs; y++) {
613 free(refs[y]);
614 }
615 free(refs);
616 for (n = number, w = &worm[0]; --n >= 0; w++) {
617 free(w->xpos);
618 free(w->ypos);
619 }
620#endif
621#ifdef USE_PTHREADS
622 /*
623 * Do this just in case one of the threads did not really exit.
624 */
625 Trace(("join all threads"));
626 for (n = 0; n < number; n++) {
627 pthread_join(worm[n].thread, NULL);
628 }
629#endif
630 ExitProgram(EXIT_SUCCESS);
631}