home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
hamradio
/
s920603.zip
/
DISPLAY.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-28
|
14KB
|
592 lines
/* ANSI display emulation
*
* This file emulates the IBM ANSI terminal display. It maintains a
* display buffer and descriptor for each virtual display, of which there
* can be many. All writes occur first into this display buffer, and then
* any particular display buffer can be copied onto the real screen.
* This allows many background tasks to run without blocking even though
* only one task's display is actually being shown.
*
* This display driver is substantially faster than even the NANSI.SYS
* loadable screen driver, particularly when large blocks are written.
*
* Extensions to handle displaying multiple small virtual windows should
* be pretty straightforward.
*
* Copyright 1992 Phil Karn, KA9Q
*
*/
#include <conio.h>
#include <alloc.h>
#include <string.h>
#include "global.h"
#include "display.h"
#include "proc.h"
#define ESC 0x1b /* ASCII ESCAPE */
#define FF 0x0c /* ASCII ^L (form feed) */
#define BEL 0x7 /* ASCII ^G (bell) */
int fgattr[] = { 0, 4, 2, 14, 1, 5, 3, 7 }; /* Foreground attribs */
int bgattr[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* Background attribs */
static void dclrline __ARGS((struct display *dp));
static void dclrscr __ARGS((struct display *dp));
static void desc __ARGS((struct display *dp,char c));
static void darg __ARGS((struct display *dp,char c));
static void dchar __ARGS((struct display *dp,char c));
static void dclreol __ARGS((struct display *dp));
static void dattrib __ARGS((struct display *dp,int val));
static char *bufloc __ARGS((struct display *dp));
static void dinsline __ARGS((struct display *dp));
static void ddelline __ARGS((struct display *dp));
static void ddelchar __ARGS((struct display *dp));
static void dinsert __ARGS((struct display *dp));
static void dclreod __ARGS((struct display *dp));
/* Create a new virtual display.
* The "noscrol" flag, if set, causes lines to "wrap around" from the bottom
* to the top of the screen instead of scrolling the entire screen upwards
* with each new line. This can be handy for packet trace screens.
*/
struct display *
newdisplay(rows,cols,noscrol)
int rows,cols; /* Size of new screen */
int noscrol; /* 1: old IBM-style wrapping instead of scrolling */
{
struct display *dp;
if(rows < 1 || cols < 1)
return NULLDISP; /* Bogus args */
dp = (struct display *)calloc(1,sizeof(struct display) + 2*rows*cols);
dp->cookie = D_COOKIE;
dp->buf = (char *)(dp + 1);
dp->rows = rows;
dp->cols = cols;
dp->attrib = 0x7; /* White on black, no blink or intensity */
dclrscr(dp); /* Start with a clean slate */
dp->flags |= DIRTY_SCREEN | DIRTY_CURSOR;
if(noscrol)
dp->flags |= NOSCROL;
return dp;
}
/* Close a display - simply get rid of the memory */
void
closedisplay(dp)
struct display *dp;
{
if(dp != NULLDISP && dp->cookie == D_COOKIE)
free(dp);
}
/* Write data to the virtual display. Does NOT affect the real screen -
* dupdate(dp) must be called to copy the virtual screen to the real
* screen.
*/
void
displaywrite(dp,buf,cnt)
struct display *dp; /* Virtual screen pointer */
char *buf; /* Data to be written */
int cnt; /* Count */
{
char c;
if(dp == NULLDISP || dp->cookie != D_COOKIE)
return;
while(cnt-- != 0){
c = *buf++;
switch(dp->state){
case ESCAPE:
desc(dp,c);
break;
case ARG:
darg(dp,c);
break;
case NORMAL:
dchar(dp,c);
break;
}
}
psignal(dp,1);
}
/* Make the real screen look like the virtual one. It attempts to do as
* little work as possible unless the "force" flag is set -- then
* the entire screen is updated. (This is useful when switching between
* virtual display screens.)
*
* Note the different row and column numbering conventions -- I start
* at zero, the puttext() and gotoxy() library functions start at 1.
*
* The "dirty row" stuff is intended to allow updating of only a single
* modified row instead of rewriting the entire screen -- it's not fully
* implemented yet. I may replace it with a per-row flag.
*/
void
dupdate(dp,force)
struct display *dp; /* Virtual screen pointer */
int force; /* Force complete update regardless of dirty bits */
{
if(dp == NULLDISP || dp->cookie != D_COOKIE)
return;
if(force || (dp->flags & (DIRTY_SCREEN | DIRTY_ROW))){
/* Write it all to the screen */
if(dp->flags & NOSCROL){
puttext(1,1,dp->cols,dp->rows,dp->buf);
} else {
/* Scroll-mode update */
puttext(1,1,dp->cols,dp->rows - dp->firstrow,
dp->buf + 2*dp->firstrow*dp->cols);
if(dp->firstrow != 0)
puttext(1,dp->rows-dp->firstrow+1,dp->cols,dp->rows,dp->buf);
}
}
if(force || (dp->flags & DIRTY_CURSOR)){
/* Update cursor */
if(dp->flags & NOSCROL)
gotoxy(dp->col+1,((dp->row + dp->firstrow) % dp->rows) +1);
else
gotoxy(dp->col+1,dp->row+1);
}
dp->flags &= ~(DIRTY_SCREEN|DIRTY_ROW|DIRTY_CURSOR);
}
/* Process incoming character while in ESCAPE state */
static void
desc(dp,c)
struct display *dp;
char c;
{
switch(c){
case '[': /* Always second char of ANSI escape sequence */
/* Get ready for argument list */
dp->state = ARG;
dp->argi = 0;
dp->arg[0] = 0;
break;
case '7': /* Save cursor location (VT-100) */
dp->savcol = dp->col;
dp->savrow = dp->row;
dp->state = NORMAL;
break;
case '8': /* Restore cursor location (VT-100) */
dp->col = dp->savcol;
dp->row = dp->savrow;
dp->flags |= DIRTY_CURSOR;
dp->state = NORMAL;
break;
case ESC:
break; /* Remain in ESCAPE state */
default:
dp->state = NORMAL;
dchar(dp,c);
}
}
/* Process characters after a ESC[ sequence */
static void
darg(dp,c)
struct display *dp;
char c;
{
int i;
switch(c){
case ESC:
dp->state = ESCAPE;
return;
case '?': /* Ignored */
case '=':
return;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/* Collect decimal number */
dp->arg[dp->argi] = 10*dp->arg[dp->argi] + (c - '0');
return;
case ';': /* Next argument is beginning */
if(dp->argi <= MAXARGS - 1)
dp->argi++;
dp->arg[dp->argi] = 0;
return;
case '@': /* Open up space for character */
dinsert(dp);
break;
case 'A': /* Cursor up */
if(dp->arg[0] == 0)
dp->arg[0] = 1; /* Default is one line */
if(dp->arg[0] < dp->row)
dp->row -= dp->arg[0];
else
dp->row = 0;
dp->flags |= DIRTY_CURSOR;
if(dp->flags & DIRTY_ROW)
dp->flags |= DIRTY_SCREEN;
break;
case 'B': /* Cursor down */
if(dp->arg[0] == 0)
dp->arg[0] = 1; /* Default is one line */
if(dp->arg[0] + dp->row >= dp->rows)
dp->row = dp->rows - 1;
else
dp->row += dp->arg[0];
dp->flags |= DIRTY_CURSOR;
if(dp->flags & DIRTY_ROW)
dp->flags |= DIRTY_SCREEN;
break;
case 'C': /* Cursor right */
if(dp->arg[0] == 0)
dp->arg[0] = 1; /* Default is one column */
if(dp->arg[0] + dp->col >= dp->cols)
dp->col = dp->cols - 1;
else
dp->col += dp->arg[0];
dp->flags |= DIRTY_CURSOR;
break;
case 'D': /* Cursor left */
if(dp->arg[0] == 0)
dp->arg[0] = 1; /* Default is one column */
if(dp->arg[0] < dp->col)
dp->col -= dp->arg[0];
else
dp->col = 0;
dp->flags |= DIRTY_CURSOR;
break;
case 'f':
case 'H': /* Cursor motion */
dp->row = (dp->arg[0] == 0) ? 0 : dp->arg[0] - 1;
dp->col = (dp->arg[1] == 0) ? 0 : dp->arg[1] - 1;
dp->state = NORMAL;
dp->flags |= DIRTY_CURSOR;
break;
case 'h': /* Set mode */
switch(dp->arg[0]){
case 7: /* Turn on wrap mode */
dp->flags &= ~NOWRAP;
break;
}
break;
case 'J': /* Clear screen */
switch(dp->arg[0]){
case 2:
dclrscr(dp); /* Clear entire screen, home cursor */
break;
case 0:
dclreod(dp); /* Clear to end of screen (VT-100) */
break;
}
break;
case 'K': /* Erase to end of current line */
dclreol(dp);
break;
case 'L': /* Add blank line */
dinsline(dp);
break;
case 'l': /* Clear mode */
switch(dp->arg[0]){
case 7: /* Turn off wrap mode */
dp->flags |= NOWRAP;
break;
}
break;
case 'M': /* Delete line */
ddelline(dp);
break;
case 'm': /* Set screen attributes */
for(i=0;i<=dp->argi;i++){
dattrib(dp,dp->arg[i]);
}
break;
case 'P': /* Delete character */
ddelchar(dp);
break;
case 's': /* Save cursor position */
dp->savcol = dp->col;
dp->savrow = dp->row;
break;
case 'u': /* Restore cursor position */
dp->col = dp->savcol;
dp->row = dp->savrow;
dp->flags |= DIRTY_CURSOR;
break;
}
dp->state = NORMAL;
}
/* Clear from cursor to end of screen, leaving cursor as is */
static void
dclreod(dp)
struct display *dp;
{
char *cp;
int i;
cp = bufloc(dp);
i = (dp->rows - dp->row - 1) * dp->cols + (dp->cols - dp->col - 1);
while(i-- != 0){
*cp++ = ' ';
*cp++ = dp->attrib;
}
dp->flags |= DIRTY_SCREEN;
}
static void
dinsert(dp)
struct display *dp;
{
int i;
char *cp;
cp = bufloc(dp);
i = 2*(dp->cols - dp->col - 1);
if(i != 0)
memmove(cp+2,cp,i); /* handles overlapping blocks */
*cp++ = ' ';
*cp = dp->attrib;
dp->flags |= DIRTY_ROW;
}
static void
ddelchar(dp)
struct display *dp;
{
char *cp;
int i;
cp = bufloc(dp);
i = 2*(dp->cols-dp->col-1);
/* Copy characters to right one space left */
if(i != 0)
memmove(cp,cp+2,i); /* memmove handles overlapping blocks */
/* Clear right most character on line */
cp[i] = ' ';
cp[i+1] = dp->attrib;
dp->flags |= DIRTY_ROW;
}
static void
ddelline(dp)
struct display *dp;
{
char *cp;
int i;
int colsave;
int rowsave;
colsave = dp->col;
rowsave = dp->row;
cp = bufloc(dp);
/* Copy up lines below this one */
i = 2*dp->cols*(dp->rows-dp->row-1);
if(i != 0)
memmove(cp,cp+2*dp->cols,i);
/* Clear bottom line */
dp->row = dp->rows - 1;
dclrline(dp);
dp->col = colsave;
dp->row = rowsave;
dp->flags |= DIRTY_SCREEN;
}
/* Insert blank line where cursor is. Push existing lines down one */
static void
dinsline(dp)
struct display *dp;
{
char *cp;
int colsave;
int i;
colsave = dp->col; /* Supposed to be issued only at start of line */
dp->col = 0;
cp = bufloc(dp);
i = 2*dp->cols*(dp->rows - dp->row - 1);
/* Copy everything starting with current line down one line */
if(i != 0)
memmove(cp+2*dp->cols,cp,i); /* does copy correctly */
dclrline(dp); /* Clear current line */
dp->col = colsave;
dp->flags |= DIRTY_SCREEN;
}
/* Process an argument to an attribute set command */
static void
dattrib(dp,val)
struct display *dp;
int val;
{
switch(val){
case 0: /* Normal white on black */
dp->attrib = 0x7;
break;
case 1: /* High intensity */
dp->attrib |= 0x8;
break;
case 5: /* Blink on */
dp->attrib |= 0x80;
break;
case 7: /* Reverse video (black on white) */
dp->attrib = 0x70;
break;
default:
if(val >= 30 && val < 38){
/* Set foreground color */
dp->attrib = (dp->attrib & ~0x7) | fgattr[val - 30];
} else if(val >= 40 && val < 48){
/* Set background color */
dp->attrib = (dp->attrib & ~0x70) | ((bgattr[val - 40]) << 4);
}
break;
}
}
/* Display character */
static void
dchar(dp,c)
struct display *dp;
char c;
{
char *cp;
switch(c){
case ESC:
dp->state = ESCAPE;
return;
case '\0': /* Ignore nulls and bells */
case BEL:
break;
case '\b': /* Backspace */
if(dp->col > 0){
dp->col--;
dp->flags |= DIRTY_CURSOR;
}
break;
case FF: /* Page feed */
dclrscr(dp);
break;
case '\t': /* Tab */
if(dp->col < dp->cols - 8){
dp->col = (dp->col + 8) & ~7;
dp->flags |= DIRTY_CURSOR;
}
break;
case '\n': /* Move cursor down one row */
dp->row++;
dp->flags |= DIRTY_CURSOR;
if(dp->flags & DIRTY_ROW)
dp->flags |= DIRTY_SCREEN;
break;
case '\r': /* Move cursor to beginning of current row */
dp->col = 0;
dp->flags |= DIRTY_CURSOR;
break;
default: /* Display character on screen */
/* Compute location in screen buffer memory */
cp = bufloc(dp);
if(c == '_' && *cp != ' '){
/* We'd like to underline the existing char,
* but we can't except on a monochrome display.
* So highlight it instead. (char-backspace-underscore)
*/
*++cp = dp->attrib | 0x8;
} else if(c != ' ' && *cp == '_'){
/* underscore-backspace-char sequence;
* also intensify the char
*/
*cp++ = c;
*cp = dp->attrib | 0x8;
} else {
/* Normal display */
*cp++ = c;
*cp = dp->attrib;
}
dp->flags |= DIRTY_CURSOR | DIRTY_ROW;
/* Update cursor position, wrapping if necessary */
if(++dp->col == dp->cols){
if(dp->flags & NOWRAP){
dp->col--;
} else {
dp->col = 0;
dp->row++;
if(dp->flags & DIRTY_ROW)
dp->flags |= DIRTY_SCREEN;
}
}
}
/* Scroll screen if necessary */
if(dp->row == dp->rows){
dp->row--;
/* Scroll screen up */
dp->firstrow = (dp->firstrow + 1) % dp->rows;
if(!(dp->flags & NOSCROL))
dp->flags |= DIRTY_SCREEN;
dclrline(dp);
}
}
/* Clear entire line containing cursor, leaving cursor alone */
static void
dclrline(dp)
struct display *dp;
{
char *cp;
int i;
int colsave;
colsave = dp->col;
dp->col = 0;
cp = bufloc(dp);
for(i=dp->cols;i!=0;i--){
*cp++ = ' ';
*cp++ = dp->attrib;
}
dp->col = colsave;
dp->flags |= DIRTY_ROW;
}
/* Clear from cursor to end of line. Cursor is not moved */
static void
dclreol(dp)
struct display *dp;
{
char *cp;
int i;
cp = bufloc(dp);
for(i=dp->cols - dp->col;i!=0;i--){
*cp++ = ' ';
*cp++ = dp->attrib;
}
dp->flags |= DIRTY_ROW;
}
/* Move cursor to top left corner, clear screen */
static void
dclrscr(dp)
struct display *dp;
{
char *cp;
int i;
dp->row = dp->col = 0;
dp->firstrow = 0;
cp = bufloc(dp);
for(i=dp->rows*dp->cols;i!=0;i--){
*cp++ = ' ';
*cp++ = dp->attrib;
}
dp->flags |= (DIRTY_CURSOR|DIRTY_SCREEN);
}
/* Return pointer into screen buffer for current cursor location */
static char *
bufloc(dp)
struct display *dp;
{
int offset;
offset = dp->col + dp->cols*((dp->row + dp->firstrow) % dp->rows);
return dp->buf + 2*offset;
}