home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume33 / problem / part03 / display.C next >
Encoding:
C/C++ Source or Header  |  1992-10-19  |  17.0 KB  |  666 lines

  1. /*
  2. ** Routines controlling the physical display
  3. **
  4. ** display.C display.C 1.18   Delta\'d: 15:10:44 10/9/92   Mike Lijewski, CNSF
  5. **
  6. ** Copyright \(c\) 1991, 1992 Cornell University
  7. ** All rights reserved.
  8. **
  9. ** Redistribution and use in source and binary forms are permitted
  10. ** provided that: \(1\) source distributions retain this entire copyright
  11. ** notice and comment, and \(2\) distributions including binaries display
  12. ** the following acknowledgement:  ``This product includes software
  13. ** developed by Cornell University\'\' in the documentation or other
  14. ** materials provided with the distribution and in all advertising
  15. ** materials mentioning features or use of this software. Neither the
  16. ** name of the University nor the names of its contributors may be used
  17. ** to endorse or promote products derived from this software without
  18. ** specific prior written permission.
  19. **
  20. ** THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR
  21. ** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  22. ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  23. */
  24.  
  25. #ifndef _IBMR2
  26. #include <libc.h>
  27. #endif /*_IBMR2*/
  28.  
  29. #include <osfcn.h>
  30. #include <signal.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <sys/ioctl.h>
  35.  
  36. #ifdef TERMIOS
  37. #include <termios.h>
  38. #include <unistd.h>
  39. #elif  TERMIO
  40. #include <termio.h>
  41. #if defined(ISC) || defined(ESIX)
  42. #include <sys/stream.h>
  43. #include <sys/ptem.h>
  44. #endif /*ISC||ESIX*/
  45. #else
  46. #include <sgtty.h>
  47. #endif
  48.  
  49. #include "utilities.h"
  50. #include "display.h"
  51.  
  52. #ifndef EXIT_FAILURE
  53. #define EXIT_FAILURE 1
  54. #endif
  55.  
  56. /*
  57. ** The definition of `ospeed\' -- needed by the termcap routines.
  58. */
  59. short ospeed;
  60.  
  61. //
  62. // termcap capabilities we use
  63. //
  64. char *AL;               // insert blank line before cursor
  65. char *ALN;              // insert N blank lines at cursor
  66. int   AM;               // automatic margins?
  67. char *BC;               // backspace, if not BS
  68. int   BS;               // ASCII backspace works
  69. char *CD;               // clear to end of display
  70. char *CE;               // clear to end of line
  71. char *CL;               // clear screen
  72. int   CO;               // number of columns
  73. char *CM;               // cursor motion
  74. char *CR;               // cursor beginning of line
  75. char *CS;               // set scroll region
  76. int   DA;               // backing store off top?
  77. int   DB;               // backing store off bottom?
  78. char *DC;               // delete character at cursor
  79. char *DL;               // delete line cursor is on
  80. char *DLN;              // delete N lines from cursor
  81. char *DM;               // string to enter delete mode
  82. char *DO;               // cursor down
  83. char *ED;               // string to end delete mode
  84. int   HC;               // hardcopy terminal?
  85. char *IS;               // initialize terminal
  86. char *HO;               // cursor home
  87. char *KD;               // down arrow key
  88. char *KE;               // de-initialize keypad
  89. char *KS;               // initialize keypad \(for arrow keys\)
  90. char *KU;               // up arrrow key
  91. char *LE;               // cursor back one column
  92. int   LI;               // number of rows
  93. char *LL;               // cursor to lower left
  94. int   OS;               // terminal overstrikes?
  95. #ifndef APOLLO
  96. char  PC;               // pad character
  97. #endif
  98. char *PCstr;            // pad string
  99. char *SE;               // end standout mode
  100. char *SF;               // scroll screen up one line
  101. char *SO;               // enter standout mode
  102. char *SR;               // scroll screen down one line
  103. char *TE;               // end cursor addressing mode
  104. char *TI;               // enter cursor addressing mode
  105. char *UP;               // cursor up
  106. char *VE;               // end visual mode
  107. char *VS;               // enter visual mode
  108. char *XN;               // strange wrap behaviour
  109.  
  110. /*
  111. ** termcap - reads termcap file setting all the terminal capabilities
  112. **           which we\'ll use.
  113. */
  114.  
  115. void termcap(const char *term_type)
  116. {
  117.     static char capability_buffer[512];
  118.     char* bp = capability_buffer;
  119.     char termcap_buffer[2048];
  120.     
  121.     switch (tgetent(termcap_buffer, term_type))
  122.     {
  123.       case -1:
  124.         (void)fputs("couldn't open termcap database\n", stderr);
  125.         exit(1);
  126.       case 0:
  127.         (void)fprintf(stderr, "invalid terminal type: `%s'\n", term_type);
  128.         exit(1);
  129.       default: break;
  130.     }
  131.     
  132.     AL = tgetstr("al", &bp);
  133.     ALN = tgetstr("AL", &bp);
  134.     AM = tgetflag("am");
  135.     BC = tgetstr("bc", &bp);
  136.     BS = tgetflag("bs");
  137.     CD = tgetstr("cd", &bp);
  138.     CE = tgetstr("ce", &bp);
  139.     CL = tgetstr("cl", &bp);
  140.     CM = tgetstr("cm", &bp);
  141.     CR = tgetstr("cr", &bp);
  142.     CS = tgetstr("cs", &bp);
  143.     DA = tgetflag("da");
  144.     DB = tgetflag("db");
  145.     DC = tgetstr("dc", &bp);
  146.     DL = tgetstr("dl", &bp);
  147.     DLN = tgetstr("DL", &bp);
  148.     DM = tgetstr("dm", &bp);
  149.     DO = tgetstr("do", &bp);
  150.     ED = tgetstr("ed", &bp);
  151.     HC = tgetflag("hc");
  152.     HO = tgetstr("ho", &bp);
  153.     IS = tgetstr("is", &bp);
  154.     KD = tgetstr("kd", &bp);
  155.     KE = tgetstr("ke", &bp);
  156.     KS = tgetstr("ks", &bp);
  157.     KU = tgetstr("ku", &bp);
  158.     LE = tgetstr("le", &bp);
  159.     LL = tgetstr("ll", &bp);
  160.     OS = tgetflag("os");
  161.     PCstr = tgetstr("pc", &bp);
  162.     SE = tgetstr("se", &bp);
  163.     SF = tgetstr("sf", &bp);
  164.     SO = tgetstr("so", &bp);
  165.     SR = tgetstr("sr", &bp);
  166.     TE = tgetstr("te", &bp);
  167.     TI = tgetstr("ti", &bp);
  168.     UP = tgetstr("up", &bp);
  169.     VE = tgetstr("ve", &bp);
  170.     VS = tgetstr("vs", &bp);
  171.     XN = tgetstr("xn", &bp);
  172.     
  173.     PC = PCstr ? PCstr[0] :  0;
  174.     
  175.     if (!BC && !LE && !BS)
  176.     {
  177.         (void)fputs("terminal can't backspace - unusable\n", stderr);
  178.         exit(1);
  179.     }
  180.     if (!BC) BC = LE ? LE : "\b";
  181.     if (!CR) CR = "\r";
  182.     if (!DO) DO = SF ? SF : "\n";
  183.     
  184.     const char *tmp = getenv("LINES");
  185.     if (tmp) LI = atoi(tmp);
  186.     tmp = getenv("COLUMNS");
  187.     if (tmp) CO = atoi(tmp);
  188.     
  189. #ifdef TIOCGWINSZ
  190.     struct winsize win;
  191.     if (ioctl(2, TIOCGWINSZ, (char *)&win) == 0)
  192.     {
  193.         if (LI == 0 && win.ws_row > 0) LI = win.ws_row;
  194.         if (CO == 0 && win.ws_col > 0) CO = win.ws_col;
  195.     }
  196. #endif
  197.     
  198.     if (CO == 0) CO = tgetnum("co");
  199.     if (LI == 0) LI = tgetnum("li");
  200.     
  201.     if (LI == -1 || CO == -1 || HC || !CM || !CE)
  202.     {
  203.         (void)fputs("terminal too dumb to be useful\n", stderr);
  204.         exit(1);
  205.     }
  206.     if (LI < 5)
  207.     {
  208.         (void)fputs("too few rows to be useful\n", stderr);
  209.         exit(1);
  210.     }
  211. }
  212.  
  213. /*
  214. ** setraw - puts terminal into raw mode.  Cbreak mode actually, but
  215. **          why be pedantic.  Flow control is disabled as well as BREAK keys.
  216. **          Echoing is turned off as well as signal generation.  Hence
  217. **          keyboard generated signals must be simulated.  Also sets
  218. **          `ospeed\'.
  219. */
  220.  
  221. #ifdef TERMIOS
  222. static struct termios tty_mode;    /* save tty mode here */
  223. #elif  TERMIO
  224. static struct termio tty_mode;    /* save tty mode here */
  225. #else
  226. static struct sgttyb  oarg;      /* save tty stuff here */
  227. static struct tchars  otarg;
  228. static struct ltchars oltarg;
  229. #endif
  230.  
  231. void setraw()
  232. {
  233. #ifdef TERMIOS
  234.     struct termios temp_mode;
  235.  
  236.     if (tcgetattr(STDIN_FILENO, &temp_mode) < 0)
  237.     {
  238.         perror("tcgetattr");
  239.         exit(EXIT_FAILURE);
  240.     }
  241.  
  242.     tty_mode = temp_mode;  /* save for latter restoration */
  243.  
  244.     temp_mode.c_iflag &= ~(IGNBRK|ICRNL|INLCR);
  245.     temp_mode.c_lflag &= ~(ICANON|ECHO|IEXTEN);
  246.     temp_mode.c_oflag &= ~OPOST;
  247.     temp_mode.c_cc[VQUIT] = 28; // C-\ is QUIT
  248.     temp_mode.c_cc[VMIN]  = 1;    // min #chars to satisfy read
  249.     temp_mode.c_cc[VTIME] = 0;    // read returns immediately
  250.  
  251.     if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &temp_mode) < 0)
  252.     {
  253.         perror("tcsetattr");
  254.         exit(EXIT_FAILURE);
  255.     }
  256.  
  257.     ospeed = cfgetospeed(&temp_mode);
  258. #elif TERMIO
  259.     struct termio temp_mode;
  260.     
  261.     if (ioctl(0, TCGETA, (char *)&temp_mode) < 0)
  262.     {
  263.         perror("ioctl - TCGETA");
  264.         exit(EXIT_FAILURE);
  265.     }
  266.  
  267.     tty_mode = temp_mode;  /* save for latter restoration */
  268.  
  269.     temp_mode.c_iflag &= ~(IGNBRK|ICRNL|INLCR);
  270.     temp_mode.c_lflag &= ~(ICANON|ECHO);
  271.     temp_mode.c_oflag &= ~OPOST;
  272.     temp_mode.c_cc[VQUIT] = 28; // C-\ is QUIT
  273.     temp_mode.c_cc[VMIN]  = 1;    // min #chars to satisfy read
  274.     temp_mode.c_cc[VTIME] = 0;    // read returns immediately
  275.  
  276.     if (ioctl(0, TCSETA, (char *)&temp_mode) < 0)
  277.     {
  278.         perror("ioctl - TCSETA");
  279.         exit(EXIT_FAILURE);
  280.     }
  281.  
  282.     ospeed = temp_mode.c_cflag & CBAUD;
  283. #else
  284.     struct sgttyb arg;
  285.     struct tchars targ;
  286.     struct ltchars ltarg;
  287.  
  288.     if (ioctl(fileno(stdin), TIOCGETP, (char *)&arg) < 0)
  289.     {
  290.         perror("ioctl - TIOCGETP");
  291.         exit(EXIT_FAILURE);
  292.     }
  293.     if (ioctl(fileno(stdin), TIOCGETC, (char *)&targ) < 0)
  294.     {
  295.         perror("ioctl - TIOCGETC");
  296.         exit(EXIT_FAILURE);
  297.     }
  298.     if (ioctl(fileno(stdin), TIOCGLTC, (char *)<arg) < 0)
  299.     {
  300.         perror("ioctl - TIOCGLTC");
  301.         exit(EXIT_FAILURE);
  302.     }
  303.  
  304.     oarg   = arg;
  305.     otarg  = targ;
  306.     oltarg = ltarg;
  307.  
  308.     arg.sg_flags=((arg.sg_flags&~(ECHO|CRMOD))|CBREAK) ;
  309.     targ.t_eofc    = -1;  // turn off end-of-file character
  310.     targ.t_brkc    = -1;  // turn off break delimiter
  311.     ltarg.t_dsuspc = -1;  // turn off delayed suspend character
  312.     ltarg.t_rprntc = -1;  // turn off reprint line character
  313.     ltarg.t_flushc = -1;  // turn off flush character
  314.     ltarg.t_werasc = -1;  // turn off erase work character
  315.     ltarg.t_lnextc = -1;  // turn off literal next char
  316.  
  317.     if (ioctl(fileno(stdin), TIOCSETN, (char *)&arg) < 0)
  318.     {
  319.         perror("ioctl - TIOCSETN");
  320.         exit(EXIT_FAILURE);
  321.     }
  322.     if (ioctl(fileno(stdin), TIOCSETC, (char *)&targ) < 0)
  323.     {
  324.         perror("ioctl - TIOCSETC");
  325.         exit(EXIT_FAILURE);
  326.     }
  327.     if (ioctl(fileno(stdin), TIOCSLTC, (char *)<arg) < 0)
  328.     {
  329.         perror("ioctl - TIOCSLTC");
  330.         exit(EXIT_FAILURE);
  331.     }
  332.  
  333.     ospeed = arg.sg_ospeed;
  334. #endif
  335. }
  336.  
  337. /*
  338.  * unsetraw - Restore a terminal\'s mode to whatever it was on the most
  339.  *            recent call to the setraw\(\) function above.
  340.  *            Exits with rc==1 on failure.
  341.  */
  342.  
  343. void unsetraw()
  344. {
  345. #ifdef TERMIOS
  346.     if (tcsetattr(0, TCSAFLUSH, &tty_mode) < 0)
  347.     {
  348.         perror("tcsetattr");
  349.         exit(EXIT_FAILURE);
  350.     }
  351. #elif TERMIO
  352.     if (ioctl(0, TCSETA, (char *)&tty_mode) < 0)
  353.     {
  354.         perror("ioctl - TCSETA");
  355.         exit(EXIT_FAILURE);
  356.     }
  357. #else
  358.     if (ioctl(fileno(stdin), TIOCSETN, (char *)&oarg) < 0)
  359.     {
  360.         perror("ioctl - TIOSETN");
  361.         exit(EXIT_FAILURE);
  362.     }
  363.     if (ioctl(fileno(stdin), TIOCSETC, (char *)&otarg) < 0)
  364.     {
  365.         perror("ioctl - TIOSETC");
  366.         exit(EXIT_FAILURE);
  367.     }
  368.     if (ioctl(fileno(stdin), TIOCSLTC, (char *)&oltarg) < 0) {
  369.         perror("ioctl - TIOSLTC");
  370.         exit(EXIT_FAILURE);
  371.     }
  372. #endif
  373. }
  374.  
  375. /*
  376. ** outputch - a function to output a single character.
  377. **            Termcap routines NEED a function.
  378. */
  379.  
  380. int outputch(int ch) { return putchar(ch); }
  381.  
  382. /*
  383. ** initialize display
  384. */
  385.  
  386. void init_display()
  387. {
  388.     setvbuf(stdout, 0, _IOFBF, 0);  // fully buffer stdout
  389.     setvbuf(stdin , 0, _IONBF, 0);  // no buffering on stdin
  390.  
  391.     const char *term = getenv("TERM");
  392.     if (term == 0 || *term == 0)
  393.     {
  394.         (void)fputs("please set your TERM variable appropriately\n", stderr);
  395.         exit(1);
  396.     }
  397.  
  398.     termcap(term);
  399.  
  400.     setraw();
  401.     initialize_terminal();
  402.     enter_cursor_addressing_mode();
  403.     enter_visual_mode();
  404.     enable_keypad();
  405.     clear_display();
  406.     synch_display();
  407. }
  408.  
  409. /*
  410. ** terminate display
  411. */
  412.  
  413. void term_display()
  414. {
  415.     output_string_capability(tgoto(CM, 0, rows()-1));
  416.     end_visual_mode();
  417.     end_cursor_addressing_mode();
  418.     disable_keypad();
  419.     synch_display();
  420.     unsetraw();
  421. }
  422.  
  423. // Have we just been continued after being suspended?
  424. // Should be a sig_atomic_t.
  425. int resumingAfterSuspension;
  426.  
  427. /*
  428. ** scroll_listing_up_N - scrolls the listing window up n lines.
  429. **                       The cursor is left in column 0 of the first
  430. **                       line to scroll into the window.
  431. **                       Must have CS capability.
  432. */
  433.  
  434. void scroll_listing_up_N(int n)
  435. {
  436.     output_string_capability(tgoto(CS, rows()-3, 0));
  437.     move_cursor(rows()-3, 0);
  438.     for (int i = 0; i < n; i++) cursor_down();
  439.     output_string_capability(tgoto(CS, rows()-1, 0));
  440.     move_cursor(rows()-2-n, 0);
  441. }
  442.  
  443. /*
  444. ** scroll_listing_down_N - half_down - scrolls the listing window
  445. **                         \(line 0 - rows\(\)-3\) down \(rows\(\)-2\)/2 lines.
  446. **                         The cursor is left in HOME position.
  447. **                         Must have CS capability.
  448. */
  449.  
  450. void scroll_listing_down_N(int n)
  451. {
  452.     output_string_capability(tgoto(CS, rows()-3, 0));
  453.     move_cursor(0, 0);
  454.     for (int i = 0; i < n; i++) output_string_capability(SR, rows()-2);
  455.     output_string_capability(tgoto(CS, rows()-1, 0));
  456.     cursor_home();
  457. }
  458.  
  459. /*
  460. ** scroll_listing_up_one - scrolls the listing window \(line 0 - rows\(\)-3\)
  461. **                         up one row. The cursor is left in column
  462. **                         0 of rows\(\)-3 row.  Assumes CS capability.
  463. */
  464.  
  465. void scroll_listing_up_one()
  466. {
  467.     output_string_capability(tgoto(CS, rows()-3, 0));
  468.     move_cursor(rows()-3, 0);
  469.     cursor_down();
  470.     output_string_capability(tgoto(CS, rows()-1, 0));
  471.     move_cursor(rows()-3, 0);
  472. }
  473.  
  474. /*
  475. ** scroll_listing_down_one - scrolls the listing window \(line 0 - rows\(\)-3\)
  476. **                           down one row. The cursor is left at HOME.
  477. **                           Assumes CS capability.
  478. */
  479.  
  480. void scroll_listing_down_one()
  481. {
  482.     output_string_capability(tgoto(CS, rows()-3, 0));
  483.     cursor_home();
  484.     output_string_capability(SR, rows()-2);
  485.     output_string_capability(tgoto(CS, rows()-1, 0));
  486.     cursor_home();
  487. }
  488.  
  489. /*
  490. ** termstop - caught a SIGTSTP.  We are relying on the signal to
  491. **            interrupt read\(2\).
  492. */
  493.  
  494. #ifdef SIGTSTP
  495. void termstop(int)
  496. {
  497.     (void)signal(SIGTSTP,  SIG_IGN);
  498. #ifdef SIGWINCH
  499.     (void)signal(SIGWINCH, SIG_IGN);
  500. #endif
  501.     clear_display();
  502.     synch_display();
  503.     unsetraw();
  504.     (void)kill(getpid(), SIGSTOP);
  505.     setraw();
  506.     (void)signal(SIGTSTP,  termstop);
  507. #ifdef SIGWINCH
  508.     (void)signal(SIGWINCH, winch);
  509. #endif
  510.     // window size may have changed
  511. #ifdef TIOCGWINSZ
  512.     int oCO = columns(), oLI = rows();
  513.     struct winsize w;
  514.     if (ioctl(2, TIOCGWINSZ, (char *)&w) == 0 && w.ws_row > 0) LI = w.ws_row;
  515.     if (ioctl(2, TIOCGWINSZ, (char *)&w) == 0 && w.ws_col > 0) CO = w.ws_col;
  516.     if (oCO != columns() || oLI != rows()) windowSizeChanged = 1;
  517. #endif
  518.     resumingAfterSuspension = 1;
  519. }
  520. #endif /*SIGTSTP*/
  521.  
  522. /*
  523. ** clear_display
  524. */
  525.  
  526. void clear_display() {
  527.     if (CL)
  528.         output_string_capability(CL);
  529.     else if (CD)
  530.     {
  531.         cursor_home();
  532.         output_string_capability(CD);
  533.     }
  534.     else
  535.     {
  536.         cursor_home();
  537.         for (int i = 0; i < rows(); i++)
  538.         {
  539.             clear_to_end_of_line();
  540.             cursor_down();
  541.         }
  542.         cursor_home();
  543.     }
  544. }
  545.  
  546. /*
  547. ** scroll_screen_up_one - must have DL or SF
  548. */
  549.  
  550. void scroll_screen_up_one()
  551. {
  552.     if (DL)
  553.     {
  554.         cursor_home();
  555.         output_string_capability(DL, rows());
  556.     }
  557.     else
  558.     {
  559.         move_cursor(rows()-1, 0);
  560.         output_string_capability(SF, rows());
  561.     }
  562.     if (DB) clear_message_line();
  563. }
  564.  
  565. /*
  566. ** scroll_screen_down_one - must have AL or SR
  567. */
  568.  
  569. void scroll_screen_down_one()
  570. {
  571.     cursor_home();
  572.  
  573.     if (AL)
  574.         output_string_capability(AL, rows());
  575.     else
  576.         output_string_capability(SR, rows());
  577.     if (DA) clear_to_end_of_line();
  578. }
  579.  
  580. /*
  581. ** scroll_screen_up_N - must have DLN, DL or SF.
  582. **         
  583. */
  584.  
  585. void scroll_screen_up_N(int n)
  586. {
  587.     if (DLN)
  588.     {
  589.         cursor_home();
  590.         output_string_capability(tgoto(DLN, 0, n), rows());
  591.     }
  592.     else if (DL)
  593.     {
  594.         cursor_home();
  595.         for (int i = 0; i < n; i++)
  596.             output_string_capability(DL, rows());
  597.     }
  598.     else
  599.     {
  600.         move_cursor(rows()-1, 0);
  601.         for (int i = 0; i < n; i++) output_string_capability(SF, rows());
  602.     }
  603.     if (DB) clear_to_end_of_screen(rows()-n);
  604. }
  605.  
  606. /*
  607. ** scroll_screen_down_N - must have ALN, AL or SR.
  608. */
  609.  
  610. void scroll_screen_down_N(int n)
  611. {
  612.     cursor_home();
  613.     int i;
  614.     if (ALN)
  615.         output_string_capability(tgoto(ALN, 0, n), rows());
  616.     else if (AL)
  617.         for (i = 0; i < n; i++) output_string_capability(AL, rows());
  618.     else
  619.         for (i = 0; i < n; i++) output_string_capability(SR, rows());
  620.     if (DA)
  621.     {
  622.         for (i = 0; i < n; i++)
  623.         {
  624.             clear_to_end_of_line();
  625.             cursor_down();
  626.         }
  627.         cursor_home();
  628.     }
  629. }
  630.  
  631. /*
  632. ** clear_to_end_of_screen - clears screen from line y to the bottom
  633. */
  634.  
  635. void clear_to_end_of_screen(int y)
  636. {
  637.     move_cursor(y, 0);
  638.     if (CD)
  639.         output_string_capability(DL, rows()-y);
  640.     else
  641.         for (int i = 0; i < rows()-y; i++)
  642.         {
  643.             clear_to_end_of_line();
  644.             putchar('\n');
  645.         }
  646. }
  647.  
  648. /*
  649. ** delete_listing_line - deletes line at line y, scrolling the lines below
  650. **                       y up.  We only call this routine when we KNOW that
  651. **                       there is at least one line in need of being scrolled
  652. **                       up. Must have CS capability.
  653. */
  654.  
  655. void delete_listing_line(int y)
  656. {
  657.     move_cursor(y, 0);
  658.     clear_to_end_of_line();
  659.     output_string_capability(tgoto(CS, rows()-3, y));
  660.     move_cursor(rows()-3, 0);
  661.     cursor_down();
  662.     output_string_capability(tgoto(CS, rows()-1, 0));
  663. }
  664.  
  665.  
  666.