home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2996 / screen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-03-06  |  12.6 KB  |  696 lines

  1. /*
  2.  * Routines which deal with the characteristics of the terminal.
  3.  * Uses termcap to be as terminal-independent as possible.
  4.  *
  5.  * {{ Someday this should be rewritten to use curses. }}
  6.  */
  7.  
  8. #include "less.h"
  9. #if XENIX
  10. #include <sys/types.h>
  11. #include <sys/ioctl.h>
  12. #endif
  13.  
  14. #if TERMIO
  15. #include <termio.h>
  16. #else
  17. #include <sgtty.h>
  18. #endif
  19.  
  20. #if !TERMIO && defined(TIOCGWINSZ)
  21. #include <sys/ioctl.h>
  22. #else
  23. /*
  24.  * For the Unix PC (ATT 7300 & 3B1):
  25.  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  26.  * whether to include sys/window.h.  Use SIGPHONE from signal.h instead.
  27.  */
  28. #include <signal.h>
  29. #ifdef SIGPHONE
  30. #include <sys/window.h>
  31. #endif
  32. #endif
  33.  
  34. /*
  35.  * Strings passed to tputs() to do various terminal functions.
  36.  */
  37. static char
  38.     *sc_pad,        /* Pad string */
  39.     *sc_home,        /* Cursor home */
  40.     *sc_addline,        /* Add line, scroll down following lines */
  41.     *sc_lower_left,        /* Cursor to last line, first column */
  42.     *sc_move,        /* General cursor positioning */
  43.     *sc_clear,        /* Clear screen */
  44.     *sc_eol_clear,        /* Clear to end of line */
  45.     *sc_s_in,        /* Enter standout (highlighted) mode */
  46.     *sc_s_out,        /* Exit standout mode */
  47.     *sc_u_in,        /* Enter underline mode */
  48.     *sc_u_out,        /* Exit underline mode */
  49.     *sc_b_in,        /* Enter bold mode */
  50.     *sc_b_out,        /* Exit bold mode */
  51.     *sc_bl_in,        /* Enter blink mode */
  52.     *sc_bl_out,        /* Exit blink mode */
  53.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  54.     *sc_backspace,        /* Backspace cursor */
  55.     *sc_init,        /* Startup terminal initialization */
  56.     *sc_deinit;        /* Exit terminal de-initialization */
  57.  
  58. static int init_done = 0;
  59.  
  60. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  61. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  62. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  63. public int sc_width, sc_height;    /* Height & width of screen */
  64. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  65. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  66. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  67. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  68.  
  69. static char *cheaper();
  70.  
  71. /*
  72.  * These two variables are sometimes defined in,
  73.  * and needed by, the termcap library.
  74.  * It may be necessary on some systems to declare them extern here.
  75.  */
  76. /*extern*/ short ospeed;    /* Terminal output baud rate */
  77. /*extern*/ char PC;        /* Pad character */
  78.  
  79. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  80. extern int know_dumb;        /* Don't complain about a dumb terminal */
  81. extern int back_scroll;
  82. extern int swindow;
  83. extern char *tgetstr();
  84. extern char *tgoto();
  85. extern char *getenv();
  86.  
  87.  
  88. /*
  89.  * Change terminal to "raw mode", or restore to "normal" mode.
  90.  * "Raw mode" means 
  91.  *    1. An outstanding read will complete on receipt of a single keystroke.
  92.  *    2. Input is not echoed.  
  93.  *    3. On output, \n is mapped to \r\n.
  94.  *    4. \t is NOT expanded into spaces.
  95.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  96.  *       etc. are NOT disabled.
  97.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  98.  */
  99.     public void
  100. raw_mode(on)
  101.     int on;
  102. {
  103.     static int curr_on = 0;
  104.  
  105.     if (on == curr_on)
  106.         return;
  107. #if TERMIO
  108.     {
  109.     struct termio s;
  110.     static struct termio save_term;
  111.  
  112.     if (on)
  113.     {
  114.         /*
  115.          * Get terminal modes.
  116.          */
  117.         ioctl(2, TCGETA, &s);
  118.  
  119.         /*
  120.          * Save modes and set certain variables dependent on modes.
  121.          */
  122.         save_term = s;
  123.         ospeed = s.c_cflag & CBAUD;
  124.         erase_char = s.c_cc[VERASE];
  125.         kill_char = s.c_cc[VKILL];
  126.  
  127.         /*
  128.          * Set the modes to the way we want them.
  129.          */
  130.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  131.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  132.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  133.         s.c_cc[VMIN] = 1;
  134.         s.c_cc[VTIME] = 0;
  135.     } else
  136.     {
  137.         /*
  138.          * Restore saved modes.
  139.          */
  140.         s = save_term;
  141.     }
  142.     ioctl(2, TCSETAW, &s);
  143.     }
  144. #else
  145.     {
  146.     struct sgttyb s;
  147.     static struct sgttyb save_term;
  148.  
  149.     if (on)
  150.     {
  151.         /*
  152.          * Get terminal modes.
  153.          */
  154.         ioctl(2, TIOCGETP, &s);
  155.  
  156.         /*
  157.          * Save modes and set certain variables dependent on modes.
  158.          */
  159.         save_term = s;
  160.         ospeed = s.sg_ospeed;
  161.         erase_char = s.sg_erase;
  162.         kill_char = s.sg_kill;
  163.  
  164.         /*
  165.          * Set the modes to the way we want them.
  166.          */
  167.         s.sg_flags |= CBREAK;
  168.         s.sg_flags &= ~(ECHO|XTABS);
  169.     } else
  170.     {
  171.         /*
  172.          * Restore saved modes.
  173.          */
  174.         s = save_term;
  175.     }
  176.     ioctl(2, TIOCSETN, &s);
  177.     }
  178. #endif
  179.     curr_on = on;
  180. }
  181.  
  182.     static void
  183. cannot(s)
  184.     char *s;
  185. {
  186.     PARG parg;
  187.  
  188.     if (know_dumb)
  189.         /* 
  190.          * User knows this is a dumb terminal, so don't tell him.
  191.          */
  192.         return;
  193.  
  194.     parg.p_string = s;
  195.     error("WARNING: terminal cannot %s", &parg);
  196. }
  197.  
  198. /*
  199.  * Get size of the output screen.
  200.  */
  201.     public void
  202. get_scrsize(p_height, p_width)
  203.     int *p_height;
  204.     int *p_width;
  205. {
  206.     register char *s;
  207. #ifdef TIOCGWINSZ
  208.     struct winsize w;
  209. #else
  210. #ifdef WIOCGETD
  211.     struct uwdata w;
  212. #endif
  213. #endif
  214.  
  215. #ifdef TIOCGWINSZ
  216.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  217.         *p_height = w.ws_row;
  218.     else
  219. #else
  220. #ifdef WIOCGETD
  221.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  222.         *p_height = w.uw_height/w.uw_vs;
  223.     else
  224. #endif
  225. #endif
  226.     if ((s = getenv("LINES")) != NULL)
  227.         *p_height = atoi(s);
  228.     else
  229.          *p_height = tgetnum("li");
  230.  
  231.     if (*p_height <= 0)
  232.         *p_height = 24;
  233.  
  234. #ifdef TIOCGWINSZ
  235.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  236.         *p_width = w.ws_col;
  237.     else
  238. #ifdef WIOCGETD
  239.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  240.         *p_width = w.uw_width/w.uw_hs;
  241.     else
  242. #endif
  243. #endif
  244.     if ((s = getenv("COLUMNS")) != NULL)
  245.         *p_width = atoi(s);
  246.     else
  247.          *p_width = tgetnum("co");
  248.  
  249.      if (*p_width <= 0)
  250.           *p_width = 80;
  251. }
  252.  
  253. /*
  254.  * Get terminal capabilities via termcap.
  255.  */
  256.     public void
  257. get_term()
  258. {
  259.     char *sp;
  260.     register char *t1, *t2;
  261.     register int hard;
  262.     char *term;
  263.     char termbuf[2048];
  264.  
  265.     static char sbuf[1024];
  266.  
  267.     /*
  268.      * Find out what kind of terminal this is.
  269.      */
  270.      if ((term = getenv("TERM")) == NULL)
  271.          term = "unknown";
  272.      if (tgetent(termbuf, term) <= 0)
  273.          strcpy(termbuf, "dumb:hc:");
  274.  
  275.      hard = tgetflag("hc");
  276.  
  277.     /*
  278.      * Get size of the screen.
  279.      */
  280.     get_scrsize(&sc_height, &sc_width);
  281.     pos_init();
  282.     if (swindow < 0)
  283.         swindow = sc_height - 1;
  284.  
  285.     auto_wrap = tgetflag("am");
  286.     ignaw = tgetflag("xn");
  287.  
  288.     /*
  289.      * Assumes termcap variable "sg" is the printing width of:
  290.      * the standout sequence, the end standout sequence,
  291.      * the underline sequence, the end underline sequence,
  292.      * the boldface sequence, and the end boldface sequence.
  293.      */
  294.     if ((so_s_width = tgetnum("sg")) < 0)
  295.         so_s_width = 0;
  296.     so_e_width = so_s_width;
  297.  
  298.     bo_s_width = bo_e_width = so_s_width;
  299.     ul_s_width = ul_e_width = so_s_width;
  300.     bl_s_width = bl_e_width = so_s_width;
  301.  
  302.     /*
  303.      * Get various string-valued capabilities.
  304.      */
  305.     sp = sbuf;
  306.  
  307.     sc_pad = tgetstr("pc", &sp);
  308.     if (sc_pad != NULL)
  309.         PC = *sc_pad;
  310.  
  311.     sc_init = tgetstr("ti", &sp);
  312.     if (sc_init == NULL)
  313.         sc_init = "";
  314.  
  315.     sc_deinit= tgetstr("te", &sp);
  316.     if (sc_deinit == NULL)
  317.         sc_deinit = "";
  318.  
  319.     sc_eol_clear = tgetstr("ce", &sp);
  320.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  321.     {
  322.         cannot("clear to end of line");
  323.         sc_eol_clear = "";
  324.     }
  325.  
  326.     sc_clear = tgetstr("cl", &sp);
  327.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  328.     {
  329.         cannot("clear screen");
  330.         sc_clear = "\n\n";
  331.     }
  332.  
  333.     sc_move = tgetstr("cm", &sp);
  334.     if (hard || sc_move == NULL || *sc_move == '\0')
  335.     {
  336.         /*
  337.          * This is not an error here, because we don't 
  338.          * always need sc_move.
  339.          * We need it only if we don't have home or lower-left.
  340.          */
  341.         sc_move = "";
  342.     }
  343.  
  344.     sc_s_in = tgetstr("so", &sp);
  345.     if (hard || sc_s_in == NULL)
  346.         sc_s_in = "";
  347.  
  348.     sc_s_out = tgetstr("se", &sp);
  349.     if (hard || sc_s_out == NULL)
  350.         sc_s_out = "";
  351.  
  352.     sc_u_in = tgetstr("us", &sp);
  353.     if (hard || sc_u_in == NULL)
  354.         sc_u_in = sc_s_in;
  355.  
  356.     sc_u_out = tgetstr("ue", &sp);
  357.     if (hard || sc_u_out == NULL)
  358.         sc_u_out = sc_s_out;
  359.  
  360.     sc_b_in = tgetstr("md", &sp);
  361.     if (hard || sc_b_in == NULL)
  362.     {
  363.         sc_b_in = sc_s_in;
  364.         sc_b_out = sc_s_out;
  365.     } else
  366.     {
  367.         sc_b_out = tgetstr("me", &sp);
  368.         if (hard || sc_b_out == NULL)
  369.             sc_b_out = "";
  370.     }
  371.  
  372.     sc_bl_in = tgetstr("mb", &sp);
  373.     if (hard || sc_bl_in == NULL)
  374.     {
  375.         sc_bl_in = sc_s_in;
  376.         sc_bl_out = sc_s_out;
  377.     } else
  378.     {
  379.         sc_bl_out = sc_b_out;
  380.     }
  381.  
  382.     sc_visual_bell = tgetstr("vb", &sp);
  383.     if (hard || sc_visual_bell == NULL)
  384.         sc_visual_bell = "";
  385.  
  386.     if (tgetflag("bs"))
  387.         sc_backspace = "\b";
  388.     else
  389.     {
  390.         sc_backspace = tgetstr("bc", &sp);
  391.         if (sc_backspace == NULL || *sc_backspace == '\0')
  392.             sc_backspace = "\b";
  393.     }
  394.  
  395.     /*
  396.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  397.      * to move the cursor to the upper left corner of the screen.
  398.      */
  399.     t1 = tgetstr("ho", &sp);
  400.     if (hard || t1 == NULL)
  401.         t1 = "";
  402.     if (*sc_move == '\0')
  403.         t2 = "";
  404.     else
  405.     {
  406.         strcpy(sp, tgoto(sc_move, 0, 0));
  407.         t2 = sp;
  408.         sp += strlen(sp) + 1;
  409.     }
  410.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  411.  
  412.     /*
  413.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  414.      * to move the cursor to the lower left corner of the screen.
  415.      */
  416.     t1 = tgetstr("ll", &sp);
  417.     if (hard || t1 == NULL)
  418.         t1 = "";
  419.     if (*sc_move == '\0')
  420.         t2 = "";
  421.     else
  422.     {
  423.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  424.         t2 = sp;
  425.         sp += strlen(sp) + 1;
  426.     }
  427.     sc_lower_left = cheaper(t1, t2,
  428.         "move cursor to lower left of screen", "\r");
  429.  
  430.     /*
  431.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  432.      * to add a line at the top of the screen.
  433.      */
  434.     t1 = tgetstr("al", &sp);
  435.     if (hard || t1 == NULL)
  436.         t1 = "";
  437.     t2 = tgetstr("sr", &sp);
  438.     if (hard || t2 == NULL)
  439.         t2 = "";
  440.     sc_addline = cheaper(t1, t2, "scroll backwards", "");
  441.     if (*sc_addline == '\0')
  442.     {
  443.         /*
  444.          * Force repaint on any backward movement.
  445.          */
  446.         back_scroll = 0;
  447.     }
  448. }
  449.  
  450. /*
  451.  * Return the "best" of the two given termcap strings.
  452.  * The best, if both exist, is the one with the lower 
  453.  * cost (see cost() function).
  454.  */
  455.     static char *
  456. cheaper(t1, t2, doit, def)
  457.     char *t1, *t2;
  458.     char *doit;
  459.     char *def;
  460. {
  461.     if (*t1 == '\0' && *t2 == '\0')
  462.     {
  463.         cannot(doit);
  464.         return (def);
  465.     }
  466.     if (*t1 == '\0')
  467.         return (t2);
  468.     if (*t2 == '\0')
  469.         return (t1);
  470.     if (cost(t1) < cost(t2))
  471.         return (t1);
  472.     return (t2);
  473. }
  474.  
  475. /*
  476.  * Return the cost of displaying a termcap string.
  477.  * We use the trick of calling tputs, but as a char printing function
  478.  * we give it inc_costcount, which just increments "costcount".
  479.  * This tells us how many chars would be printed by using this string.
  480.  * {{ Couldn't we just use strlen? }}
  481.  */
  482. static int costcount;
  483.  
  484. /*ARGSUSED*/
  485.     static void
  486. inc_costcount(c)
  487.     int c;
  488. {
  489.     costcount++;
  490. }
  491.  
  492.     static int
  493. cost(t)
  494.     char *t;
  495. {
  496.     costcount = 0;
  497.     tputs(t, sc_height, inc_costcount);
  498.     return (costcount);
  499. }
  500.  
  501.  
  502. /*
  503.  * Below are the functions which perform all the 
  504.  * terminal-specific screen manipulation.
  505.  */
  506.  
  507.  
  508. /*
  509.  * Initialize terminal
  510.  */
  511.     public void
  512. init()
  513. {
  514.     tputs(sc_init, sc_height, putchr);
  515.     init_done = 1;
  516. }
  517.  
  518. /*
  519.  * Deinitialize terminal
  520.  */
  521.     public void
  522. deinit()
  523. {
  524.     if (!init_done)
  525.         return;
  526.     tputs(sc_deinit, sc_height, putchr);
  527.     init_done = 0;
  528. }
  529.  
  530. /*
  531.  * Home cursor (move to upper left corner of screen).
  532.  */
  533.     public void
  534. home()
  535. {
  536.     tputs(sc_home, 1, putchr);
  537. }
  538.  
  539. /*
  540.  * Add a blank line (called with cursor at home).
  541.  * Should scroll the display down.
  542.  */
  543.     public void
  544. add_line()
  545. {
  546.     tputs(sc_addline, sc_height, putchr);
  547. }
  548.  
  549. /*
  550.  * Move cursor to lower left corner of screen.
  551.  */
  552.     public void
  553. lower_left()
  554. {
  555.     tputs(sc_lower_left, 1, putchr);
  556. }
  557.  
  558. /*
  559.  * Ring the terminal bell.
  560.  */
  561.     public void
  562. bell()
  563. {
  564.     if (quiet == VERY_QUIET)
  565.         vbell();
  566.     else
  567.         putchr('\7');
  568. }
  569.  
  570. /*
  571.  * Output the "visual bell", if there is one.
  572.  */
  573.     public void
  574. vbell()
  575. {
  576.     if (*sc_visual_bell == '\0')
  577.         return;
  578.     tputs(sc_visual_bell, sc_height, putchr);
  579. }
  580.  
  581. /*
  582.  * Clear the screen.
  583.  */
  584.     public void
  585. clear()
  586. {
  587.     tputs(sc_clear, sc_height, putchr);
  588. }
  589.  
  590. /*
  591.  * Clear from the cursor to the end of the cursor's line.
  592.  * {{ This must not move the cursor. }}
  593.  */
  594.     public void
  595. clear_eol()
  596. {
  597.     tputs(sc_eol_clear, 1, putchr);
  598. }
  599.  
  600. /*
  601.  * Begin "standout" (bold, underline, or whatever).
  602.  */
  603.     public void
  604. so_enter()
  605. {
  606.     tputs(sc_s_in, 1, putchr);
  607. }
  608.  
  609. /*
  610.  * End "standout".
  611.  */
  612.     public void
  613. so_exit()
  614. {
  615.     tputs(sc_s_out, 1, putchr);
  616. }
  617.  
  618. /*
  619.  * Begin "underline" (hopefully real underlining, 
  620.  * otherwise whatever the terminal provides).
  621.  */
  622.     public void
  623. ul_enter()
  624. {
  625.     tputs(sc_u_in, 1, putchr);
  626. }
  627.  
  628. /*
  629.  * End "underline".
  630.  */
  631.     public void
  632. ul_exit()
  633. {
  634.     tputs(sc_u_out, 1, putchr);
  635. }
  636.  
  637. /*
  638.  * Begin "bold"
  639.  */
  640.     public void
  641. bo_enter()
  642. {
  643.     tputs(sc_b_in, 1, putchr);
  644. }
  645.  
  646. /*
  647.  * End "bold".
  648.  */
  649.     public void
  650. bo_exit()
  651. {
  652.     tputs(sc_b_out, 1, putchr);
  653. }
  654.  
  655. /*
  656.  * Begin "blink"
  657.  */
  658.     public void
  659. bl_enter()
  660. {
  661.     tputs(sc_bl_in, 1, putchr);
  662. }
  663.  
  664. /*
  665.  * End "blink".
  666.  */
  667.     public void
  668. bl_exit()
  669. {
  670.     tputs(sc_bl_out, 1, putchr);
  671. }
  672.  
  673. /*
  674.  * Erase the character to the left of the cursor 
  675.  * and move the cursor left.
  676.  */
  677.     public void
  678. backspace()
  679. {
  680.     /* 
  681.      * Try to erase the previous character by overstriking with a space.
  682.      */
  683.     tputs(sc_backspace, 1, putchr);
  684.     putchr(' ');
  685.     tputs(sc_backspace, 1, putchr);
  686. }
  687.  
  688. /*
  689.  * Output a plain backspace, without erasing the previous char.
  690.  */
  691.     public void
  692. putbs()
  693. {
  694.     tputs(sc_backspace, 1, putchr);
  695. }
  696.