home *** CD-ROM | disk | FTP | other *** search
- /* 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;
- }
-