home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume27 / ytalk-3.0 / part01 / term.c < prev    next >
C/C++ Source or Header  |  1993-08-20  |  22KB  |  1,170 lines

  1. /* term.c */
  2.  
  3. /*               NOTICE
  4.  *
  5.  * Copyright (c) 1990,1992,1993 Britt Yenne.  All rights reserved.
  6.  * 
  7.  * This software is provided AS-IS.  The author gives no warranty,
  8.  * real or assumed, and takes no responsibility whatsoever for any 
  9.  * use or misuse of this software, or any damage created by its use
  10.  * or misuse.
  11.  * 
  12.  * This software may be freely copied and distributed provided that
  13.  * no part of this NOTICE is deleted or edited in any manner.
  14.  * 
  15.  */
  16.  
  17. /* Mail comments or questions to ytalk@austin.eds.com */
  18.  
  19. #include "header.h"
  20. #ifdef USE_SGTTY
  21. # include <sys/ioctl.h>
  22. # ifdef hpux
  23. #  include <sys/bsdtty.h>
  24. #  include <sgtty.h>
  25. # endif
  26. #else
  27. # include <termios.h>
  28. #endif
  29. #include "curses.h"
  30. #include "xwin.h"
  31. #include "menu.h"
  32.  
  33. static int  (*_open_term)();    /* open a new terminal */
  34. static void (*_close_term)();    /* close a terminal */
  35. static void (*_addch_term)();    /* write a char to a terminal */
  36. static void (*_move_term)();    /* move cursor to Y,X position */
  37. static void (*_clreol_term)();    /* clear to end of line */
  38. static void (*_clreos_term)();    /* clear to end of screen */
  39. static void (*_scroll_term)();    /* scroll up one line */
  40. static void (*_rev_scroll_term)(); /* scroll down one line */
  41. static void (*_flush_term)();    /* flush pending output */
  42.  
  43. static int term_type = 0;
  44. #ifdef USE_SGTTY
  45.  static int line_discipline;
  46.  static int local_mode;
  47.  static struct sgttyb sgttyb;
  48.  static struct tchars tchars;
  49.  static struct ltchars ltchars;
  50. #else
  51.  static struct termios tio;
  52. #endif
  53.  
  54. #ifdef USE_SGTTY
  55. static void
  56. init_sgtty()
  57. {
  58.     if(ioctl(0, TIOCGETD, &line_discipline) < 0)
  59.     {
  60.     show_error("TIOCGETD");
  61.     bail(YTE_INIT);
  62.     }
  63.     if(ioctl(0, TIOCLGET, &local_mode) < 0)
  64.     {
  65.     show_error("TIOCGETP");
  66.     bail(YTE_INIT);
  67.     }
  68.     if(ioctl(0, TIOCGETP, &sgttyb) < 0)
  69.     {
  70.     show_error("TIOCGETP");
  71.     bail(YTE_INIT);
  72.     }
  73.     if(ioctl(0, TIOCGETC, &tchars) < 0)
  74.     {
  75.     show_error("TIOCGETC");
  76.     bail(YTE_INIT);
  77.     }
  78.     if(ioctl(0, TIOCGLTC, <chars) < 0)
  79.     {
  80.     show_error("TIOCGLTC");
  81.     bail(YTE_INIT);
  82.     }
  83.     me->old_rub = sgttyb.sg_erase;
  84.     me->RUB  = RUBDEF;
  85.     me->KILL = sgttyb.sg_kill;
  86.     me->WORD = ltchars.t_werasc;
  87.     me->CLR = '\024';        /* ^T */
  88. }
  89. #else
  90. static void
  91. init_termios()
  92. {
  93.     /* get edit chars */
  94.  
  95.     if(tcgetattr(0, &tio) < 0)
  96.     {
  97.     show_error("tcgetattr failed");
  98.     bail(YTE_INIT);
  99.     }
  100.     me->old_rub = tio.c_cc[VERASE];
  101.     me->RUB  = RUBDEF;
  102. #ifdef VKILL
  103.     me->KILL = tio.c_cc[VKILL];
  104. #else
  105.     me->KILL = '\025';    /* ^U */
  106. #endif
  107. #ifdef VWERASE
  108.     me->WORD = tio.c_cc[VWERASE];
  109.     if(me->WORD == 0xff)
  110.     me->WORD = '\027';    /* ^W */
  111. #else
  112.     me->WORD = '\027';    /* ^W */
  113. #endif
  114.     me->CLR = '\024';        /* ^T */
  115. }
  116. #endif
  117.  
  118. /* Initialize terminal and input characteristics.
  119.  */
  120. void
  121. init_term()
  122. {
  123.     char tmpstr[64];
  124.  
  125. #ifdef USE_SGTTY
  126.     init_sgtty();
  127. #else
  128.     init_termios();
  129. #endif
  130.  
  131.     /* Decide on a terminal (window) system and set up the
  132.      * function pointers.
  133.      */
  134.  
  135.     term_type = 0;    /* nothing selected yet */
  136.  
  137. #ifdef USE_X11
  138.     if(term_type == 0 && (def_flags & FL_XWIN) && getenv("DISPLAY"))
  139.     {
  140.     _open_term = open_xwin;
  141.     _close_term = close_xwin;
  142.     _addch_term = addch_xwin;
  143.     _move_term = move_xwin;
  144.     _clreol_term = clreol_xwin;
  145.     _clreos_term = clreos_xwin;
  146.     _scroll_term = scroll_xwin;
  147.     _rev_scroll_term = rev_scroll_xwin;
  148.     _flush_term = flush_xwin;
  149.     init_xwin();
  150.     term_type = 2;    /* using xwin */
  151.     }
  152. #endif
  153.  
  154.     /* if no window system found, default to curses */
  155.      
  156.     if(term_type == 0)
  157.     {
  158.     _open_term = open_curses;
  159.     _close_term = close_curses;
  160.     _addch_term = addch_curses;
  161.     _move_term = move_curses;
  162.     _clreol_term = clreol_curses;
  163.     _clreos_term = clreos_curses;
  164.     _scroll_term = scroll_curses;
  165.     _rev_scroll_term = NULL;
  166.     _flush_term = flush_curses;
  167.     init_curses();
  168.     term_type = 1;    /* using curses */
  169.     }
  170.  
  171.     /* set me up a terminal */
  172.  
  173.     sprintf(tmpstr, "YTalk version %d.%d", VMAJOR, VMINOR);
  174.     if(open_term(me, tmpstr) < 0)
  175.     {
  176.     end_term();
  177.     show_error("init_term: open_term() failed");
  178.     bail(0);
  179.     }
  180. }
  181.  
  182. /* Set terminal size.
  183.  */
  184. void
  185. set_terminal_size(fd, rows, cols)
  186.   int fd, rows, cols;
  187. {
  188. #ifdef TIOCSWINSZ
  189.     struct winsize winsize;
  190.  
  191.     winsize.ws_row = rows;
  192.     winsize.ws_col = cols;
  193.     ioctl(fd, TIOCSWINSZ, &winsize);
  194. #endif
  195. }
  196.  
  197. /* Set terminal and input characteristics for slave terminals.
  198.  */
  199. void
  200. set_terminal_flags(fd)
  201.   int fd;
  202. {
  203. #ifdef USE_SGTTY
  204.     (void)ioctl(fd, TIOCSETD, &line_discipline);
  205.     (void)ioctl(fd, TIOCLSET, &local_mode);
  206.     (void)ioctl(fd, TIOCSETP, &sgttyb);
  207.     (void)ioctl(fd, TIOCSETC, &tchars);
  208.     (void)ioctl(fd, TIOCSLTC, <chars);
  209. #else
  210.     if(tcsetattr(fd, TCSANOW, &tio) < 0)
  211.     show_error("tcsetattr failed");
  212. #endif
  213. }
  214.  
  215. int
  216. what_term()
  217. {
  218.     return term_type;
  219. }
  220.  
  221. /* Abort all terminal processing.
  222.  */
  223. void
  224. end_term()
  225. {
  226.     switch(term_type)
  227.     {
  228.     case 1:        /* curses */
  229.         end_curses();
  230.         break;
  231. #ifdef USE_X11
  232.     case 2:        /* xwin */
  233.         end_xwin();
  234.         break;
  235. #endif
  236.     }
  237.     term_type = 0;
  238. }
  239.  
  240. /* Open a new user window.
  241.  */
  242. int
  243. open_term(user, title)
  244.   register yuser *user;
  245.   register char *title;
  246. {
  247.     if(_open_term(user, title) != 0)
  248.     return -1;
  249.     user->x = user->y = 0;
  250.     if(user->scr == NULL)
  251.     resize_win(user, 24, 80);
  252.     return 0;
  253. }
  254.  
  255. /* Close a user window.
  256.  */
  257. void
  258. close_term(user)
  259.   register yuser *user;
  260. {
  261.     register int i;
  262.  
  263.     if(user->scr)
  264.     {
  265.     _close_term(user);
  266.     for(i = 0; i < user->t_rows; i++)
  267.         free(user->scr[i]);
  268.     free(user->scr);
  269.     user->scr = NULL;
  270.     user->t_rows = user->rows = 0;
  271.     user->t_cols = user->cols = 0;
  272.     }
  273. }
  274.  
  275. /* Place a character.
  276.  */
  277. void
  278. addch_term(user, c)
  279.   register yuser *user;
  280.   register ychar c;
  281. {
  282.     if(c >= ' ' && c <= '~')
  283.     {
  284.     _addch_term(user, c);
  285.     user->scr[user->y][user->x] = c;
  286.     if(++(user->x) >= user->cols)
  287.     {
  288.         user->bump = 1;
  289.         user->x = user->cols - 1;
  290.         if(user->cols < user->t_cols)
  291.         _move_term(user, user->y, user->x);
  292.     }
  293.     }
  294. }
  295.  
  296. /* Move the cursor.
  297.  */
  298. void
  299. move_term(user, y, x)
  300.   register yuser *user;
  301.   register int y, x;
  302. {
  303.     if(y < 0 || y >= user->rows)
  304.     y = user->rows - 1;
  305.     if(x < 0 || x >= user->cols)
  306.     {
  307.     user->bump = 1;
  308.     x = user->cols - 1;
  309.     }
  310.     else
  311.     user->bump = 0;
  312.     _move_term(user, y, x);
  313.     user->y = y;
  314.     user->x = x;
  315. }
  316.  
  317. /* Clear to EOL.
  318.  */
  319. void
  320. clreol_term(user)
  321.   register yuser *user;
  322. {
  323.     register int j;
  324.     register ychar *c;
  325.  
  326.     if(user->cols < user->t_cols)
  327.     {
  328.     c = user->scr[user->y] + user->x;
  329.     for(j = user->x; j < user->cols; j++)
  330.     {
  331.         *(c++) = ' ';
  332.         _addch_term(user, ' ');
  333.     }
  334.     move_term(user, user->y, user->x);
  335.     }
  336.     else
  337.     {
  338.     _clreol_term(user);
  339.     c = user->scr[user->y] + user->x;
  340.     for(j = user->x; j < user->cols; j++)
  341.         *(c++) = ' ';
  342.     }
  343. }
  344.  
  345. /* Clear to EOS.
  346.  */
  347. void
  348. clreos_term(user)
  349.   register yuser *user;
  350. {
  351.     register int j, i;
  352.     register ychar *c;
  353.     int x, y;
  354.  
  355.     if(user->cols < user->t_cols || user->rows < user->t_rows)
  356.     {
  357.     x = user->x;
  358.     y = user->y;
  359.     clreol_term(user);
  360.     for(i = user->y + 1; i < user->rows; i++)
  361.     {
  362.         move_term(user, i, 0);
  363.         clreol_term(user);
  364.     }
  365.     move_term(user, y, x);
  366.     }
  367.     else
  368.     {
  369.     _clreos_term(user);
  370.     j = user->x;
  371.     for(i = user->y; i < user->rows; i++)
  372.     {
  373.         c = user->scr[i] + j;
  374.         for(; j < user->cols; j++)
  375.         *(c++) = ' ';
  376.         j = 0;
  377.     }
  378.     }
  379. }
  380.  
  381. /* Scroll window.
  382.  */
  383. void
  384. scroll_term(user)
  385.   register yuser *user;
  386. {
  387.     register int i;
  388.     register ychar *c;
  389.  
  390.     if(user->sc_bot > user->sc_top)
  391.     {
  392.     c = user->scr[user->sc_top];
  393.     for(i = user->sc_top; i < user->sc_bot; i++)
  394.         user->scr[i] = user->scr[i+1];
  395.     user->scr[user->sc_bot] = c;
  396.     for(i = 0; i < user->cols; i++)
  397.         *(c++) = ' ';
  398.     if(_scroll_term
  399.     && user->rows == user->t_rows
  400.     && user->cols == user->t_cols
  401.     && user->sc_top == 0
  402.     && user->sc_bot == user->rows - 1)
  403.         _scroll_term(user);
  404.     else
  405.         redraw_term(user, 0);
  406.     }
  407.     else
  408.     {
  409.     move_term(user, user->sc_top, 0);
  410.     clreol_term(user);
  411.     }
  412. }
  413.  
  414. /* Reverse-scroll window.
  415.  */
  416. void
  417. rev_scroll_term(user)
  418.   register yuser *user;
  419. {
  420.     register int i;
  421.     register ychar *c;
  422.  
  423.     if(user->sc_bot > user->sc_top)
  424.     {
  425.     c = user->scr[user->sc_bot];
  426.     for(i = user->sc_bot; i > user->sc_top; i--)
  427.         user->scr[i] = user->scr[i-1];
  428.     user->scr[user->sc_top] = c;
  429.     for(i = 0; i < user->cols; i++)
  430.         *(c++) = ' ';
  431.     if(_rev_scroll_term
  432.     && user->rows == user->t_rows
  433.     && user->cols == user->t_cols
  434.     && user->sc_top == 0
  435.     && user->sc_bot == user->rows - 1)
  436.         _rev_scroll_term(user);
  437.     else
  438.         redraw_term(user, 0);
  439.     }
  440.     else
  441.     {
  442.     move_term(user, user->sc_top, 0);
  443.     clreol_term(user);
  444.     }
  445. }
  446.  
  447. /* Flush window output.
  448.  */
  449. void
  450. flush_term(user)
  451.   register yuser *user;
  452. {
  453.     _flush_term(user);
  454. }
  455.  
  456. /* Rub one character.
  457.  */
  458. void
  459. rub_term(user)
  460.   register yuser *user;
  461. {
  462.     if(user->x > 0)
  463.     {
  464.     if(user->bump)
  465.     {
  466.         addch_term(user, ' ');
  467.         user->bump = 0;
  468.     }
  469.     else
  470.     {
  471.         move_term(user, user->y, user->x - 1);
  472.         addch_term(user, ' ');
  473.         move_term(user, user->y, user->x - 1);
  474.     }
  475.  
  476.     }
  477. }
  478.  
  479. /* Rub one word.
  480.  */
  481. int
  482. word_term(user)
  483.   register yuser *user;
  484. {
  485.     register int x, out;
  486.  
  487.     for(x = user->x - 1; x >= 0 && user->scr[user->y][x] == ' '; x--)
  488.     continue;
  489.     for(; x >= 0 && user->scr[user->y][x] != ' '; x--)
  490.     continue;
  491.     out = user->x - (++x);
  492.     if(out <= 0)
  493.     return 0;
  494.     move_term(user, user->y, x);
  495.     clreol_term(user);
  496.     return out;
  497. }
  498.  
  499. /* Kill current line.
  500.  */
  501. void
  502. kill_term(user)
  503.   register yuser *user;
  504. {
  505.     if(user->x > 0)
  506.     {
  507.     move_term(user, user->y, 0);
  508.     clreol_term(user);
  509.     }
  510. }
  511.  
  512. /* Expand a tab.  We use non-destructive tabs.
  513.  */
  514. void
  515. tab_term(user)
  516.   register yuser *user;
  517. {
  518.     move_term(user, user->y, (user->x + 8) & 0xfff8);
  519. }
  520.  
  521. /* Process a newline.
  522.  */
  523. void
  524. newline_term(user)
  525.   register yuser *user;
  526. {
  527.     register int new_y, next_y;
  528.  
  529.     new_y = user->y + 1;
  530.     if(user->flags & FL_RAW)
  531.     {
  532.     if(new_y >= user->rows)
  533.     {
  534.         if(user->flags & FL_SCROLL)
  535.         scroll_term(user);
  536.     }
  537.     else
  538.         move_term(user, new_y, user->x);
  539.     }
  540.     else
  541.     {
  542.     if(new_y >= user->rows)
  543.     {
  544.         if(user->flags & FL_SCROLL)
  545.         {
  546.         scroll_term(user);
  547.         move_term(user, user->y, 0);
  548.         return;
  549.         }
  550.         new_y = 0;
  551.     }
  552.     next_y = new_y + 1;
  553.     if(next_y >= user->rows)
  554.         next_y = 0;
  555.     if(next_y > 0 || !(user->flags & FL_SCROLL))
  556.     {
  557.         move_term(user, next_y, 0);
  558.         clreol_term(user);
  559.     }
  560.     move_term(user, new_y, 0);
  561.     clreol_term(user);
  562.     }
  563. }
  564.  
  565. /* Insert lines.
  566.  */
  567. void
  568. add_line_term(user, num)
  569.   register yuser *user;
  570.   int num;
  571. {
  572.     register ychar *c;
  573.     register int i;
  574.  
  575.     if(num == 1 && user->y == 0)
  576.     rev_scroll_term(user);
  577.     else
  578.     {
  579.     /* find number of remaining lines */
  580.  
  581.     i = user->rows - user->y - num;
  582.     if(i <= 0)
  583.     {
  584.         i = user->x;
  585.         move_term(user, user->y, 0);
  586.         clreos_term(user);
  587.         move_term(user, user->y, i);
  588.         return;
  589.     }
  590.  
  591.     /* swap the remaining lines to bottom */
  592.  
  593.     for(i--; i >= 0; i--)
  594.     {
  595.         c = user->scr[user->y + i];
  596.         user->scr[user->y + i] = user->scr[user->y + i + num];
  597.         user->scr[user->y + i + num] = c;
  598.     }
  599.  
  600.     /* clear the added lines */
  601.  
  602.     for(num--; num >= 0; num--)
  603.     {
  604.         c = user->scr[user->y + num];
  605.         for(i = 0; i < user->cols; i++)
  606.         *(c++) = ' ';
  607.     }
  608.     redraw_term(user, user->y);
  609.     }
  610. }
  611.  
  612. /* Delete lines.
  613.  */
  614. void
  615. del_line_term(user, num)
  616.   register yuser *user;
  617.   int num;
  618. {
  619.     register ychar *c;
  620.     register int i;
  621.  
  622.     if(num == 1 && user->y == 0)
  623.     scroll_term(user);
  624.     else
  625.     {
  626.     /* find number of remaining lines */
  627.  
  628.     i = user->rows - user->y - num;
  629.     if(i <= 0)
  630.     {
  631.         i = user->x;
  632.         move_term(user, user->y, 0);
  633.         clreos_term(user);
  634.         move_term(user, user->y, i);
  635.         return;
  636.     }
  637.  
  638.     /* swap the remaining lines to top */
  639.  
  640.     for(; i > 0; i--)
  641.     {
  642.         c = user->scr[user->rows - i];
  643.         user->scr[user->rows - i] = user->scr[user->rows - i - num];
  644.         user->scr[user->rows - i - num] = c;
  645.     }
  646.  
  647.     /* clear the remaining bottom lines */
  648.  
  649.     for(; num > 0; num--)
  650.     {
  651.         c = user->scr[user->rows - num];
  652.         for(i = 0; i < user->cols; i++)
  653.         *(c++) = ' ';
  654.     }
  655.     redraw_term(user, user->y);
  656.     }
  657. }
  658.  
  659. static void
  660. copy_text(fr, to, count)
  661.   register ychar *fr, *to;
  662.   register int count;
  663. {
  664.     if(to < fr)
  665.     {
  666.     for(; count > 0; count--)
  667.         *(to++) = *(fr++);
  668.     }
  669.     else
  670.     {
  671.     fr += count;
  672.     to += count;
  673.     for(; count > 0; count--)
  674.         *(--to) = *(--fr);
  675.     }
  676. }
  677.  
  678. /* Add chars.
  679.  */
  680. void
  681. add_char_term(user, num)
  682.   register yuser *user;
  683.   int num;
  684. {
  685.     register ychar *c;
  686.     register int i;
  687.  
  688.     /* find number of remaining non-blank chars */
  689.  
  690.     i = user->cols - user->x - num;
  691.     c = user->scr[user->y] + user->cols - num - 1;
  692.     while(i > 0 && *c == ' ')
  693.     c--, i--;
  694.     if(i <= 0)
  695.     {
  696.     clreol_term(user);
  697.     return;
  698.     }
  699.  
  700.     /* transfer the chars and clear the remaining */
  701.  
  702.     c++;
  703.     copy_text(c - i, c - i + num, i);
  704.     for(c -= i; num > 0; num--)
  705.     {
  706.     *(c++) = ' ';
  707.     _addch_term(user, ' ');
  708.     }
  709.     for(; i > 0; i--)
  710.     _addch_term(user, *(c++));
  711.     _move_term(user, user->y, user->x);
  712. }
  713.  
  714. /* Delete chars.
  715.  */
  716. void
  717. del_char_term(user, num)
  718.   register yuser *user;
  719.   int num;
  720. {
  721.     register ychar *c;
  722.     register int i;
  723.  
  724.     /* find number of remaining non-blank chars */
  725.  
  726.     i = user->cols - user->x - num;
  727.     c = user->scr[user->y] + user->cols - 1;
  728.     while(i > 0 && *c == ' ')
  729.     c--, i--;
  730.     if(i <= 0)
  731.     {
  732.     clreol_term(user);
  733.     return;
  734.     }
  735.  
  736.     /* transfer the chars and clear the remaining */
  737.  
  738.     c++;
  739.     copy_text(c - i, c - i - num, i);
  740.     for(c -= (i + num); i > 0; i--)
  741.     _addch_term(user, *(c++));
  742.     for(; num > 0; num--)
  743.     {
  744.     *(c++) = ' ';
  745.     _addch_term(user, ' ');
  746.     }
  747.     _move_term(user, user->y, user->x);
  748. }
  749.  
  750. /* Redraw a user's window.
  751.  */
  752. void
  753. redraw_term(user, y)
  754.   register yuser *user;
  755.   register int y;
  756. {
  757.     register int x, spaces;
  758.     register ychar *c;
  759.  
  760.     for(; y < user->t_rows; y++)
  761.     {
  762.     _move_term(user, y, 0);
  763.     _clreol_term(user);
  764.     spaces = 0;
  765.     c = user->scr[y];
  766.     for(x = 0; x < user->t_cols; x++, c++)
  767.     {
  768.         if(*c == ' ')
  769.         spaces++;
  770.         else
  771.         {
  772.         if(spaces)
  773.         {
  774.             if(spaces <= 3)    /* arbitrary */
  775.             {
  776.             for(; spaces > 0; spaces--)
  777.                 _addch_term(user, ' ');
  778.             }
  779.             else
  780.             {
  781.             _move_term(user, y, x);
  782.             spaces = 0;
  783.             }
  784.         }
  785.         _addch_term(user, *c);
  786.         }
  787.     }
  788.     }
  789.  
  790.     /* redisplay any active menu */
  791.  
  792.     if(menu_ptr != NULL)
  793.     update_menu();
  794.     else
  795.     _move_term(user, user->y, user->x);
  796. }
  797.  
  798. /* Return the first interesting row for a user with a window of
  799.  * the given height and width.
  800.  */
  801. static int
  802. first_interesting_row(user, height, width)
  803.   yuser *user;
  804.   int height, width;
  805. {
  806.     register int j, i;
  807.     register ychar *c;
  808.  
  809.     if(height < user->t_rows)
  810.     {
  811.     j = (user->y + 1) - height;
  812.     if(j < 0)
  813.         j += user->t_rows;
  814.     }
  815.     else
  816.     {
  817.     j = user->y + 1;
  818.     if(j >= user->t_rows)
  819.         j = 0;
  820.     }
  821.     while(j != user->y)
  822.     {
  823.     i = (width > user->t_cols) ? user->t_cols : width;
  824.     for(c = user->scr[j]; i > 0; i--, c++)
  825.         if(*c != ' ')
  826.         break;
  827.     if(i > 0)
  828.         break;
  829.     if(++j >= user->t_rows)
  830.         j = 0;
  831.     }
  832.     return j;
  833. }
  834.  
  835. /* Called when a user's window has been resized.
  836.  */
  837. void
  838. resize_win(user, height, width)
  839.   yuser *user;
  840.   int height, width;
  841. {
  842.     register int j, i;
  843.     register ychar *c, **newscr;
  844.     int new_y, y_pos;
  845.  
  846.     if(height == user->t_rows && width == user->t_cols)
  847.     return;
  848.  
  849.     /* resize the user terminal buffer */
  850.  
  851.     new_y = -1;
  852.     y_pos = 0;
  853.     newscr = (ychar **)get_mem(height * sizeof(ychar *));
  854.     if(user->scr == NULL)
  855.     {
  856.     user->t_rows = user->rows = 0;
  857.     user->t_cols = user->cols = 0;
  858.     }
  859.     else if(user->region_set)
  860.     {
  861.     /* save as many top lines as possible */
  862.  
  863.     for(j = 0; j < height && j < user->t_rows; j++)
  864.         newscr[j] = user->scr[j];
  865.     new_y = j - 1;
  866.     y_pos = user->y;
  867.     for(; j < user->t_rows; j++)
  868.         free(user->scr[j]);
  869.     free(user->scr);
  870.     }
  871.     else
  872.     {
  873.     /* shift all recent lines to top of screen */
  874.  
  875.     j = first_interesting_row(user, height, width);
  876.     for(i = 0; i < height; i++)
  877.     {
  878.         newscr[++new_y] = user->scr[j];
  879.         if(j == user->y)
  880.         break;
  881.         if(++j >= user->t_rows)
  882.         j = 0;
  883.     }
  884.     for(i++; i < user->t_rows; i++)
  885.     {
  886.         if(++j >= user->t_rows)
  887.         j = 0;
  888.         free(user->scr[j]);
  889.     }
  890.     y_pos = new_y;
  891.     free(user->scr);
  892.     }
  893.     user->scr = newscr;
  894.  
  895.     /* fill in the missing portions */
  896.  
  897.     if(width > user->t_cols)
  898.     for(i = 0; i <= new_y; i++)
  899.     {
  900.         user->scr[i] = (ychar *)realloc_mem(user->scr[i], width);
  901.         for(j = user->t_cols; j < width; j++)
  902.         user->scr[i][j] = ' ';
  903.     }
  904.     for(i = new_y + 1; i < height; i++)
  905.     {
  906.     c = user->scr[i] = (ychar *)get_mem(width);
  907.     for(j = 0; j < width; j++)
  908.         *(c++) = ' ';
  909.     }
  910.  
  911.     /* reset window values */
  912.  
  913.     user->t_rows = user->rows = height;
  914.     user->t_cols = user->cols = width;
  915.     user->sc_top = 0;
  916.     user->sc_bot = height - 1;
  917.     move_term(user, y_pos, user->x);
  918.     send_winch(user);
  919.     redraw_term(user, 0);
  920.     flush_term(user);
  921. }
  922.  
  923. /* Draw a nice box.
  924.  */
  925. static void
  926. draw_box(user, height, width, c)
  927.   yuser *user;
  928.   int height, width;
  929.   char c;
  930. {
  931.     register int i;
  932.  
  933.     if(width < user->t_cols)
  934.     {
  935.     for(i = 0; i < height; i++)
  936.     {
  937.         move_term(user, i, width);
  938.         addch_term(user, c);
  939.         if(width + 1 < user->t_cols)
  940.         clreol_term(user);
  941.     }
  942.     }
  943.     if(height < user->t_rows)
  944.     {
  945.     move_term(user, height, 0);
  946.     for(i = 0; i < width; i++)
  947.         addch_term(user, c);
  948.     if(width < user->t_cols)
  949.         addch_term(user, c);
  950.     if(width + 1 < user->t_cols)
  951.         clreol_term(user);
  952.     if(height + 1 < user->t_rows)
  953.     {
  954.         move_term(user, height + 1, 0);
  955.         clreos_term(user);
  956.     }
  957.     }
  958. }
  959.  
  960. /* Set the virtual terminal size, ie: the display region.
  961.  */
  962. void
  963. set_win_region(user, height, width)
  964.   yuser *user;
  965.   int height, width;
  966. {
  967.     register int x, y;
  968.     int old_height, old_width;
  969.  
  970.     if(height < 2 || height > user->t_rows)
  971.     height = user->t_rows;
  972.     if(width < 2 || width > user->t_cols)
  973.     width = user->t_cols;
  974.  
  975.     /* Don't check if they're already equal; always perform processing.
  976.      * Just because it's equal over here doesn't mean it's equal for all
  977.      * ytalk connections.  We still need to clear the screen.
  978.      */
  979.  
  980.     old_height = user->rows;
  981.     old_width = user->cols;
  982.     user->rows = user->t_rows;
  983.     user->cols = user->t_cols;
  984.     if(user->region_set)
  985.     {
  986.     x = user->x;
  987.     y = user->y;
  988.     if(width > old_width || height > old_height)
  989.         draw_box(user, old_height, old_width, ' ');
  990.     }
  991.     else
  992.     {
  993.     x = y = 0;
  994.     move_term(user, 0, 0);
  995.     clreos_term(user);
  996.     user->region_set = 1;
  997.     }
  998.     draw_box(user, height, width, '%');
  999.  
  1000.     /* set the display region */
  1001.  
  1002.     user->rows = height;
  1003.     user->cols = width;
  1004.     user->sc_top = 0;
  1005.     user->sc_bot = height - 1;
  1006.     move_term(user, y, x);
  1007.     flush_term(user);
  1008.     
  1009.     if(user == me)
  1010.     send_region();
  1011. }
  1012.  
  1013. /* Set the virtual terminal size, ie: the display region.
  1014.  */
  1015. void
  1016. end_win_region(user)
  1017.   yuser *user;
  1018. {
  1019.     int old_height, old_width;
  1020.  
  1021.     old_height = user->rows;
  1022.     old_width = user->cols;
  1023.     user->rows = user->t_rows;
  1024.     user->cols = user->t_cols;
  1025.     if(old_height < user->t_rows || old_width < user->t_cols)
  1026.     draw_box(user, old_height, old_width, ' ');
  1027.     user->region_set = 0;
  1028.     if(user == me)
  1029.     send_end_region();
  1030. }
  1031.  
  1032. /* Set the scrolling region.
  1033.  */
  1034. void
  1035. set_scroll_region(user, top, bottom)
  1036.   yuser *user;
  1037.   int top, bottom;
  1038. {
  1039.     if(top < 0 || top >= user->rows)
  1040.     return;
  1041.     if(bottom < top || bottom >= user->rows)
  1042.     return;
  1043.     user->sc_top = top;
  1044.     user->sc_bot = bottom;
  1045. }
  1046.  
  1047. /* Send a message to the terminal.
  1048.  */
  1049. void
  1050. msg_term(user, str)
  1051.   yuser *user;
  1052.   char *str;
  1053. {
  1054.     int y;
  1055.  
  1056.     if((y = user->y + 1) >= user->rows)
  1057.     y = 0;
  1058.     _move_term(user, y, 0);
  1059.     _addch_term(user, '[');
  1060.     while(*str)
  1061.     _addch_term(user, *(str++));
  1062.     _addch_term(user, ']');
  1063.     _clreol_term(user);
  1064.     _move_term(user, user->y, user->x);
  1065.     _flush_term(user);
  1066. }
  1067.  
  1068. /* Spew terminal contents to a file descriptor.
  1069.  */
  1070. void
  1071. spew_term(user, fd, rows, cols)
  1072.   yuser *user;
  1073.   int fd, rows, cols;
  1074. {
  1075.     register ychar *c, *e;
  1076.     register int len;
  1077.     int y;
  1078.     static char tmp[20];
  1079.  
  1080.     if(user->region_set)
  1081.     {
  1082.     y = 0;
  1083.     if(cols > user->cols)
  1084.         cols = user->cols;
  1085.     if(rows > user->rows)
  1086.         rows = user->rows;
  1087.     for(;;)
  1088.     {
  1089.         for(c = e = user->scr[y], len = cols; len > 0; len--, c++)
  1090.         if(*c != ' ')
  1091.             e = c + 1;
  1092.         if(e != user->scr[y])
  1093.         (void)write(fd, user->scr[y], e - user->scr[y]);
  1094.         if(++y >= rows)
  1095.         break;
  1096.         (void)write(fd, "\n", 1);
  1097.     }
  1098.  
  1099.     /* move the cursor to the correct place */
  1100.  
  1101.     sprintf(tmp, "%c[%d;%dH", 27, user->y + 1, user->x + 1);
  1102.     (void)write(fd, tmp, strlen(tmp));
  1103.     }
  1104.     else
  1105.     {
  1106.     y = first_interesting_row(user, rows, cols);
  1107.     for(;;)
  1108.     {
  1109.         if(y == user->y)
  1110.         {
  1111.         if(user->x > 0)
  1112.             (void)write(fd, user->scr[y], user->x);
  1113.         break;
  1114.         }
  1115.         for(c = e = user->scr[y], len = user->t_cols; len > 0; len--, c++)
  1116.         if(*c != ' ')
  1117.             e = c + 1;
  1118.         if(e != user->scr[y])
  1119.         (void)write(fd, user->scr[y], e - user->scr[y]);
  1120.         (void)write(fd, "\n", 1);
  1121.         if(++y >= user->t_rows)
  1122.         y = 0;
  1123.     }
  1124.     }
  1125. }
  1126.  
  1127. /* Draw some raw characters to the screen without updating any buffers.
  1128.  * Whoever uses this should know what they're doing.  It should always
  1129.  * be followed by a redraw_term() before calling any of the normal
  1130.  * term functions again.
  1131.  *
  1132.  * If the given string is not as long as the given length, then the
  1133.  * string is repeated to fill the given length.
  1134.  *
  1135.  * This is an unadvertised function.
  1136.  */
  1137. void
  1138. raw_term(user, y, x, str, len)
  1139.   yuser *user;
  1140.   int y, x;
  1141.   ychar *str;
  1142.   int len;
  1143. {
  1144.     register ychar *c;
  1145.  
  1146.     if(y < 0 || y >= user->t_rows)
  1147.     return;
  1148.     if(x < 0 || x >= user->t_cols)
  1149.     return;
  1150.     _move_term(user, y, x);
  1151.  
  1152.     for(c = str; len > 0; len--, c++)
  1153.     {
  1154.     if(*c == '\0')
  1155.         c = str;
  1156.     if(*c < ' ' || *c > '~')
  1157.         return;
  1158.     _addch_term(user, *c);
  1159.     }
  1160. }
  1161.  
  1162. int
  1163. center(width, n)
  1164.   int width, n;
  1165. {
  1166.     if(n >= width)
  1167.     return 0;
  1168.     return (width - n) >> 1;
  1169. }
  1170.