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