1 /* $OpenBSD: mach.c,v 1.22 2016/09/11 14:21:17 tb Exp $ */ 2 /* $NetBSD: mach.c,v 1.5 1995/04/28 22:28:48 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Barry Brachman. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /* 37 * Terminal interface 38 * 39 * Input is raw and unechoed 40 */ 41 #include <sys/ioctl.h> 42 43 #include <ctype.h> 44 #include <curses.h> 45 #include <err.h> 46 #include <signal.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <termios.h> 50 #include <time.h> 51 52 #include "bog.h" 53 #include "extern.h" 54 55 static int gone; 56 57 static int ccol, crow, maxw; 58 static int colstarts[MAXCOLS], ncolstarts; 59 static char *separator; 60 int ncols, nlines, lastline; 61 62 /* 63 * The following determine the screen layout 64 */ 65 int PROMPT_COL = 20; 66 int PROMPT_LINE = 3; 67 68 int BOARD_COL = 0; 69 int BOARD_LINE = 0; 70 71 int SCORE_COL = 20; 72 int SCORE_LINE = 0; 73 74 int LIST_COL = 0; 75 int LIST_LINE = 10; 76 77 int TIMER_COL = 20; 78 int TIMER_LINE = 2; 79 80 static void cont_catcher(int); 81 static int prwidth(char **, int); 82 static void prword(char **, int); 83 static void stop_catcher(int); 84 static void tty_cleanup(void); 85 static int tty_setup(void); 86 static void tty_showboard(char *); 87 static void winch_catcher(int); 88 89 /* 90 * Do system dependent initialization 91 * This is called once, when the program starts 92 */ 93 int 94 setup(void) 95 { 96 char *cp, *ep; 97 98 if (tty_setup() < 0) 99 return(-1); 100 101 separator = malloc(4 * grid + 2); 102 if (separator == NULL) 103 err(1, NULL); 104 105 ep = separator + 4 * grid; 106 for (cp = separator; cp < ep;) { 107 *cp++ = '+'; 108 *cp++ = '-'; 109 *cp++ = '-'; 110 *cp++ = '-'; 111 } 112 *cp++ = '+'; 113 *cp = '\0'; 114 115 SCORE_COL += (grid - 4) * 4; 116 TIMER_COL += (grid - 4) * 4; 117 PROMPT_COL += (grid - 4) * 4; 118 LIST_LINE += (grid - 4) * 2; 119 120 return(0); 121 } 122 123 /* 124 * Do system dependent clean up 125 * This is called once, just before the program terminates 126 */ 127 void 128 cleanup(void) 129 { 130 tty_cleanup(); 131 } 132 133 /* 134 * Display the player's word list, the list of words not found, and the running 135 * stats 136 */ 137 void 138 results(void) 139 { 140 int col, row; 141 int denom1, denom2; 142 143 move(LIST_LINE, LIST_COL); 144 clrtobot(); 145 printw("Words you found (%d):", npwords); 146 refresh(); 147 move(LIST_LINE + 1, LIST_COL); 148 prtable(pword, npwords, 0, ncols, prword, prwidth); 149 150 getyx(stdscr, row, col); 151 move(row + 1, col); 152 printw("Words you missed (%d):", nmwords); 153 refresh(); 154 move(row + 2, col); 155 prtable(mword, nmwords, 0, ncols, prword, prwidth); 156 157 denom1 = npwords + nmwords; 158 denom2 = tnpwords + tnmwords; 159 160 move(SCORE_LINE, SCORE_COL); 161 printw("Score: %d out of %d\n", npwords, denom1); 162 move(SCORE_LINE + 1, SCORE_COL); 163 printw("Percentage: %0.2f%% (%0.2f%% over %d game%s)\n", 164 denom1 ? (100.0 * npwords) / (double) denom1 : 0.0, 165 denom2 ? (100.0 * tnpwords) / (double) denom2 : 0.0, 166 ngames, ngames > 1 ? "s" : ""); 167 move(TIMER_LINE, TIMER_COL); 168 wclrtoeol(stdscr); 169 } 170 171 static void 172 prword(char **base, int indx) 173 { 174 printw("%s", base[indx]); 175 } 176 177 static int 178 prwidth(char **base, int indx) 179 { 180 return (strlen(base[indx])); 181 } 182 183 /* 184 * Main input routine 185 * 186 * - doesn't accept words longer than MAXWORDLEN or containing caps 187 */ 188 char * 189 get_line(char *q) 190 { 191 int ch, done; 192 char *p; 193 int row, col; 194 195 p = q; 196 done = 0; 197 while (!done) { 198 ch = timerch(); 199 switch (ch) { 200 case '\n': 201 case '\r': 202 case ' ': 203 done = 1; 204 break; 205 case '\033': 206 findword(); 207 break; 208 case '\177': /* <del> */ 209 case '\010': /* <bs> */ 210 if (p == q) 211 break; 212 p--; 213 getyx(stdscr, row, col); 214 move(row, col - 1); 215 clrtoeol(); 216 refresh(); 217 break; 218 case '\025': /* <^u> */ 219 case '\027': /* <^w> */ 220 if (p == q) 221 break; 222 getyx(stdscr, row, col); 223 move(row, col - (int) (p - q)); 224 p = q; 225 clrtoeol(); 226 refresh(); 227 break; 228 #ifdef SIGTSTP 229 case '\032': /* <^z> */ 230 stop_catcher(0); 231 break; 232 #endif 233 case '\023': /* <^s> */ 234 stoptime(); 235 printw("<PAUSE>"); 236 refresh(); 237 while ((ch = inputch()) != '\021' && ch != '\023') 238 ; 239 move(crow, ccol); 240 clrtoeol(); 241 refresh(); 242 starttime(); 243 break; 244 case '\003': /* <^c> */ 245 cleanup(); 246 exit(0); 247 case '\004': /* <^d> */ 248 done = 1; 249 ch = EOF; 250 break; 251 case '\014': /* <^l> */ 252 case '\022': /* <^r> */ 253 redraw(); 254 break; 255 case '?': 256 stoptime(); 257 if (help() < 0) 258 showstr("Can't open help file", 1); 259 starttime(); 260 break; 261 default: 262 if (!islower(ch)) 263 break; 264 if ((int) (p - q) == MAXWORDLEN) { 265 p = q; 266 badword(); 267 break; 268 } 269 *p++ = ch; 270 addch(ch); 271 refresh(); 272 break; 273 } 274 } 275 *p = '\0'; 276 if (ch == EOF) 277 return(NULL); 278 return(q); 279 } 280 281 int 282 inputch(void) 283 { 284 int ch; 285 286 if ((ch = getch()) == ERR) 287 err(1, "cannot read input"); 288 return (ch & 0177); 289 } 290 291 void 292 redraw(void) 293 { 294 clearok(stdscr, 1); 295 refresh(); 296 } 297 298 void 299 flushin(FILE *fp) 300 { 301 tcflush(fileno(fp), TCIFLUSH); 302 } 303 304 /* 305 * Stop the game timer 306 */ 307 void 308 stoptime(void) 309 { 310 time_t t; 311 312 time(&t); 313 gone = (int) (t - start_t); 314 } 315 316 /* 317 * Restart the game timer 318 */ 319 void 320 starttime(void) 321 { 322 time_t t; 323 324 time(&t); 325 start_t = t - (long) gone; 326 } 327 328 /* 329 * Initialize for the display of the player's words as they are typed 330 * This display starts at (LIST_LINE, LIST_COL) and goes "down" until the last 331 * line. After the last line a new column is started at LIST_LINE 332 * Keep track of each column position for showword() 333 * There is no check for exceeding COLS 334 */ 335 void 336 startwords(void) 337 { 338 crow = LIST_LINE; 339 ccol = LIST_COL; 340 maxw = 0; 341 ncolstarts = 1; 342 colstarts[0] = LIST_COL; 343 move(LIST_LINE, LIST_COL); 344 refresh(); 345 } 346 347 /* 348 * Add a word to the list and start a new column if necessary 349 * The maximum width of the current column is maintained so we know where 350 * to start the next column 351 */ 352 void 353 addword(char *w) 354 { 355 int n; 356 357 if (crow == lastline) { 358 crow = LIST_LINE; 359 ccol += (maxw + 5); 360 colstarts[ncolstarts++] = ccol; 361 maxw = 0; 362 move(crow, ccol); 363 } 364 else { 365 move(++crow, ccol); 366 if ((n = strlen(w)) > maxw) 367 maxw = n; 368 } 369 refresh(); 370 } 371 372 /* 373 * The current word is unacceptable so erase it 374 */ 375 void 376 badword(void) 377 { 378 move(crow, ccol); 379 clrtoeol(); 380 refresh(); 381 } 382 383 /* 384 * Highlight the nth word in the list (starting with word 0) 385 * No check for wild arg 386 */ 387 void 388 showword(int n) 389 { 390 int col, row; 391 392 row = LIST_LINE + n % (lastline - LIST_LINE + 1); 393 col = colstarts[n / (lastline - LIST_LINE + 1)]; 394 move(row, col); 395 standout(); 396 printw("%s", pword[n]); 397 standend(); 398 move(crow, ccol); 399 refresh(); 400 delay(15); 401 move(row, col); 402 printw("%s", pword[n]); 403 move(crow, ccol); 404 refresh(); 405 } 406 407 /* 408 * Walk the path of a word, refreshing the letters, 409 * optionally pausing after each 410 */ 411 static void 412 doword(int pause, int r, int c) 413 { 414 int i, row, col; 415 unsigned char ch; 416 417 for (i = 0; wordpath[i] != -1; i++) { 418 row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1; 419 col = BOARD_COL + (wordpath[i] % 4) * 4 + 2; 420 move(row, col); 421 ch = board[wordpath[i]]; 422 if (HISET(ch)) 423 attron(A_BOLD); 424 if (SEVENBIT(ch) == 'q') 425 printw("Qu"); 426 else 427 printw("%c", toupper(SEVENBIT(ch))); 428 if (HISET(ch)) 429 attroff(A_BOLD); 430 if (pause) { 431 move(r, c); 432 refresh(); 433 delay(5); 434 } 435 } 436 } 437 438 /* 439 * Get a word from the user and check if it is in either of the two 440 * word lists 441 * If it's found, show the word on the board for a short time and then 442 * erase the word 443 * 444 * Note: this function knows about the format of the board 445 */ 446 void 447 findword(void) 448 { 449 int c, found, i, r; 450 char buf[MAXWORDLEN + 1]; 451 452 getyx(stdscr, r, c); 453 getword(buf); 454 found = 0; 455 for (i = 0; i < npwords; i++) { 456 if (strcmp(buf, pword[i]) == 0) { 457 found = 1; 458 break; 459 } 460 } 461 if (!found) { 462 for (i = 0; i < nmwords; i++) { 463 if (strcmp(buf, mword[i]) == 0) { 464 found = 1; 465 break; 466 } 467 } 468 } 469 for (i = 0; i < MAXWORDLEN; i++) 470 wordpath[i] = -1; 471 usedbits = 0; 472 if (!found || checkword(buf, -1, wordpath) == -1) { 473 move(r, c); 474 clrtoeol(); 475 addstr("[???]"); 476 refresh(); 477 delay(10); 478 move(r, c); 479 clrtoeol(); 480 refresh(); 481 return; 482 } 483 484 standout(); 485 doword(1, r, c); 486 standend(); 487 doword(0, r, c); 488 489 move(r, c); 490 clrtoeol(); 491 refresh(); 492 } 493 494 /* 495 * Display a string at the current cursor position for the given number of secs 496 */ 497 void 498 showstr(const char *str, int delaysecs) 499 { 500 addstr(str); 501 refresh(); 502 delay(delaysecs * 10); 503 move(crow, ccol); 504 clrtoeol(); 505 refresh(); 506 } 507 508 void 509 putstr(char *s) 510 { 511 addstr(s); 512 } 513 514 /* 515 * Get a valid word and put it in the buffer 516 */ 517 void 518 getword(char *q) 519 { 520 int ch, col, done, i, row; 521 char *p; 522 523 done = 0; 524 i = 0; 525 p = q; 526 addch('['); 527 refresh(); 528 while (!done && i < MAXWORDLEN - 1) { 529 ch = inputch(); 530 switch (ch) { 531 case '\177': /* <del> */ 532 case '\010': /* <bs> */ 533 if (p == q) 534 break; 535 p--; 536 getyx(stdscr, row, col); 537 move(row, col - 1); 538 clrtoeol(); 539 break; 540 case '\025': /* <^u> */ 541 case '\027': /* <^w> */ 542 if (p == q) 543 break; 544 getyx(stdscr, row, col); 545 move(row, col - (int) (p - q)); 546 p = q; 547 clrtoeol(); 548 break; 549 case ' ': 550 case '\n': 551 case '\r': 552 done = 1; 553 break; 554 case '\014': /* <^l> */ 555 case '\022': /* <^r> */ 556 clearok(stdscr, 1); 557 refresh(); 558 break; 559 default: 560 if (islower(ch)) { 561 *p++ = ch; 562 addch(ch); 563 i++; 564 } 565 break; 566 } 567 refresh(); 568 } 569 *p = '\0'; 570 addch(']'); 571 refresh(); 572 } 573 574 void 575 showboard(char *b) 576 { 577 tty_showboard(b); 578 } 579 580 void 581 prompt(const char *mesg) 582 { 583 move(PROMPT_LINE, PROMPT_COL); 584 printw("%s", mesg); 585 move(PROMPT_LINE + 1, PROMPT_COL); 586 refresh(); 587 } 588 589 static int 590 tty_setup(void) 591 { 592 initscr(); 593 raw(); 594 noecho(); 595 596 /* 597 * Does curses look at the winsize structure? 598 * Should handle SIGWINCH ... 599 */ 600 nlines = LINES; 601 lastline = nlines - 1; 602 ncols = COLS; 603 604 signal(SIGTSTP, stop_catcher); 605 signal(SIGCONT, cont_catcher); 606 signal(SIGWINCH, winch_catcher); 607 return(0); 608 } 609 610 static void 611 stop_catcher(int signo __unused) 612 { 613 sigset_t sigset, osigset; 614 615 stoptime(); 616 noraw(); 617 echo(); 618 move(nlines - 1, 0); 619 refresh(); 620 621 signal(SIGTSTP, SIG_DFL); 622 sigemptyset(&sigset); 623 sigaddset(&sigset, SIGTSTP); 624 sigprocmask(SIG_UNBLOCK, &sigset, &osigset); 625 kill(0, SIGTSTP); 626 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 627 signal(SIGTSTP, stop_catcher); 628 } 629 630 static void 631 cont_catcher(int signo __unused) 632 { 633 noecho(); 634 raw(); 635 clearok(stdscr, 1); 636 move(crow, ccol); 637 refresh(); 638 starttime(); 639 } 640 641 /* 642 * The signal is caught but nothing is done about it... 643 * It would mean reformatting the entire display 644 */ 645 static void 646 winch_catcher(int signo __unused) 647 { 648 struct winsize win; 649 650 signal(SIGWINCH, winch_catcher); 651 ioctl(fileno(stdout), TIOCGWINSZ, &win); 652 /* 653 LINES = win.ws_row; 654 COLS = win.ws_col; 655 */ 656 } 657 658 static void 659 tty_cleanup(void) 660 { 661 move(nlines - 1, 0); 662 refresh(); 663 noraw(); 664 echo(); 665 endwin(); 666 } 667 668 static void 669 tty_showboard(char *b) 670 { 671 unsigned int i; 672 int line; 673 char ch; 674 675 clear(); 676 move(BOARD_LINE, BOARD_COL); 677 line = BOARD_LINE; 678 printw(separator); 679 move(++line, BOARD_COL); 680 for (i = 0; i < ncubes; i++) { 681 printw("| "); 682 ch = SEVENBIT(b[i]); 683 if (HISET(b[i])) 684 attron(A_BOLD); 685 if (ch == 'q') 686 printw("Qu"); 687 else 688 printw("%c ", toupper((unsigned char)ch)); 689 if (HISET(b[i])) 690 attroff(A_BOLD); 691 if ((i + 1) % grid == 0) { 692 printw("|"); 693 move(++line, BOARD_COL); 694 printw(separator); 695 move(++line, BOARD_COL); 696 } 697 } 698 move(SCORE_LINE, SCORE_COL); 699 printw("Type '?' for help"); 700 refresh(); 701 } 702