|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 17208 (0x4338) Types: TextFile Names: »scrn.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Cchess/scrn.c«
/* CCHESS SCREEN ROUTINES. Version 1.00 * * This is mostly low level i/o stuff to interface to the TERMCAP library. * In case you are planning to steal this code for another application, be * careful of wputchar(). It has a lot of scrolling features custom made * for cchess's screen handling style (i.e. the top of the screen held * fixed, and the bottom half scrolled. * * (C) Copyright Jan Wolter - Apr 1986. */ #include "cchess.h" /* Stuff for the termcap library */ #ifndef NOTERMCAP extern short ospeed; /* The output baud rate */ static char PC= '\000'; /* The pad characters (default ^@) */ static boolean AM; /* Does terminal have automatic margins? */ static boolean BW; /* Does terminal wrap on backspaces? */ static char area[1024]; /* Memory to store the following: */ static char *BC; /* The backspace string (default `\b`) */ char *BL; /* The bell character */ static char *CD; /* The clear to bottom of screen string */ char *CE; /* The clear to end of line string */ static char *CL; /* The clear screen string */ static char *CM; /* The cursor move string */ static char *CR; /* The carriage return string */ char *DL; /* The delete line string */ static char *DO; /* The cursor down string */ static char *HO; /* The home cursor string */ static char *ND; /* The cursor right string */ static char *SE; /* The end stand out mode string */ static char *SO; /* The start stand out mode string */ static char *TE; /* Terminal uninitialization string */ static char *TI; /* Terminal initialization string */ static char *UP; /* The cursor up string */ extern char *tgetstr(), *tgoto(), *getenv(); extern char *malloc(); #endif NOTERMCAP /* READTERM() * * Read in terminal capabilities description (termcap). Terminal type is * obtained from $TERM environment variable. All sorts of funny stuff is * set up here. */ readterm() { #ifndef NOTERMCAP register char *bp, *pad; char *tp; #endif NOTERMCAP /* Innocent until proven guilty */ issmart = FALSE; erases = TRUE; cleardown = FALSE; #ifndef NOTERMCAP /* Get the termcap entry */ bp = (char *) malloc(1024); if (tgetent(bp,getenv("TERM")) != 1) { /* No termcap: */ /* assume you can backspace with ^H, return with ^M */ free(bp); area[0] = '\b'; area[1] = '\0'; area[2] = '\r'; area[3] = '\0'; area[4] = '\007'; area[5] = '\0'; BC = area; /* Backspace with ^H */ CL = area+1; /* Clear is null string */ CR = area+2; /* Carraige return with ^M */ BL = area+4; /* Ring with ^G */ BW = TRUE; /* You wrap on left side */ AM = TRUE; /* You wrap on right side */ return; } /* Load the pad character for tputs. Default to null. */ tp = area; if(pad = tgetstr("pc",&tp)) PC = *pad; /* Get the clear screen string. Null string if undefined. */ if (!(CL = tgetstr("cl",&tp))) { CL = tp; *tp++ = '\000'; } /* Don't absolutely need these, but they may prove useful */ SO = tgetstr("so",&tp); /* Start standout mode */ SE = tgetstr("se",&tp); /* End standout mode */ AM = tgetflag("am"); /* Automatic margins? */ BW = tgetflag("bw"); /* Backspace wraps? */ if(!SO || !SE) SO = SE = 0; /* Get number of lines and columns. */ #ifdef U43BSD getwindow(); if (COLS == 0) { #endif U43BSD LINES = tgetnum("li"); if (LINES < 20) LINES = 0; /* Treat short screens like teletypes */ COLS = tgetnum("co"); if (COLS < 0) COLS = 40; /* Default to 40 if not defined. */ #ifdef U43BSD } #endif U43BSD if (COLS < 22) { printf("Cchess requires at least a 22 column terminal.\n"); exit(1); } /* Believe it or not, there are 22 column terminals in the world. */ /* Do some special stuff to make them work. 24 and above is fine */ commodore = (COLS < 24); /* Get the backspace string */ if (tgetflag("bs")) { BC = tp; *tp++ = '\b'; *tp++ = '\000'; } else if (!(BC = tgetstr("bc",&tp))) BC = tgetstr("le",&tp); /* Get the bell string, prefering visual bell if it is there */ if (!(BL = tgetstr("vb",&tp)) && !(BL = tgetstr("bl",&tp))) { BL = tp; *tp++ = '\007'; *tp++ = '\000'; } /* Get the carriage return string */ if (tgetflag("nc")) CR = (char *) 0; else if (!(CR = tgetstr("cr",&tp))) { CR = tp; *tp++ = '\r'; *tp++ = '\000'; } /* Does this critter backspace in a useful way? */ erases = BC && !tgetflag("os") && !tgetflag("hc"); /* If it's not obviously brain-damaged, see if it is smart */ if (LINES != 0 && erases && *CL != 0) { /* Get the strings we need to be smart */ CM = tgetstr("cm",&tp); /* Cursor Addressing */ HO = tgetstr("ho",&tp); /* Home Cursor */ DO = tgetstr("do",&tp); /* Cursor Down */ ND = tgetstr("nd",&tp); /* Cursor Right */ UP = tgetstr("up",&tp); /* Move cursor up */ /* Are we smart? */ if (issmart = (CM || ((HO || UP) && DO && ND))) { /* Get strings we can use if we are smart */ CD = tgetstr("cd",&tp); /* Clear rest of screen */ CE = tgetstr("ce",&tp); /* Clear rest of line */ cleardown = CD || CE; DL = tgetstr("dl",&tp); /* Delete line */ TI = tgetstr("ti",&tp); /* Initialize */ TE = tgetstr("te",&tp); /* Uninitialize */ } } /* Check if termcap buffer overflowed. Free memory */ if (tp-area > sizeof(area)) { printf("Panic: Termcap too big. %d bytes.\n",tp-area); exit(1); } free(bp); term_init(); #endif NOTERMCAP } /* TERM_INIT(), TERM_EXIT() * * Put the terminal in to or out of visual mode. */ term_init() { #ifndef NOTERMCAP if (TI) xputs(TI); #endif NOTERMCAP } term_exit() { #ifndef NOTERMCAP if (TE) xputs(TE); #endif NOTERMCAP } #ifndef NOTERMCAP /* XPUTC() * * putchar() is a macro. We need a real subroutine to use with tputs. * This is it. */ int xputc(ch) char ch; { putchar(ch); } /* XPUTS() * * send out a control sequence. */ xputs(s) char *s; { (void) tputs(s, 1, xputc); } /* NXPUTS() * * Put out the same control sequence <s> n times. */ nxputs(s,n) char *s; REGISTER int n; { for ( ; n > 0; n--) (void) tputs(s, 1, xputc); } /* CMMOVE() * * Use the "cm" string to do direct cursor motion. Don't call this. Use * calls to the higher level cursor() routine instead. */ cmmove(x,y) REGISTER int x,y; { xputs(tgoto(CM,x-1,y-1)); cx = x; cy = y; } /* CURSOR() * Move the cursor to line <y> and column <x>. If the current positions are * negative, the cursor position is unknown, and an absolute move is made to * locate them. Otherwise, whichever of the availble capabilities gives the * fastest move are used (well, almost). If the move cannot be made, this * routine returns FALSE. If you have CM or (HO and DO and ND) all moves * should be possible. If the current position is always known, UP will * substitute for HO. A boolean is returned to indicate the success or * failure of the move. */ cursor(x,y) REGISTER int x,y; { /* If cursor position is unknown, put it in a known position */ if (cy < 0) { if (x < 3 && y < 3 && HO && DO && ND) xputs(HO); else if (CM) { cmmove(x,y); return (TRUE); } else if (HO && DO && ND) xputs(HO); else return(FALSE); } else if (cx < 0) { if (x < 3 && CR && ND) xputs(CR); else if (CM) { cmmove(x,y); return (TRUE); } else if (CR && ND) xputs(CR); else return (FALSE); cx = 1; } /* Do the vertical movement */ if (vcursor(x,y)) { /* Do the horizontal movement */ /* If we are already where we want, do nothing */ if (cx == x) return(TRUE); /* Move cursor left */ if (cx > x) { if ((!CM || cx - x < 3) && BC) { nxputs(BC,cx - x); cx = x; return(TRUE); } if ((x < 3 || !CM) && ND && CR) { xputs(CR); cx = 1; } else if (CM) { cmmove(x,y); return(TRUE); } else return(FALSE); } /* Move cursor right */ if (cx < x) { if (ND && (x - cx < 3 || !CM)) { nxputs(ND,x - cx); cx = x; return(TRUE); } if (CM) { cmmove(x,y); return(TRUE); } return(FALSE); } return(TRUE); } else return(FALSE); } /* VCURSOR() * * Move the cursor into line <y>. This routine trys it's best to make the * best use of capabilities available, and to come up with reasonably * efficient moves. It ain't perfect. Beware of convoluted logic. */ vcursor(x,y) REGISTER int x,y; { /* If already in the goal line, return */ if (cy == y) return(TRUE); /* Move upwards */ if (y < cy) { if (UP && cy - y < 3 && abs(cx - x) < 3) { nxputs(UP,cy - y); cy = y; return(TRUE); } if (HO && ((x<3 && y<3) || (!CM && !UP))) xputs(HO); else if (CM) { cmmove(x,y); return (TRUE); } else if (UP) { nxputs(UP,cy - y); cy = y; return(TRUE); } else return (FALSE); } /* Move downwards */ if (y > cy) { if (DO && ((y - cy < 3 && abs(x-cx) < 3) || !CM)) { nxputs(DO,y - cy); cy = y; return(TRUE); } if (CM) { cmmove(x,y); return(TRUE); } return (FALSE); } return(TRUE); } /* CLINE() * * Clear to the end of the line. Leave the cursor where it was. Should * only be called if the CE string is defined, or the terminal erases. */ cline() { short ox; if (CE) xputs(CE); /* Use the clear to end of line cmd */ else if (erases) { ox = cx; /* Use overprinted spaces */ for ( ; cx < COLS; cx++) putchar(' '); cursor(ox,cy); } } /* CLBOT() * * Clear to the bottom of the display. Leave the cursor where it was. Should * only be called if the CD string is defined, or the terminal is smart and * erases. If the CE and CD strings are both missing, this can be very slow, * since it will then do the whole thing with spaces. */ clbot() { register short ox,oy; if (CD) { if (cx == 1) xputs(CD); else { /* CD can only be used from the beginning of a line */ cline(); if (cy < LINES) { ox = cx; oy = cy; wputchar('\n'); xputs(CD); cursor(ox,oy); } } } else if (issmart) { ox = cx; oy = cy; cline(); while (cy < LINES) { wputchar('\n'); cline(); } cursor(ox,oy); } } /* DELINE() * * This deletes the line <n> from the display. It should be called only if * the DL string is defined, and the terminal is SMART (ie, can move it's * cursors wherever necessary). The cursor is returned to the place it came * from afterwards. */ deline(n) short n; { short ox=cx,oy=cy; /* Save the old position */ vcursor(1,n); /* Get the cursor into line n */ xputs(DL); /* Delete the line */ if (oy == n) ox = 0; /* If deleted current line */ else if (oy > n) oy--; /* Old position may scroll up */ cursor(ox,oy); /* Put the cursor back */ } #endif NOTERMCAP /* BEG_SO(), END_SO() * * Enter and exit standout mode. */ beg_so() { #ifndef NOTERMCAP if(SO) xputs(SO); #endif NOTERMCAP } end_so() { #ifndef NOTERMCAP if(SE) xputs(SE); #endif NOTERMCAP } /* INDENT() * * This routine indents <n> spaces on the current line. It works for any * terminal. It may use spaces. <n> is assumed to be less than COLS and * BLANK_LEN. If cx > 1 before entry, the indent will be on the next line, * not this line. */ indent(n) int n; { #ifndef NOTERMCAP if (n < 3 || !issmart) { #endif if (cx > 1) wputchar('\n'); printf(blanks(n)); cx = n+1; #ifndef NOTERMCAP } else cursor(n+1, cy + (cx > 1)); #endif NOTERMCAP } /* BACKSPACE() * * This sends a backspace, and updates the current cursor position. * It fixes terminals that don't wrap on backspace to do so, if possible. */ backspace() { #ifndef NOTERMCAP if (!BC) { wputchar('<'); /* Can't backspace. Make a mark instead. */ return; } if (cx-- == 1) { if (cy > 1) { /* Wrap back to previous line */ if (BW) { xputs(BC); /* use backspace */ cx = COLS; --cy; } else if (issmart) cursor(COLS,cy-1); /* use cursor motion */ else cx++; /* Can't backspace--do nothing */ } else cx = 1; /* At home position. Do nothing */ } else xputs(BC); /* Ordinary backspacing */ #else putchar('\b'); #endif NOTERMCAP } bell() { #ifndef NOTERMCAP xputs(BL); #else putchar('\007'); #endif NOTERMCAP } /* CLR() * * Clear the screen. If we have the termcap library, use tputs. * Otherwise, just make a note to redraw next time we need to. */ extern short movelen; clr() { #ifndef NOTERMCAP tputs(CL,LINES,xputc); #endif NOTERMCAP cx = cy = 1; scrolled = FALSE; movelen = 0; } /* WPUTCHAR() * * This routine is like putchar(), but it updates the current cursor position. * It doesn't know about tabs or returns, and should not be sent any. It * Does handle backspaces and newlines. The latter are output as carriage- * return-newline combinations. If the terminal doesn't have automatic * margins, it fakes them. */ wputchar(c) char c; { #ifdef NOTERMCAP if (c == '\n') putchar('\r'); putchar(c); #else /* If a backspace, do it */ if (c == '\b') backspace(); /* If newline or auto-newline due to typing past last column */ else if (c == '\n' || ++cx > COLS) { if (cy == LINES) /* this should NOT be ">=" */ { if (msgarea == 0) { scrolled = TRUE; cy--; } else if (DL) deline(msgarea); else if (cleardown) { cursor(1,msgarea); clbot(); return; } else { scrolled = TRUE; cy--; } } /* If autowrapping, send character first */ if (c != '\n') putchar(c); /* Send the newline, unless automatic margins do it for us */ if (c == '\n' || !AM) { if (CR) xputs(CR); else cursor(1,cy); putchar('\n'); /* Don't want to have DO here */ } cx = 1; cy++; } else putchar(c); #endif NOTERMCAP } /* WPRINTF() * * This routine is like printf(), but it updates the cursor position, and can * take at most five arguments. The wprint() routine is the same, but takes * no arguments. */ /*VARARGS1*/ wprintf(str,arg1,arg2,arg3,arg4,arg5) char *str,*arg1,*arg2,*arg3,*arg4,*arg5; { char buf[MB_LEN]; sprintf(buf,str,arg1,arg2,arg3,arg4,arg5); wprint(buf); } wprint(str) char *str; { for ( ; *str != 0; str++) wputchar(*str); } /* MPRINTF() * * This is like wprintf(), except it plays games kind of like more in * the message area. To use it, first call initmore() to set up the * message area for moring. Space, Newline and 'Q' are accepted at the * more prompt and do the usual things. It erases the more prompts if * the terminal has any brains at all. */ static int morespace,morecount; static boolean domore; initmore() { #ifndef NOTERMCAP /* Try to make room in the message area */ if (!DL && cleardown) { cursor(1,msgarea); clbot(); } if (LINES) morecount = morespace = LINES - msgarea; else morecount = -2; /* Don't "more" on teletypes */ domore = FALSE; #endif NOTERMCAP } /*VARARGS1*/ mprintf(str,arg1,arg2,arg3,arg4) char *str,*arg1,*arg2,*arg3,*arg4; { char buf[MB_LEN]; sprintf(buf,str,arg1,arg2,arg3,arg4); mprint(buf); } mprint(str) char *str; { #ifndef NOTERMCAP char c,p; if (morecount == -1) return; while (*str != '\000') { if (cx == COLS) c = '\n'; else c = *str++; if (domore) { beg_so(); wprint("--More--"); end_so(); p = xenter(" Q\n"); /* Erase More Prompt */ if (DL && (CE || erases)) { cursor(1,cy); cline(); } else wputchar('\n'); domore = FALSE; if (p == 'Q') { /* Ignore mprint calls until next initmore */ morecount = -1; return; } else if (DL && p == '\n') morecount = 1; else morecount = morespace; } wputchar(c); if (c == '\n' && --morecount == 0) domore = TRUE; } #else while (*str != '\000') wputchar(*str++); #endif NOTERMCAP } /* CPRINTF() * * Print string <st> centered on the screen. Up to five arguments can be used * as in printf. No newline is added at the end. If the line is longer than * the screen, is it is broken up as well as possible, and each chunk is * centered. This routine is weird. Cprint is simlar, but takes no * arguments. Oh yes, if the first character is a *, the message is printed * in standout mode. */ /*VARARGS1*/ cprintf(str,arg1,arg2,arg3,arg4) char *str,*arg1,*arg2,*arg3,*arg4; { char buf[MB_LEN+10]; sprintf(buf,str,arg1,arg2,arg3,arg4); cprint(buf); } cprint(c) REGISTER char *c; { char pop = 0; boolean standout; register int n; if (standout = (*c == '*')) c++; for(;;) { if ((n = strlen(c)) > COLS) { n = findbreak(c,COLS-1); pop = c[n]; c[n] = '\000'; } indent((int)(COLS-n)/2); if (standout) beg_so(); wprint(c); if (standout) end_so(); if (pop == 0) break; else { c[n] = pop; c = c + n; while (*c != 0 && (*c == ' ' || *c == '\t')) c++; pop = 0; } } } /* FINDBREAK() * * Find a place for a word break just before column <n> of the string <s>. * If no break is found within 20 characters, column n is returned. */ int findbreak(s,n) char *s; REGISTER int n; { register int i; char c; for (i = 0; i < 20 && i < n ; i++) { if ((c = s[n-i]) == ' ' || c == '\t') return(n-i); } return(n); }