home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / s920603.zip / DISPLAY.C < prev    next >
C/C++ Source or Header  |  1992-05-28  |  14KB  |  592 lines

  1. /* ANSI display emulation
  2.  *
  3.  * This file emulates the IBM ANSI terminal display. It maintains a
  4.  * display buffer and descriptor for each virtual display, of which there
  5.  * can be many. All writes occur first into this display buffer, and then
  6.  * any particular display buffer can be copied onto the real screen.
  7.  * This allows many background tasks to run without blocking even though
  8.  * only one task's display is actually being shown.
  9.  *
  10.  * This display driver is substantially faster than even the NANSI.SYS
  11.  * loadable screen driver, particularly when large blocks are written.
  12.  *
  13.  * Extensions to handle displaying multiple small virtual windows should
  14.  * be pretty straightforward.
  15.  *
  16.  * Copyright 1992 Phil Karn, KA9Q
  17.  * 
  18.  */
  19. #include <conio.h>
  20. #include <alloc.h>
  21. #include <string.h>
  22. #include "global.h"
  23. #include "display.h"
  24. #include "proc.h"
  25.  
  26. #define    ESC    0x1b        /* ASCII ESCAPE */
  27. #define    FF    0x0c        /* ASCII ^L (form feed) */
  28. #define    BEL    0x7        /* ASCII ^G (bell) */
  29.  
  30. int fgattr[] = { 0, 4, 2, 14, 1, 5, 3, 7 };    /* Foreground attribs */
  31. int bgattr[] = { 0, 4, 2, 6, 1, 5, 3, 7 };    /* Background attribs */
  32.  
  33. static void dclrline __ARGS((struct display *dp));
  34. static void dclrscr __ARGS((struct display *dp));
  35. static void desc __ARGS((struct display *dp,char c));
  36. static void darg __ARGS((struct display *dp,char c));
  37. static void dchar  __ARGS((struct display *dp,char c));
  38. static void dclreol __ARGS((struct display *dp));
  39. static void dattrib __ARGS((struct display *dp,int val));
  40. static char *bufloc __ARGS((struct display *dp));
  41. static void dinsline __ARGS((struct display *dp));
  42. static void ddelline __ARGS((struct display *dp));
  43. static void ddelchar __ARGS((struct display *dp));
  44. static void dinsert __ARGS((struct display *dp));
  45. static void dclreod __ARGS((struct display *dp));
  46.  
  47. /* Create a new virtual display.
  48.  * The "noscrol" flag, if set, causes lines to "wrap around" from the bottom
  49.  * to the top of the screen instead of scrolling the entire screen upwards
  50.  * with each new line. This can be handy for packet trace screens.
  51.  */
  52. struct display *
  53. newdisplay(rows,cols,noscrol)
  54. int rows,cols;    /* Size of new screen */
  55. int noscrol;    /* 1: old IBM-style wrapping instead of scrolling */
  56. {
  57.     struct display *dp;
  58.  
  59.     if(rows < 1 || cols < 1)
  60.         return NULLDISP;    /* Bogus args */
  61.     dp = (struct display *)calloc(1,sizeof(struct display) + 2*rows*cols);
  62.     dp->cookie = D_COOKIE;
  63.     dp->buf = (char *)(dp + 1);
  64.     dp->rows = rows;
  65.     dp->cols = cols;
  66.     dp->attrib = 0x7;    /* White on black, no blink or intensity */
  67.     dclrscr(dp);        /* Start with a clean slate */
  68.     dp->flags |= DIRTY_SCREEN | DIRTY_CURSOR;
  69.     if(noscrol)
  70.         dp->flags |= NOSCROL;
  71.     return dp;
  72. }
  73.  
  74. /* Close a display - simply get rid of the memory */
  75. void
  76. closedisplay(dp)
  77. struct display *dp;
  78. {
  79.     if(dp != NULLDISP && dp->cookie == D_COOKIE)
  80.         free(dp);
  81. }
  82.  
  83. /* Write data to the virtual display. Does NOT affect the real screen -
  84.  * dupdate(dp) must be called to copy the virtual screen to the real
  85.  * screen.
  86.  */
  87. void
  88. displaywrite(dp,buf,cnt)
  89. struct display *dp;    /* Virtual screen pointer */
  90. char *buf;        /* Data to be written */
  91. int cnt;        /* Count */
  92. {
  93.     char c;
  94.  
  95.     if(dp == NULLDISP || dp->cookie != D_COOKIE)
  96.         return;
  97.  
  98.     while(cnt-- != 0){
  99.         c = *buf++;
  100.         switch(dp->state){
  101.         case ESCAPE:
  102.             desc(dp,c);
  103.             break;
  104.         case ARG:
  105.             darg(dp,c);
  106.             break;
  107.         case NORMAL:
  108.             dchar(dp,c);
  109.             break;
  110.         }
  111.     }
  112.     psignal(dp,1);
  113. }
  114. /* Make the real screen look like the virtual one. It attempts to do as
  115.  * little work as possible unless the "force" flag is set -- then
  116.  * the entire screen is updated. (This is useful when switching between
  117.  * virtual display screens.)
  118.  *
  119.  * Note the different row and column numbering conventions -- I start
  120.  * at zero, the puttext() and gotoxy() library functions start at 1.
  121.  *
  122.  * The "dirty row" stuff is intended to allow updating of only a single
  123.  * modified row instead of rewriting the entire screen -- it's not fully
  124.  * implemented yet. I may replace it with a per-row flag.
  125.  */
  126. void
  127. dupdate(dp,force)
  128. struct display *dp;    /* Virtual screen pointer */
  129. int force;    /* Force complete update regardless of dirty bits */
  130. {
  131.     if(dp == NULLDISP || dp->cookie != D_COOKIE)
  132.         return;
  133.  
  134.     if(force || (dp->flags & (DIRTY_SCREEN | DIRTY_ROW))){
  135.         /* Write it all to the screen */
  136.         if(dp->flags & NOSCROL){
  137.             puttext(1,1,dp->cols,dp->rows,dp->buf);
  138.         } else {
  139.             /* Scroll-mode update */
  140.             puttext(1,1,dp->cols,dp->rows - dp->firstrow,
  141.              dp->buf + 2*dp->firstrow*dp->cols);
  142.             if(dp->firstrow != 0)
  143.                 puttext(1,dp->rows-dp->firstrow+1,dp->cols,dp->rows,dp->buf);
  144.         }
  145.     }
  146.     if(force || (dp->flags & DIRTY_CURSOR)){
  147.         /* Update cursor */
  148.         if(dp->flags & NOSCROL)
  149.             gotoxy(dp->col+1,((dp->row + dp->firstrow) % dp->rows) +1);
  150.         else
  151.             gotoxy(dp->col+1,dp->row+1);
  152.     }
  153.     dp->flags &= ~(DIRTY_SCREEN|DIRTY_ROW|DIRTY_CURSOR);
  154. }
  155. /* Process incoming character while in ESCAPE state */
  156. static void
  157. desc(dp,c)
  158. struct display *dp;
  159. char c;
  160. {
  161.     switch(c){
  162.     case '[':    /* Always second char of ANSI escape sequence */
  163.         /* Get ready for argument list */
  164.         dp->state = ARG;
  165.         dp->argi = 0;
  166.         dp->arg[0] = 0;
  167.         break;
  168.     case '7':    /* Save cursor location (VT-100) */
  169.         dp->savcol = dp->col;
  170.         dp->savrow = dp->row;
  171.         dp->state = NORMAL;
  172.         break;
  173.     case '8':    /* Restore cursor location (VT-100) */
  174.         dp->col = dp->savcol;
  175.         dp->row = dp->savrow;
  176.         dp->flags |= DIRTY_CURSOR;
  177.         dp->state = NORMAL;
  178.         break;
  179.     case ESC:
  180.         break;    /* Remain in ESCAPE state */
  181.     default:
  182.         dp->state = NORMAL;
  183.         dchar(dp,c);
  184.     }
  185. }
  186.  
  187. /* Process characters after a ESC[ sequence */
  188. static void
  189. darg(dp,c)
  190. struct display *dp;
  191. char c;
  192. {
  193.     int i;
  194.  
  195.     switch(c){
  196.     case ESC:
  197.         dp->state = ESCAPE;
  198.         return;
  199.     case '?':    /* Ignored */
  200.     case '=':
  201.         return;
  202.     case '0':
  203.     case '1':
  204.     case '2':
  205.     case '3':
  206.     case '4':
  207.     case '5':
  208.     case '6':
  209.     case '7':
  210.     case '8':
  211.     case '9':
  212.         /* Collect decimal number */
  213.         dp->arg[dp->argi] = 10*dp->arg[dp->argi] + (c - '0');
  214.         return;
  215.     case ';':    /* Next argument is beginning */
  216.         if(dp->argi <= MAXARGS - 1)
  217.             dp->argi++;
  218.         dp->arg[dp->argi] = 0;
  219.         return;
  220.     case '@':    /* Open up space for character */
  221.         dinsert(dp);
  222.         break;
  223.     case 'A':    /* Cursor up */
  224.         if(dp->arg[0] == 0)
  225.             dp->arg[0] = 1;    /* Default is one line */
  226.         if(dp->arg[0] < dp->row)
  227.             dp->row -= dp->arg[0];
  228.         else
  229.             dp->row = 0;
  230.         dp->flags |= DIRTY_CURSOR;
  231.         if(dp->flags & DIRTY_ROW)
  232.             dp->flags |= DIRTY_SCREEN;
  233.         break;
  234.     case 'B':    /* Cursor down */
  235.         if(dp->arg[0] == 0)
  236.             dp->arg[0] = 1;    /* Default is one line */
  237.         if(dp->arg[0] + dp->row >= dp->rows)
  238.             dp->row = dp->rows - 1;
  239.         else
  240.             dp->row += dp->arg[0];
  241.         dp->flags |= DIRTY_CURSOR;
  242.         if(dp->flags & DIRTY_ROW)
  243.             dp->flags |= DIRTY_SCREEN;
  244.         break;
  245.     case 'C':    /* Cursor right */
  246.         if(dp->arg[0] == 0)
  247.             dp->arg[0] = 1;    /* Default is one column */
  248.         if(dp->arg[0] + dp->col >= dp->cols)
  249.             dp->col = dp->cols - 1;
  250.         else
  251.             dp->col += dp->arg[0];
  252.         dp->flags |= DIRTY_CURSOR;
  253.         break;
  254.     case 'D':    /* Cursor left */
  255.         if(dp->arg[0] == 0)
  256.             dp->arg[0] = 1;    /* Default is one column */
  257.         if(dp->arg[0] < dp->col)
  258.             dp->col -= dp->arg[0];
  259.         else
  260.             dp->col = 0;
  261.         dp->flags |= DIRTY_CURSOR;
  262.         break;
  263.     case 'f':
  264.     case 'H':    /* Cursor motion */
  265.         dp->row = (dp->arg[0] == 0) ? 0 : dp->arg[0] - 1;
  266.         dp->col = (dp->arg[1] == 0) ? 0 : dp->arg[1] - 1;
  267.         dp->state = NORMAL;
  268.         dp->flags |= DIRTY_CURSOR;
  269.         break;
  270.     case 'h':    /* Set mode */
  271.         switch(dp->arg[0]){
  272.         case 7:    /* Turn on wrap mode */
  273.             dp->flags &= ~NOWRAP;
  274.             break;
  275.         }
  276.         break;
  277.     case 'J':    /* Clear screen */
  278.         switch(dp->arg[0]){
  279.         case 2:
  280.             dclrscr(dp);    /* Clear entire screen, home cursor */
  281.             break;
  282.         case 0:
  283.             dclreod(dp);    /* Clear to end of screen (VT-100) */
  284.             break;
  285.         }
  286.         break;
  287.     case 'K':    /* Erase to end of current line */
  288.         dclreol(dp);
  289.         break;
  290.     case 'L':    /* Add blank line */
  291.         dinsline(dp);
  292.         break;        
  293.     case 'l':    /* Clear mode */
  294.         switch(dp->arg[0]){
  295.         case 7:    /* Turn off wrap mode */
  296.             dp->flags |= NOWRAP;
  297.             break;
  298.         }
  299.         break;
  300.     case 'M':    /* Delete line */
  301.         ddelline(dp);
  302.         break;
  303.     case 'm':    /* Set screen attributes */
  304.         for(i=0;i<=dp->argi;i++){
  305.             dattrib(dp,dp->arg[i]);
  306.         }
  307.         break;
  308.     case 'P':    /* Delete character */
  309.         ddelchar(dp);
  310.         break;
  311.     case 's':    /* Save cursor position */
  312.         dp->savcol = dp->col;
  313.         dp->savrow = dp->row;
  314.         break;
  315.     case 'u':    /* Restore cursor position */
  316.         dp->col = dp->savcol;
  317.         dp->row = dp->savrow;
  318.         dp->flags |= DIRTY_CURSOR;
  319.         break;
  320.     }
  321.     dp->state = NORMAL;
  322. }
  323. /* Clear from cursor to end of screen, leaving cursor as is */
  324. static void
  325. dclreod(dp)
  326. struct display *dp;
  327. {
  328.     char *cp;
  329.     int i;
  330.  
  331.     cp = bufloc(dp);
  332.     i = (dp->rows - dp->row - 1) * dp->cols + (dp->cols - dp->col - 1);
  333.     while(i-- != 0){
  334.         *cp++ = ' ';
  335.         *cp++ = dp->attrib;
  336.     }
  337.     dp->flags |= DIRTY_SCREEN;
  338. }
  339.  
  340. static void
  341. dinsert(dp)
  342. struct display *dp;
  343. {
  344.     int i;
  345.     char *cp;
  346.  
  347.     cp = bufloc(dp);
  348.     i = 2*(dp->cols - dp->col - 1);
  349.     if(i != 0)
  350.         memmove(cp+2,cp,i);    /* handles overlapping blocks */
  351.     *cp++ = ' ';
  352.     *cp = dp->attrib;
  353.     dp->flags |= DIRTY_ROW;
  354. }
  355. static void
  356. ddelchar(dp)
  357. struct display *dp;
  358. {
  359.     char *cp;
  360.     int i;
  361.  
  362.     cp = bufloc(dp);
  363.     i = 2*(dp->cols-dp->col-1);
  364.     /* Copy characters to right one space left */
  365.     if(i != 0)
  366.         memmove(cp,cp+2,i);    /* memmove handles overlapping blocks */
  367.     /* Clear right most character on line */
  368.     cp[i] = ' ';
  369.     cp[i+1] = dp->attrib;
  370.     dp->flags |= DIRTY_ROW;
  371. }
  372. static void
  373. ddelline(dp)
  374. struct display *dp;
  375. {
  376.     char *cp;
  377.     int i;
  378.     int colsave;
  379.     int rowsave;
  380.  
  381.     colsave = dp->col;
  382.     rowsave = dp->row;
  383.     cp = bufloc(dp);
  384.     /* Copy up lines below this one */
  385.     i = 2*dp->cols*(dp->rows-dp->row-1);
  386.     if(i != 0)
  387.         memmove(cp,cp+2*dp->cols,i);
  388.     /* Clear bottom line */
  389.     dp->row = dp->rows - 1;
  390.     dclrline(dp);
  391.     dp->col = colsave;
  392.     dp->row = rowsave;
  393.     dp->flags |= DIRTY_SCREEN;
  394. }        
  395. /* Insert blank line where cursor is. Push existing lines down one */
  396. static void
  397. dinsline(dp)
  398. struct display *dp;
  399. {
  400.     char *cp;
  401.     int colsave;
  402.     int i;
  403.  
  404.     colsave = dp->col; /* Supposed to be issued only at start of line */
  405.     dp->col = 0;
  406.     cp = bufloc(dp);
  407.     i = 2*dp->cols*(dp->rows - dp->row - 1);
  408.  
  409.     /* Copy everything starting with current line down one line */
  410.     if(i != 0)
  411.         memmove(cp+2*dp->cols,cp,i);    /* does copy correctly */
  412.     dclrline(dp);            /* Clear current line */
  413.     dp->col = colsave;
  414.     dp->flags |= DIRTY_SCREEN;
  415. }
  416.  
  417. /* Process an argument to an attribute set command */
  418. static void
  419. dattrib(dp,val)
  420. struct display *dp;
  421. int val;
  422. {
  423.     switch(val){
  424.     case 0:    /* Normal white on black */
  425.         dp->attrib = 0x7;
  426.         break;
  427.     case 1:    /* High intensity */
  428.         dp->attrib |= 0x8;
  429.         break;
  430.     case 5:    /* Blink on */
  431.         dp->attrib |= 0x80;
  432.         break;
  433.     case 7:    /* Reverse video (black on white) */
  434.         dp->attrib = 0x70;
  435.         break;
  436.     default:
  437.         if(val >= 30 && val < 38){
  438.             /* Set foreground color */
  439.             dp->attrib = (dp->attrib & ~0x7) | fgattr[val - 30];
  440.         } else if(val >= 40 && val < 48){
  441.             /* Set background color */
  442.             dp->attrib = (dp->attrib & ~0x70) | ((bgattr[val - 40]) << 4);
  443.         }
  444.         break;
  445.     }
  446. }
  447. /* Display character */
  448. static void
  449. dchar(dp,c)
  450. struct display *dp;
  451. char c;
  452. {
  453.     char *cp;
  454.  
  455.     switch(c){
  456.     case ESC:
  457.         dp->state = ESCAPE;
  458.         return;
  459.     case '\0':    /* Ignore nulls and bells */
  460.     case BEL:
  461.         break;
  462.     case '\b':    /* Backspace */
  463.         if(dp->col > 0){
  464.             dp->col--;
  465.             dp->flags |= DIRTY_CURSOR;
  466.         }
  467.         break;
  468.     case FF:    /* Page feed */
  469.         dclrscr(dp);
  470.         break;
  471.     case '\t':    /* Tab */
  472.         if(dp->col < dp->cols - 8){
  473.             dp->col = (dp->col + 8) & ~7;
  474.             dp->flags |= DIRTY_CURSOR;
  475.         }
  476.         break;
  477.     case '\n':    /* Move cursor down one row */
  478.         dp->row++;
  479.         dp->flags |= DIRTY_CURSOR;
  480.         if(dp->flags & DIRTY_ROW)
  481.             dp->flags |= DIRTY_SCREEN;
  482.         break;
  483.     case '\r':    /* Move cursor to beginning of current row */
  484.         dp->col = 0;
  485.         dp->flags |= DIRTY_CURSOR;
  486.         break;
  487.     default:    /* Display character on screen */
  488.         /* Compute location in screen buffer memory */
  489.         cp = bufloc(dp);
  490.         if(c == '_' && *cp != ' '){
  491.             /* We'd like to underline the existing char,
  492.              * but we can't except on a monochrome display.
  493.              * So highlight it instead. (char-backspace-underscore)
  494.              */
  495.             *++cp = dp->attrib | 0x8;
  496.         } else if(c != ' ' && *cp == '_'){
  497.             /* underscore-backspace-char sequence;
  498.              * also intensify the char
  499.              */
  500.             *cp++ = c;
  501.             *cp = dp->attrib | 0x8;
  502.         } else {
  503.             /* Normal display */
  504.             *cp++ = c;
  505.             *cp = dp->attrib;
  506.         }
  507.         dp->flags |= DIRTY_CURSOR | DIRTY_ROW;
  508.         /* Update cursor position, wrapping if necessary */
  509.         if(++dp->col == dp->cols){
  510.             if(dp->flags & NOWRAP){
  511.                 dp->col--;
  512.             } else {
  513.                 dp->col = 0;
  514.                 dp->row++;
  515.                 if(dp->flags & DIRTY_ROW)
  516.                     dp->flags |= DIRTY_SCREEN;
  517.             }
  518.         }
  519.     }
  520.     /* Scroll screen if necessary */
  521.     if(dp->row == dp->rows){
  522.         dp->row--;
  523.         /* Scroll screen up */
  524.         dp->firstrow = (dp->firstrow + 1) % dp->rows;
  525.         if(!(dp->flags & NOSCROL))
  526.             dp->flags |= DIRTY_SCREEN;
  527.         dclrline(dp);
  528.     }
  529. }
  530.  
  531. /* Clear entire line containing cursor, leaving cursor alone */
  532. static void
  533. dclrline(dp)
  534. struct display *dp;
  535. {
  536.     char *cp;
  537.     int i;
  538.     int colsave;
  539.  
  540.     colsave = dp->col;
  541.     dp->col = 0;
  542.     cp = bufloc(dp);
  543.     for(i=dp->cols;i!=0;i--){
  544.         *cp++ = ' ';
  545.         *cp++ = dp->attrib;
  546.     }
  547.     dp->col = colsave;
  548.     dp->flags |= DIRTY_ROW;
  549. }
  550. /* Clear from cursor to end of line. Cursor is not moved */
  551. static void
  552. dclreol(dp)
  553. struct display *dp;
  554. {
  555.     char *cp;
  556.     int i;
  557.  
  558.     cp = bufloc(dp);
  559.     for(i=dp->cols - dp->col;i!=0;i--){
  560.         *cp++ = ' ';
  561.         *cp++ = dp->attrib;
  562.     }
  563.     dp->flags |= DIRTY_ROW;
  564. }
  565. /* Move cursor to top left corner, clear screen */
  566. static void
  567. dclrscr(dp)
  568. struct display *dp;
  569. {
  570.      char *cp;
  571.     int i;
  572.  
  573.     dp->row = dp->col = 0;
  574.     dp->firstrow = 0;
  575.     cp = bufloc(dp);
  576.     for(i=dp->rows*dp->cols;i!=0;i--){
  577.         *cp++ = ' ';
  578.         *cp++ = dp->attrib;
  579.     }
  580.     dp->flags |= (DIRTY_CURSOR|DIRTY_SCREEN);
  581. }
  582. /* Return pointer into screen buffer for current cursor location */
  583. static char *
  584. bufloc(dp)
  585. struct display *dp;
  586. {
  587.     int offset;
  588.  
  589.     offset = dp->col + dp->cols*((dp->row + dp->firstrow) % dp->rows);
  590.     return dp->buf + 2*offset;
  591. }
  592.