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 ****************************************************************************/ 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} 609