|
|
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);
}