home *** CD-ROM | disk | FTP | other *** search
- /* fe_curses.c 9/5/91
- *
- * Copyright 1991 Perry R. Ross
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation without fee is hereby granted, subject to the restrictions
- * detailed in the README file, which is included here by reference.
- * Any other use requires written permission from the author. This software
- * is distributed "as is" without any warranty, including any implied
- * warranties of merchantability or fitness for a particular purpose.
- * The author shall not be liable for any damages resulting from the
- * use of this software. By using this software, the user agrees
- * to these terms.
- */
-
- #include "ldb.h"
-
- /*======================================================================
- * This file is the "curses" front-end for ldb. It performs all
- * display output and keyboard input in a way that will (hopefully)
- * allow other front-ends to be added later. These could include
- * MAC's (blech), IBM PC's (double blech), and X, although it must
- * be stated that, as of this writing, ldb is not organized in an
- * event-driven manner, so it will take more work to port to X than
- * just writing fe_motif.c or what have you. But I'm working on it.
- *
- * All publicly-accessible functions in the front-end begin with Fe.
- * These are the functions that must be written to implement a new
- * front-end. There are a few private functions in this file, which
- * begin with P. These are used internally by fe_curses.c and need not
- * be implemented in other front-ends.
- *
- * The front-end is activated by calling FeInitialize. In addition to
- * performing any required initialization, it is mandatory for FeInitialize
- * to set FeIsActive to non-zero. The front-end is closed down by
- * calling FeFinishSession, which must set FeIsActive to 0. No calls
- * to any Fe functions may be made while FeIsActive is 0.
- *======================================================================
- */
-
- PRIVATE char PGetChr();
- PRIVATE PGetString();
- PRIVATE PDrawComment();
-
- /* VAX C doesn't have tgetstr, but if you're using vax-c, */
- /* you're probably using a DEC terminal anyway. */
- #ifdef vaxc
- #define PClearScreen() fputs("\33[H\33[2J",stdout);fflush(stdout)
- #else
- PRIVATE PClearScreen();
- #endif
-
-
- /*----------------------------------------------------------------------
- * FeInitialize -- initialize the front end
- *
- * This function initializes the curses package, turns off echo,
- * turns on cbreak mode (to allow reading one character at a time),
- * turns off mapping return to newline, and sets the FeIsActive flag.
- *----------------------------------------------------------------------
- */
-
- FeInitialize()
- {
-
- initscr();
- noecho();
- cbreak();
- nonl();
- FeIsActive = 1;
- }
-
-
- /*----------------------------------------------------------------------
- * FeFinishSession -- shut down the front end
- *
- * This function moves the cursor to the bottom of the screen, clears
- * the bottom line, closes down the curses package, and clears the
- * FeIsActive flag.
- *----------------------------------------------------------------------
- */
-
- FeFinishSession()
- {
-
- if (FeIsActive) {
- move(23,0);
- clrtoeol();
- refresh();
- endwin();
- FeIsActive = 0;
- }
- }
-
-
- /*----------------------------------------------------------------------
- * FeDrawScreen -- draw the constant parts of the screen
- *
- * This function draws the parts of the screen that don't change for
- * each game. This includes the board outline and a few other
- * miscellaneous things.
- *----------------------------------------------------------------------
- */
-
- FeDrawScreen()
- {
- static char horz[] = "_____________________________________________________";
- int i;
-
- clear();
- mvaddstr(2,5,horz);
- mvaddstr(17,5,horz);
- for (i = 3; i < 18; i++) {
- mvaddch(i,5,'|');
- mvaddch(i,29,'|');
- mvaddch(i,33,'|');
- mvaddch(i,57,'|');
- }
- mvaddstr(10,30,"BAR");
- mvaddstr(3,61,"----------------");
- mvaddstr(4,63,"Roll Move");
- mvaddstr(12,61,"----------------");
- mvaddstr(13,63,"Roll Move");
- mvaddstr(18,63,"-----[ ]------");
- for (i = 18; i < 24; i++)
- mvaddch(i,62,'|');
- mvaddstr(18,0,"Messages:");
- mvaddstr(19,0,"Sent:");
- mvaddstr(21,0,"Rcvd:");
- refresh();
- }
-
-
-
- /*----------------------------------------------------------------------
- * FeDrawGame -- draw all items associated with a game
- *
- * This function displays all information related to a specific game.
- * This includes the point labels, move blocks, cube; shoot, just
- * about everything you can think of.
- *----------------------------------------------------------------------
- */
-
- FeDrawGame(g)
- struct game *g;
- {
- int i, p, r1, r2, p1, p2;
- char blots[12], tmp[60], *n;
- static char pts1[] = " 1 2 3 4 5 6 | | 7 8 9 10 11 12";
- static char pts2[] = "24 23 22 21 20 19 | |18 17 16 15 14 13";
-
- move(0,0);
- clrtoeol();
- move(0,0);
- n = (g->opname != NULL) ? g->opname : "UNKNOWN";
- printw("Playing: %s (%s)",g->opaddr,n); /* who am I playing? */
- if (g->flags & F_INVERT) { /* board is inverted? */
- mvaddstr(16,6,pts2); /* draw inverted point labels */
- mvaddstr(4,6,pts1);
- r1 = 11; /* remember which move block to use */
- r2 = 2;
- p = 1;
- }
- else {
- mvaddstr(4,6,pts2); /* draw normal point labels */
- mvaddstr(16,6,pts1);
- r1 = 2; /* remember which move block to use */
- r2 = 11;
- p = 0;
- }
- if (g->mydir > 0) { /* I'm playing up, switch move blocks */
- i = r1;
- r1 = r2;
- r2 = i;
- p = 1 - p;
- }
- move(r1,61);
- clrtoeol(); /* label the move blocks */
- sprintf(tmp,"Opponent (%s)",colorname(g->opcolor));
- i = 61 + ( (16 - strlen(tmp)) >> 1 ); /* center the string */
- mvaddstr(r1,i,tmp);
- move(r2,61);
- clrtoeol();
- sprintf(tmp,"You (%s)",colorname(g->mycolor));
- i = 61 + ( (16 - strlen(tmp)) >> 1 );
- mvaddstr(r2,i,tmp);
-
- FeLabelBoard(bdlabels[g->curbd]); /* which board am I looking at? */
- switch (g->curbd) { /* which board should I draw? */
- case BD_BEFOP:
- FeDrawBoard(g->opbd,g->opmvs,g->opdir,0,g->flags & F_INVERT);
- break;
- case BD_AFTOP:
- FeDrawBoard(g->mybd,g->opmvs,g->opdir,1,g->flags & F_INVERT);
- break;
- case BD_CUR:
- FeDrawBoard(g->board,NULL,g->mydir,0,g->flags & F_INVERT);
- break;
- }
- mvaddch(5,59,'|'); /* draw those little arrows */
- mvaddch(14,59,'|'); /* that tell us which direction */
- mvaddch(15,59,'|'); /* we are going */
- if (p == 0) {
- mvaddstr(4,1,"-->");
- mvaddstr(4,58,"--");
- mvaddch(6,59,'V');
- mvaddstr(16,58,"<-");
- mvaddstr(16,1,"<--");
- mvaddch(14,58,' ');
- mvaddch(14,60,' ');
- mvaddch(13,59,' ');
- }
- else {
- mvaddstr(4,1,"<--");
- mvaddstr(4,58,"<-");
- mvaddch(6,59,'|');
- mvaddstr(16,58,"--");
- mvaddstr(16,1,"-->");
- mvaddch(14,58,'/');
- mvaddch(14,60,'\\');
- mvaddch(13,59,'.');
- }
- *blots = '\0'; /* did any of our blots get hit? */
- for (i = 0, p = 0; i < 4; i++) {
- FeDrawMove(g,0,i); /* draw my moves */
- FeDrawMove(g,1,i); /* draw opponent's moves */
- if (g->blot[i] > 0) {
- strcat(blots," "); /* add a blot to the list */
- sprintf(tmp,"%d",g->blot[i]);
- strcat(blots,tmp);
- p++;
- }
- }
- FeDrawCube(g); /* draw the current game value */
- PDrawComment(0, g); /* draw my old comment */
- PDrawComment(1, g); /* draw opponent's comment */
- if (g->state == ST_MYACCEPT)
- strcpy(tmp,"Opponent has doubled.");
- else if (g->state == ST_GAMEOVER) { /* game is over, find out why */
- switch (g->term) {
- case T_IWIN: /* I won, check for gammon/backgammon */
- if (g->board[OFFPT(g->opdir)].qty == 0) {
- p1 = (g->mydir > 0) ? 19 : 0; /* check my inner tbl*/
- p2 = (g->mydir > 0) ? 25 : 6; /* for op's pieces */
- if (addpcs(g->board,g->opcolor,p1,p2) > 0)
- sprintf(tmp,"Backgammon! You win %d points.",
- 3*g->gameval);
- else
- sprintf(tmp,"Gammon! You win %d points.",
- 2*g->gameval);
- }
- else
- sprintf(tmp,"You win! Game value was %d point%s.",
- g->gameval,(g->gameval == 1) ? "" : "s");
- break;
- case T_ILOSE: /* I lost, check for gammon/backgammon */
- if (g->board[OFFPT(g->mydir)].qty == 0) {
- p1 = (g->opdir > 0) ? 19 : 0;/* check op's inner tbl*/
- p2 = (g->opdir > 0) ? 25 : 6; /* for my pieces */
- if (addpcs(g->board,g->mycolor,p1,p2) > 0)
- sprintf(tmp,"Backgammon! You lose %d points.",
- 3*g->gameval);
- else
- sprintf(tmp,"Gammon! You lose %d points.",
- 2*g->gameval);
- }
- else
- sprintf(tmp,"You lose. Game value was %d point%s.",
- g->gameval,(g->gameval == 1) ? "" : "s");
- break;
- case T_ICONCEDE: /* I wimped out */
- sprintf(tmp,"You conceded. You lose %d point%s.",
- g->gameval,(g->gameval == 1) ? "" : "s");
- break;
- case T_OPCONCEDE: /* Opponent wimped out */
- sprintf(tmp,"Opponent conceded. You win %d point%s.",
- g->gameval,(g->gameval == 1) ? "" : "s");
- break;
- case T_IDECLINE: /* I declined the double */
- sprintf(tmp,"Double declined. You lose %d point%s.",
- g->gameval,(g->gameval == 1) ? "" : "s");
- break;
- case T_OPDECLINE: /* Opponent declined my double */
- sprintf(tmp,"Double declined. You win %d point%s.",
- g->gameval,(g->gameval == 1) ? "" : "s");
- break;
- }
- }
- else if (*blots)
- sprintf(tmp,"Blot%s hit:%s",(p == 1) ? "" : "s",blots);
- else
- *tmp = '\0';
- FeStatusLine(tmp);
- FeMessage(g->dispmsg); /* put message (if any) on message line */
- if (g->dispmsg != NULL) { /* if there was a message, it has been */
- free(g->dispmsg); /* displayed, so it can thrown away */
- g->dispmsg = NULL;
- }
- refresh();
- }
-
-
- /*----------------------------------------------------------------------
- * FeDrawPoint -- draw all pieces on a point
- *
- * This function redraws all 15 slots on a point. It is passed a
- * board image and the index of the point to draw, from which it
- * extracts the number of pieces on that point and the character used
- * to represent pieces. It will draw as many of these pieces as
- * exist on the point, and will draw blanks over the remaining slots
- * to erase any pieces that may have existed before.
- * If the nh argument is greater than 0, it specifies how many of
- * the pieces drawn should be highlighted. This is used to highlight
- * pieces moved by the opponent.
- *----------------------------------------------------------------------
- */
-
- FeDrawPoint(b,pt,nh,inv)
- board b; /* the board array */
- int pt; /* which point are we to draw */
- int nh; /* how many pieces should be highlighted */
- int inv; /* is the board inverted? */
- {
- static int cols[BOARDSIZE] = { 31,7,11,15,19,23,27,35,39,43,47,51,55,
- 55,51,47,43,39,35,27,23,19,15,11,7,31,2,2};
- int sr, r; /* the row we are at */
- int c; /* the column we are at */
- int d; /* which direction does the column grow 1/-1 */
- int i; /* counter */
- char x; /* char to draw piece with */
- int nn; /* number of normal pieces */
-
- if ( (pt > 12) && (pt != DOWNOFF)) {
- sr = inv ? 15 : 5; /* starting row is 5 (unless inverted) */
- d = inv ? -1 : 1; /* direction is down (unless inverted) */
- }
- else {
- sr = inv ? 5 : 15; /* starting row is 15 (unless inverted) */
- d = inv ? 1 : -1; /* direction is up (unless inverted) */
- }
- c = cols[pt];
- x = b[pt].color; /* char to draw piece with */
- if (nh < 0)
- nh = 0;
- else if (nh > b[pt].qty)
- nh = b[pt].qty;
- nn = b[pt].qty - nh; /* how many normal pcs */
- r = sr;
- for (i = 0; i < 15; i++) { /* draw all 15 slots on this point */
- if (nn <= 0) { /* no more normal pieces */
- if (nh <= 0) /* and no highlighted pieces */
- x = ' '; /* so draw blanks */
- else {
- standout(); /* go into highlight mode */
- nh--;
- }
- }
- else
- nn--;
- mvaddch(r,c,x); /* draw this piece */
- standend(); /* make sure we're not highlighting */
- if (i == 4) {
- r = sr; /* reset row */
- c--; /* use col to left of first row */
- }
- else if (i == 9) {
- r = sr; /* reset row */
- c += 2; /* use col to right of first row */
- }
- else
- r += d; /* bump row number */
- }
- }
-
-
- /*----------------------------------------------------------------------
- * FeDrawMove -- draw a line in a move block
- *
- * This function draws one line in a move block. This consists of the
- * value of the roll, the starting and ending position of the piece
- * moved, and an asterisk if the move was from the opponent and hit
- * one of our blots.
- *----------------------------------------------------------------------
- */
-
- FeDrawMove(g,who,mn)
- struct game *g; /* the game structure */
- int who; /* 0 = opponent, 1 = me */
- int mn; /* which move to draw */
- {
- int p, r, d;
- struct mv *m;
-
- d = who ? g->mydir : g->opdir; /* this move upbound or downbound? */
- p = (d > 0); /* upper or lower block? */
- if (g->flags & F_INVERT) /* inverted board */
- p = !p; /* switch move blocks */
- r = mn + (p ? 5 : 14); /* figure out the row number */
- m = who ? &g->mvs[mn] : &g->opmvs[mn]; /* find the move structure */
- move(r,64);
- clrtoeol(); /* clear the old move */
- if (m->roll > 0) {
- move(r,64);
- printw("%d",m->roll); /* draw the roll */
- move(r,69);
- if (m->pt < 0) { /* if it is unused, say so */
- addstr("UNUSED");
- return;
- }
- if ( ( (p = m->pt) == UPBAR) || (m->pt == DOWNBAR) ) {
- p = BARPT(d); /* if coming off bar, say so */
- printw("BAR-");
- }
- else
- printw("%d-",m->pt); /* draw starting point */
- if ( ( (p += d*m->roll) <= 0) || (p >= 25) )
- printw("OFF"); /* if bearing off, say so */
- else
- printw("%d",p); /* draw ending point */
- if ( (who == 0) && g->blot[mn]) /* if opponent move hit a blot */
- mvaddch(r,76,'*'); /* mark it */
- }
- }
-
-
- /*----------------------------------------------------------------------
- * FeDrawBoard -- draw all points on a board
- *
- * This is a convenience function that calls FeDrawPoint for all
- * points on a board. It takes as an argument an array of moves,
- * as well as an argument that determines whether DrawPoint should be
- * instructed to highlight the source of those moves, the destination,
- * or nothing.
- *----------------------------------------------------------------------
- */
-
- FeDrawBoard(b,mvs,dir,sd,inv)
- board b; /* board image */
- struct mv mvs[4]; /* moves to highlight (NULL = none) */
- int dir; /* direction */
- int sd; /* 0=highlight source, 1=dest */
- int inv; /* is the board inverted? */
- {
- int i, s, e;
- static char hcnt[BOARDSIZE]; /* number of pieces to highlight */
-
- for (i = 0; i < BOARDSIZE; i++)
- hcnt[i] = 0; /* init to no highlight */
- if (mvs != NULL) { /* find all points that should be highlighted */
- for (i = 0; i < 4; i++) {
- if ( (mvs[i].roll <= 0) || ( (s = mvs[i].pt) < 0) )
- continue; /* this move is unused */
- if ( (s < 1) || (s > 24) ) /* if coming off bar */
- s = BARPT(dir); /* use correct bar point */
- e = s + dir*mvs[i].roll; /* add in the roll used */
- if ( (e < 1) || (e > 24) ) /* off the board */
- e = OFFPT(dir); /* use correct off point */
- if (sd > 0) { /* we are showing dest */
- hcnt[e]++; /* inc destination count */
- hcnt[s]--; /* handle continued moves */
- }
- else { /* we are showing start */
- hcnt[s]++; /* inc start count */
- hcnt[e]--; /* handle continued moves */
- }
- }
- }
- for (i = 0; i < BOARDSIZE; i++) /* draw each point */
- FeDrawPoint(b,i,hcnt[i],inv);
- }
-
-
- /*----------------------------------------------------------------------
- * FeLabelBoard -- center a string above the board.
- *
- * This function is used to label which board the user is seeing.
- *----------------------------------------------------------------------
- */
-
- FeLabelBoard(s)
- char *s;
- {
- int i;
-
- if (strlen(s) > 50) /* 50 chars is all the room we have */
- s[50] = '\0'; /* chop it off */
- mvaddstr(1,5,BLANKS(50)); /* clear old contents */
- if ( (i = 31 - strlen(s)/2) < 0)
- i = 0;
- mvaddstr(1,i,s); /* draw the string */
- }
-
-
- /*----------------------------------------------------------------------
- * FeGetPoint -- read a point number from the user
- *
- * This function prompts the user for a point number. The user types
- * the input on the line of the move block corresponding to the
- * roll being used. Normally, the input is a number between 1 and 24.
- * Numbers that do not use 2 digits (i.e. 1-9) must have a non-digit
- * character typed after them; alternatively, the user may enter them
- * with a leading 0. There are a number of special characters that
- * are also recognized:
- * char return value
- * ---------------------------------------------------------------
- * space the "spdflt" argument.
- * return/linefeed the "crdflt" argument.
- * DEL/ESC/BS cancel move.
- * b/B the user's bar point.
- * p/P the point from which a piece would have
- * to be moved to land on spdflt.
- * o/O The point from which the selected roll
- * could be used to bear off. If that point
- * is unoccupied, the next lower occupied
- * point is returned.
- *----------------------------------------------------------------------
- */
-
- FeGetPoint(g,r,crdflt,spdflt)
- struct game *g; /* game structure */
- int r; /* which row in move block */
- int crdflt; /* what to return for cr/nl */
- int spdflt; /* what to return for space */
- {
- int n, row;
- char buf[4];
-
- row = r;
- if (g->flags & F_INVERT) /* which move block do I use? */
- row += (g->mydir < 0) ? 5 : 14; /* inverted board */
- else
- row += (g->mydir > 0) ? 5 : 14; /* normal board */
- move(row,69);
- clrtoeol();
- move(row,69);
- refresh();
- *buf = PGetChr(0);
- if ( (*buf == '\n') || (*buf == '\r') ) /* return means repeat move */
- return(crdflt);
- if (*buf == ' ') /* space means continue move */
- return(spdflt);
- if ( (*buf == '\177') || (*buf == '\033') || (*buf == '\b') )
- return(-1); /* DEL/ESC/BS means cancel move */
- if ( (*buf == 'b') || (*buf == 'B') ) /* bar */
- return(BARPT(g->mydir));
- if ( (*buf == 'p') || (*buf == 'P') ) { /* P means spdflt - roll*dir */
- n = spdflt - g->mvs[r].roll*g->mydir; /* make point */
- if ( (n < 1) || (n > 24) ) /* not on board */
- n = 99; /* force invalid point */
- return(n);
- }
- if ( (*buf == 'o') || (*buf == 'O') ) { /* O means bear a piece off */
- n = ( (g->mydir > 0) ? 25 : 0 ) - g->mydir*g->mvs[r].roll;
- while ( (n > 0) && (n <= 24) &&
- ( (g->board[n].qty <= 0) || (g->board[n].color != g->mycolor)) )
- n += g->mydir;
- if ( (n < 1) || (n > 24) ) /* no piece found */
- n = 99; /* force invalid point */
- return(n);
- }
- if ( ! isdigit(*buf))
- return(99); /* force invalid point message */
- addch(*buf); /* echo the char */
- refresh();
- buf[1] = PGetChr(0);
- buf[2] = '\0'; /* null terminate */
- if ( ((n = atoi(buf)) == UPBAR) || (n == DOWNBAR) )
- return(BARPT(g->mydir));
- return(n);
- }
-
-
- /*----------------------------------------------------------------------
- * PGetChr -- get a single character
- *
- * This function gets one character from the user and returns it.
- * If the "e" argument is non-zero, the character is echoed at the
- * current cursor position. The ^L and ^R characters are intercepted
- * and cause the screen to be redrawn without returning from PGetChr.
- *----------------------------------------------------------------------
- */
-
- PRIVATE char PGetChr(e)
- int e;
- {
- char c;
- int y, x;
-
- loop:
- if ( ((c = getch() & 0x7f) == 0x0c) || (c == 0x12) ) { /* ^L or ^R? */
- wrefresh(curscr); /* repaint current screen */
- goto loop; /* and get another char */
- }
- if (c == rc.superkey) { /* uh oh, we're busted */
- getyx(stdscr,y,x); /* save old cursor postition */
- PClearScreen(); /* get the screen cleared fast */
- nl(); /* set tty back to normal */
- echo();
- system(rc.supercmd); /* run the supervisor command */
- nonl(); /* ok, we're safe again */
- noecho();
- PClearScreen(); /* get the screen cleared fast */
- wrefresh(curscr); /* redisplay the game */
- move(y,x); /* put cursor back where it was */
- refresh();
- goto loop; /* and get another character */
- }
- if (e && isprint(c)) { /* echo char? */
- addch(c); /* yup */
- refresh();
- }
- return(c);
- }
-
-
- /*----------------------------------------------------------------------
- * PClearScreen -- clear the screen somehow
- *
- * This function clears the physical display without affecting what
- * the curses package thinks is there. If the "cl" (clear screen)
- * capability is defined, it uses that. If that fails, it tries
- * to move to 0,0 and use the "cd" (clear to end of display).
- * Failing that, it goes to the bottom of the screen and scrolls
- * it 24 times.
- *----------------------------------------------------------------------
- */
-
- #ifndef vaxc
- PRIVATE PClearScreen()
- {
- char *s, *x, buf[80];
-
- x = buf;
- if ( (s = tgetstr("cl",&x)) == NULL) { /* no clear screen */
- if ( (s = tgetstr("cd",&x)) != NULL) { /* do we have clr to end? */
- move(0,0); /* yup, use it */
- refresh();
- fputs(s,stdout);
- }
- else { /* well, do it the hard way */
- move(23,0);
- refresh();
- printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
- }
- }
- else
- fputs(s,stdout); /* send clear screen */
- fflush(stdout); /* git along, li'l characters */
- }
- #endif
-
-
- /*----------------------------------------------------------------------
- * FeGetComment -- prompt for a comment to send along with a move
- *
- * This function allows the user to enter a 2-line comment. If the
- * user types a non-empty string, it is stored in the mycmt/mycmt2
- * fields of the game structure passed as an argument. The previous comment,
- * if any, is discarded, as is the last comment received from the opponent.
- *----------------------------------------------------------------------
- */
-
- FeGetComment(g)
- struct game *g;
- {
- char buf[120];
- char *lptrs[2];
- int n;
-
- if (g->mycmt != NULL) /* throw away old comment */
- free(g->mycmt);
- if (g->mycmt2 != NULL)
- free(g->mycmt2);
- if (g->opcmt != NULL) { /* throw away opponent's old comment */
- free(g->opcmt);
- g->opcmt = NULL;
- }
- if (g->opcmt2 != NULL) {
- free(g->opcmt2);
- g->opcmt2 = NULL;
- }
- n = PGetString(buf,56,lptrs,2,19,6); /* get new comment */
- g->mycmt = NULL; /* mark comment as empty */
- g->mycmt2 = NULL;
- if (n > 0)
- g->mycmt = save(lptrs[0]); /* save first line */
- if (n > 1)
- g->mycmt2 = save(lptrs[1]); /* save second line */
- }
-
-
-
- /*----------------------------------------------------------------------
- * PGetString -- read a multi-line string from the user
- *
- * This function allows the user to enter multiple lines of a fixed maximum
- * length. The normal line editing characters are recognized and
- * processed. These include:
- * DEL/BS The character before the cursor is deleted.
- * ^X/^U The entire line is erased.
- * ^L/^R The screen is redrawn.
- * ^W/^B The word before the cursor is erased.
- * Typing past the end of a line automatically causes a word-
- * wrap to the next line. Words are delimited by spaces. Typing
- * a carriage return or line feed on the last line, or on an empty
- * line, terminates PGetString; otherwise, it moves to the next line.
- * ESC terminates PGetString regardless of the cursor position.
- * Backspacing from the beginning of one line to the end of the previous
- * is allowed.
- *----------------------------------------------------------------------
- */
-
- PRIVATE PGetString(buf,len,lines,nls,y,x)
- char *buf, *lines[];
- int len, nls, y, x;
- {
- char c, *bp, *p;
- int cl, l;
-
- for (cl = 0; cl < nls; cl++) {
- lines[cl] = NULL; /* clear line pointers */
- mvaddstr(y+cl,x,BLANKS(len)); /* clear line */
- }
- cl = 0; /* current line = 0 */
- l = 0; /* length of current line */
- move(y,x); /* go to first location in field */
- bp = buf; /* ptr to next storage location */
- lines[0] = buf; /* init first line pointer */
- refresh();
- while (1) {
- switch (c = PGetChr(0)) {
- case '\177': /* DEL */
- case '\b': /* BS */
- if (l <= 0) { /* at beginning of line */
- if (cl <= 0) /* on first line */
- break; /* nothing to delete */
- cl--; /* back up one line */
- *--bp = '\0'; /* back up buffer pointer */
- l = strlen(lines[cl]); /* load line length */
- move(y+cl,x+l); /* move cursor to end of prev line */
- }
- else {
- bp--; /* back up buffer pointer */
- l--; /* decrement length */
- move(y+cl,x+l);
- addch(' '); /* erase the char from the screen */
- move(y+cl,x+l);
- }
- refresh();
- break;
- case '\2': /* ^B -- erase previous character */
- case '\27': /* ^W */
- if (l <= 0) { /* beginning of line */
- if (cl <= 0) /* on first line */
- break; /* nothing to delete */
- cl--; /* back up one line */
- *--bp = '\0'; /* back up buffer pointer */
- l = strlen(lines[cl]); /* load line length */
- }
- while (l > 0) { /* skip leading spaces, if any */
- if (*--bp != ' ') {
- bp++;
- break;
- }
- l--;
- *bp = '\0';
- }
- while (l > 0) { /* delete to last space */
- if (*--bp == ' ') {
- bp++;
- break;
- }
- *bp = '\0';
- l--;
- mvaddch(y+cl,x+l,' ');
- }
- move(y+cl,x+l);
- refresh();
- break;
- case '\33': /* ESC */
- *bp++ = '\0'; /* terminate the string */
- if (l <= 0) /* empty line */
- return(cl); /* don't include it in #lines */
- return(cl+1); /* return number of lines */
- case '\r': /* CR */
- case '\n': /* NL */
- *bp++ = '\0'; /* terminate the string */
- if (l <= 0) /* empty line */
- return(cl); /* don't include it in #lines */
- if (cl >= nls-1) /* last line */
- return(cl+1); /* return number of lines */
- lines[++cl] = bp; /* go to next line */
- l = 0;
- move(y+cl,x);
- refresh();
- break;
- case '\30': /* ^X -- erase entire line & goto prev line */
- case '\25': /* ^U */
- mvaddstr(y+cl,x,BLANKS(len));
- if (cl > 0) { /* back up one line */
- bp = lines[cl--] - 1;
- l = strlen(lines[cl]);
- }
- else { /* already on top line, go to beginning */
- bp = buf;
- l = 0;
- }
- move(y+cl,x+l);
- refresh();
- break;
- case '\t': /* convert tab to a space */
- c = ' ';
- /* fall through */
- default:
- if (iscntrl(c)){ /* bad char */
- fputc('\7',stderr); /* beep */
- fflush(stderr);
- break; /* & ignore character */
- }
- if (l >= len) { /* typed up to end of line */
- if (cl >= nls-1) { /* last line, can't go on */
- fputc('\7',stderr); /* beep */
- fflush(stderr);
- break; /* & ignore character */
- }
- *bp++ = c; /* store rcvd char */
- for (p = bp-1; (l > 0) && (! isspace(*p)); p--, l--);
- if ( (p <= buf) || (*p == '\0') ) {
- *bp++ = '\0';
- lines[++cl] = bp; /* didn't find word */
- l = 0;
- }
- else {
- *p++ = '\0'; /* terminate previous line */
- mvaddstr(y+cl,x,BLANKS(len)); /* redraw */
- mvaddstr(y+cl,x,lines[cl]); /* w/o word */
- lines[++cl] = p; /* start new line */
- *bp = '\0'; /* terminate word */
- l = strlen(p); /* set line len */
- mvaddstr(y+cl,x,p); /* draw word */
- }
- move(y+cl,x+l);
- }
- else {
- *bp++ = c; /* put char in string */
- l++; /* bump length */
- addch(c); /* echo char to screen */
- }
- refresh();
- break;
- }
- }
- }
-
-
- /*----------------------------------------------------------------------
- * FeDrawMenu -- draw menu choices in menu box
- *
- * This function takes an array of strings, terminated by a NULL
- * pointer, and writes each string into successive lines of the
- * menu box in the bottom right corner of the screen. If there are
- * more strings than will fit in the box, the extra strings are
- * ignored. If NULL is passed as the menu array, the menu box
- * is cleared.
- *----------------------------------------------------------------------
- */
-
- FeDrawMenu(m)
- char *m[];
- {
- int i;
-
- for (i = 0; i < 5; i++) { /* clear all lines in menu box */
- move(19+i,63);
- clrtoeol();
- }
- if (m == NULL) /* no menu to display */
- return;
- for (i = 0; (m[i] != NULL) && (i < 5); i++) {
- if (strlen(m[i]) > 15) /* menu string is too long */
- m[15] = '\0'; /* so shorten it */
- mvaddstr(19+i,64,m[i]); /* put string in menu area */
- }
- refresh();
- }
-
-
- /*----------------------------------------------------------------------
- * FeMenu -- get menu choice from user
- *
- * This function accepts a menu choice from the user. The menu choices
- * are passed as an array of strings, terminated by a NULL pointer.
- * Users select one of these strings by typing the first letter of the
- * string, thus the first letter of the strings must be unique.
- *
- * FeMenu also handles two special cases:
- * 1. The user types a number between 1 and 6 (a roll)
- * 2. The user types some character that the caller wishes
- * to handle directly.
- * If a roll entry is valid, the caller may pass up to two different
- * rolls that are valid in the r1 and r2 arguments. Any characters
- * the caller wishes to handle are passed as a string in the
- * "extra" argument. These typically include space and newline.
- *----------------------------------------------------------------------
- */
-
- char FeMenu(m,r1,r2,extra)
- char *m[]; /* array of menu choices */
- int r1, r2; /* rolls (pass 0 if no roll valid) */
- char *extra; /* chars that caller wants to handle */
- {
- int i;
- char c, x;
-
- while (1) {
- mvaddch(18,69,' ');
- move(18,69); /* put cursor in its little box */
- refresh();
- c = PGetChr(1); /* get a character */
- FeMessage(NULL); /* clear message line */
- if ( (extra != NULL) && (strchr(extra,c) != NULL) )
- return(c); /* these chars are handled by caller */
- if ( (c >= '1') && (c <= '6') ) { /* handle digits differently */
- if (r1 <= 0) {
- FeMessage("Roll not valid here.");
- continue;
- }
- x = c - '0'; /* convert to number */
- if ( (x == r1) || (x == r2) ) /* is it a valid roll? */
- return(c); /* yup, return it */
- FeMessage("No such roll.");
- continue;
- }
- if (islower(c)) /* ignore case */
- c = toupper(c);
- for (i = 0; m[i] != NULL; i++) { /* search menu strings */
- x = *m[i];
- if (islower(x)) /* ignore case */
- x = toupper(x);
- if (c == x) /* found it */
- return(c);
- }
- FeMessage("Invalid command.");
- }
- }
-
-
- /*----------------------------------------------------------------------
- * FeMessage -- print a highlighted message on bottom line
- *
- * This function prints a string in reverse video on line 23.
- * The message length is restricted to 62 characters to avoid
- * running into the menu box. If NULL is passed as a message,
- * the message line is cleared.
- *----------------------------------------------------------------------
- */
-
- FeMessage(s)
- char *s;
- {
- char c = 0;
-
- mvaddstr(23,0,BLANKS(62)); /* clear message line */
- if (s != NULL) { /* if we have a message to print */
- if (strlen(s) > 62) { /* check that it's not too long */
- c = s[62]; /* save char at this position */
- s[62] = '\0'; /* and end the string */
- }
- move(23,0);
- standout();
- addstr(s); /* print the message */
- standend();
- if (c != '\0') /* if we shortened it, restore it */
- s[62] = c;
- }
- refresh();
- }
-
-
- /*----------------------------------------------------------------------
- * FeStatusLine -- draw string on status line
- *
- * This function puts a string on line 18 in reverse video. It is
- * used to display blots hit, double offers, etc.
- *----------------------------------------------------------------------
- */
-
- FeStatusLine(s)
- char *s;
- {
- char c = 0;
- int l;
-
- mvaddstr(18,10,BLANKS(50)); /* clear status line */
- if (s != NULL) { /* if we have a message to print */
- if ( (l = strlen(s)) > 50) { /* check that it's not too long */
- c = s[50]; /* save char at this position */
- s[50] = '\0'; /* and end the string */
- l = 50;
- }
- move(18,(50 - l)/2 + 10);
- standout();
- addstr(s); /* print the message */
- standend();
- if (c != '\0') /* if we shortened it, restore it */
- s[50] = c;
- }
- refresh();
- }
-
-
- /*----------------------------------------------------------------------
- * FeDrawCube -- draw doubling cube
- *
- * This function draws the doubling cube. The cube is displayed beside
- * the inner table of the player who owns in (i.e. the one who didn't
- * double last). If neither player has doubled, the cube is drawn
- * in the middle of the board.
- *----------------------------------------------------------------------
- */
-
- FeDrawCube(g)
- struct game *g;
- {
- int r, c;
- char buf[8];
-
- mvaddstr(3,0," "); /* clear all cube locations */
- mvaddstr(10,0," ");
- mvaddstr(17,0," ");
- if (g->gameval == (1 << g->adcnt)) /* nobody has doubled */
- r = 10; /* cube is in the middle of the board */
- else { /* assume I didn't double last, mydir is up, */
- r = 0; /* and board is not inverted */
- if (g->flags & F_IDOUBLED) /* if I did double last */
- r = 1 - r; /* switch rows */
- if (g->mydir < 0) /* if my direction is down */
- r = 1 - r; /* switch rows */
- if (g->flags & F_INVERT) /* if board is inverted */
- r = 1 - r; /* switch rows */
- r = r ? 17 : 3; /* which row am I left with? */
- }
-
- sprintf(buf,"%d",g->gameval); /* generate the game value */
- if ( (c = 4 - strlen(buf)) < 0) { /* doubled past 4 digits? */
- strcpy(buf,"****"); /* we are out of columns */
- c = 0;
- }
- move(r,c);
- standout();
- mvaddstr(r,c,buf); /* go there and draw game value */
- standend();
- }
-
-
- /*----------------------------------------------------------------------
- * PDrawComment -- print a comment stored in a game
- *
- * This function takes a pointer to a game and draws the comment
- * strings to the screen. If "who" is 0, the "mycmt" strings
- * are drawn on lines 19 and 20. Otherwise, the "opcmt" strings
- * are drawn on lines 21 and 22. Any unused space on these lines
- * is cleared.
- *----------------------------------------------------------------------
- */
-
- PRIVATE PDrawComment(who,g)
- int who;
- struct game *g;
- {
- int line;
- char *s1, *s2;
-
- line = who ? 21 : 19;
- s1 = who ? g->opcmt : g->mycmt;
- s2 = who ? g->opcmt2 : g->mycmt2;
- mvaddstr(line,6,BLANKS(56));
- mvaddstr(line+1,6,BLANKS(56));
- if (s1 != NULL) {
- if (strlen(s1) > 56)
- s1[56] = '\0';
- mvaddstr(line,6,s1);
- }
- if (s2 != NULL) {
- if (strlen(s2) > 56)
- s2[56] = '\0';
- mvaddstr(line+1,6,s2);
- }
- }
-