blob: 2029b34f9e3cefdd00d0c3d4a03ef3b88baf103a [file] [log] [blame]
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301/****************************************************************************
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 ****************************************************************************/
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.58 2008/10/04 21:54:09 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 short 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 short *xpos;
94 short *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 short **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
205cleanup(void)
206{
207 USING_WINDOW(stdscr, wrefresh);
208 curs_set(1);
209 endwin();
210}
211
212static RETSIGTYPE
213onsig(int sig GCC_UNUSED)
214{
215 cleanup();
216 ExitProgram(EXIT_FAILURE);
217}
218
219static float
220ranf(void)
221{
222 long r = (rand() & 077777);
223 return ((float) r / 32768.);
224}
225
226static int
227draw_worm(WINDOW *win, void *data)
228{
229 WORM *w = (WORM *) data;
230 const struct options *op;
231 unsigned mask = ~(1 << (w - worm));
232 chtype attrs = w->attrs | ((mask & pending) ? A_REVERSE : 0);
233
234 int x;
235 int y;
236 int h;
237
238 bool done = FALSE;
239
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];
246 }
247
248 if (x > last_x)
249 x = last_x;
250 if (y > last_y)
251 y = last_y;
252
253 if (++h == length)
254 h = 0;
255
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);
265 }
266 }
267
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];
285
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() * (float) op->nopts)];
295 break;
296 }
297
298 if (!done) {
299 x += xinc[w->orientation];
300 y += yinc[w->orientation];
301 wmove(win, y, x);
302
303 if (y < 0)
304 y = 0;
305 waddch(win, attrs);
306
307 w->ypos[h] = y;
308 w->xpos[h] = x;
309 refs[y][x]++;
310 }
311
312 return done;
313}
314
315#ifdef USE_PTHREADS
316static bool
317quit_worm(int bitnum)
318{
319 pending |= (1 << bitnum);
320 napms(10); /* let the other thread(s) have a chance */
321 pending &= ~(1 << bitnum);
322 return quitting;
323}
324
325static void *
326start_worm(void *arg)
327{
328 unsigned long compare = 0;
329 Trace(("start_worm"));
330 while (!quit_worm(((struct worm *) arg) - worm)) {
331 while (compare < sequence) {
332 ++compare;
333 use_window(stdscr, draw_worm, arg);
334 }
335 }
336 Trace(("...start_worm (done)"));
337 return NULL;
338}
339#endif
340
341static bool
342draw_all_worms(void)
343{
344 bool done = FALSE;
345 int n;
346 struct worm *w;
347
348#ifdef USE_PTHREADS
349 static bool first = TRUE;
350 if (first) {
351 first = FALSE;
352 for (n = 0, w = &worm[0]; n < number; n++, w++) {
353 int rc;
354 rc = pthread_create(&(w->thread), NULL, start_worm, w);
355 }
356 }
357#else
358 for (n = 0, w = &worm[0]; n < number; n++, w++) {
359 if (USING_WINDOW2(stdscr, draw_worm, w))
360 done = TRUE;
361 }
362#endif
363 return done;
364}
365
366static int
367get_input(void)
368{
369 int ch;
370 ch = USING_WINDOW(stdscr, wgetch);
371 return ch;
372}
373
374#ifdef KEY_RESIZE
375static int
376update_refs(WINDOW *win)
377{
378 int x, y;
379
380 (void) win;
381 if (last_x != COLS - 1) {
382 for (y = 0; y <= last_y; y++) {
383 refs[y] = typeRealloc(short, COLS, refs[y]);
384 for (x = last_x + 1; x < COLS; x++)
385 refs[y][x] = 0;
386 }
387 last_x = COLS - 1;
388 }
389 if (last_y != LINES - 1) {
390 for (y = LINES; y <= last_y; y++)
391 free(refs[y]);
392 refs = typeRealloc(short *, LINES, refs);
393 for (y = last_y + 1; y < LINES; y++) {
394 refs[y] = typeMalloc(short, COLS);
395 for (x = 0; x < COLS; x++)
396 refs[y][x] = 0;
397 }
398 last_y = LINES - 1;
399 }
400 return OK;
401}
402#endif
403
404int
405main(int argc, char *argv[])
406{
407 int x, y;
408 int n;
409 struct worm *w;
410 short *ip;
411 bool done = FALSE;
412
413 setlocale(LC_ALL, "");
414
415 for (x = 1; x < argc; x++) {
416 char *p;
417 p = argv[x];
418 if (*p == '-')
419 p++;
420 switch (*p) {
421 case 'f':
422 field = "WORM";
423 break;
424 case 'l':
425 if (++x == argc)
426 goto usage;
427 if ((length = atoi(argv[x])) < 2 || length > MAX_LENGTH) {
428 fprintf(stderr, "%s: Invalid length\n", *argv);
429 ExitProgram(EXIT_FAILURE);
430 }
431 break;
432 case 'n':
433 if (++x == argc)
434 goto usage;
435 if ((number = atoi(argv[x])) < 1 || number > MAX_WORMS) {
436 fprintf(stderr, "%s: Invalid number of worms\n", *argv);
437 ExitProgram(EXIT_FAILURE);
438 }
439 break;
440 case 't':
441 trail = '.';
442 break;
443#ifdef TRACE
444 case 'T':
445 trace_start = atoi(argv[++x]);
446 trace_end = atoi(argv[++x]);
447 break;
448 case 'N':
449 _nc_optimize_enable ^= OPTIMIZE_ALL; /* declared by ncurses */
450 break;
451#endif /* TRACE */
452 default:
453 usage:
454 fprintf(stderr,
455 "usage: %s [-field] [-length #] [-number #] [-trail]\n", *argv);
456 ExitProgram(EXIT_FAILURE);
457 }
458 }
459
460 signal(SIGINT, onsig);
461 initscr();
462 noecho();
463 cbreak();
464 nonl();
465
466 curs_set(0);
467
468 last_y = LINES - 1;
469 last_x = COLS - 1;
470
471#ifdef A_COLOR
472 if (has_colors()) {
473 int bg = COLOR_BLACK;
474 start_color();
475#if HAVE_USE_DEFAULT_COLORS
476 if (use_default_colors() == OK)
477 bg = -1;
478#endif
479
480#define SET_COLOR(num, fg) \
481 init_pair(num+1, fg, bg); \
482 flavor[num] |= COLOR_PAIR(num+1) | A_BOLD
483
484 SET_COLOR(0, COLOR_GREEN);
485 SET_COLOR(1, COLOR_RED);
486 SET_COLOR(2, COLOR_CYAN);
487 SET_COLOR(3, COLOR_WHITE);
488 SET_COLOR(4, COLOR_MAGENTA);
489 SET_COLOR(5, COLOR_BLUE);
490 SET_COLOR(6, COLOR_YELLOW);
491 }
492#endif /* A_COLOR */
493
494 refs = typeMalloc(short *, LINES);
495 for (y = 0; y < LINES; y++) {
496 refs[y] = typeMalloc(short, COLS);
497 for (x = 0; x < COLS; x++) {
498 refs[y][x] = 0;
499 }
500 }
501
502#ifdef BADCORNER
503 /* if addressing the lower right corner doesn't work in your curses */
504 refs[last_y][last_x] = 1;
505#endif /* BADCORNER */
506
507 for (n = number, w = &worm[0]; --n >= 0; w++) {
508 w->attrs = flavor[n % SIZEOF(flavor)];
509 w->orientation = 0;
510 w->head = 0;
511
512 if (!(ip = typeMalloc(short, (length + 1)))) {
513 fprintf(stderr, "%s: out of memory\n", *argv);
514 ExitProgram(EXIT_FAILURE);
515 }
516 w->xpos = ip;
517 for (x = length; --x >= 0;)
518 *ip++ = -1;
519 if (!(ip = typeMalloc(short, (length + 1)))) {
520 fprintf(stderr, "%s: out of memory\n", *argv);
521 ExitProgram(EXIT_FAILURE);
522 }
523 w->ypos = ip;
524 for (y = length; --y >= 0;)
525 *ip++ = -1;
526 }
527 if (field) {
528 const char *p;
529 p = field;
530 for (y = last_y; --y >= 0;) {
531 for (x = COLS; --x >= 0;) {
532 addch((chtype) (*p++));
533 if (!*p)
534 p = field;
535 }
536 }
537 }
538 USING_WINDOW(stdscr, wrefresh);
539 nodelay(stdscr, TRUE);
540
541 while (!done) {
542 int ch;
543
544 ++sequence;
545 if ((ch = get_input()) > 0) {
546#ifdef TRACE
547 if (trace_start || trace_end) {
548 if (generation == trace_start) {
549 trace(TRACE_CALLS);
550 get_input();
551 } else if (generation == trace_end) {
552 trace(0);
553 get_input();
554 }
555
556 generation++;
557 }
558#endif
559
560#ifdef KEY_RESIZE
561 if (ch == KEY_RESIZE) {
562 USING_WINDOW(stdscr, update_refs);
563 }
564#endif
565
566 /*
567 * Make it simple to put this into single-step mode, or resume
568 * normal operation -T.Dickey
569 */
570 if (ch == 'q') {
571 quitting = TRUE;
572 done = TRUE;
573 continue;
574 } else if (ch == 's') {
575 nodelay(stdscr, FALSE);
576 } else if (ch == ' ') {
577 nodelay(stdscr, TRUE);
578 }
579 }
580
581 done = draw_all_worms();
582 napms(10);
583 USING_WINDOW(stdscr, wrefresh);
584 }
585
586 Trace(("Cleanup"));
587 cleanup();
588#ifdef NO_LEAKS
589 for (y = 0; y < LINES; y++) {
590 free(refs[y]);
591 }
592 free(refs);
593 for (n = number, w = &worm[0]; --n >= 0; w++) {
594 free(w->xpos);
595 free(w->ypos);
596 }
597#endif
598#ifdef USE_PTHREADS
599 /*
600 * Do this just in case one of the threads did not really exit.
601 */
602 Trace(("join all threads"));
603 for (n = 0; n < number; n++) {
604 pthread_join(worm[n].thread, NULL);
605 }
606#endif
607 ExitProgram(EXIT_SUCCESS);
608}