home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / N / TCPIP / NETKIT-A.06 / NETKIT-A / NetKit-A-0.06 / ytalk-3.0.1 / term.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-27  |  22.2 KB  |  1,240 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 "cwin.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 (%d)", VMAJOR, VMINOR, VPATCH);
  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.     int sy, sx;
  390.  
  391.     if(user->sc_bot > user->sc_top)
  392.     {
  393.     c = user->scr[user->sc_top];
  394.     for(i = user->sc_top; i < user->sc_bot; i++)
  395.         user->scr[i] = user->scr[i+1];
  396.     user->scr[user->sc_bot] = c;
  397.     for(i = 0; i < user->cols; i++)
  398.         *(c++) = ' ';
  399.     if(_scroll_term
  400.     && user->rows == user->t_rows
  401.     && user->cols == user->t_cols
  402.     && user->sc_top == 0
  403.     && user->sc_bot == user->rows - 1)
  404.         _scroll_term(user);
  405.     else
  406.         redraw_term(user, 0);
  407.     }
  408.     else
  409.     {
  410.     sy = user->y;
  411.     sx = user->x;
  412.     move_term(user, user->sc_top, 0);
  413.     clreol_term(user);
  414.     move_term(user, sy, sx);
  415.     }
  416. }
  417.  
  418. /* Reverse-scroll window.
  419.  */
  420. void
  421. rev_scroll_term(user)
  422.   register yuser *user;
  423. {
  424.     register int i;
  425.     register ychar *c;
  426.     int sy, sx;
  427.  
  428.     if(user->sc_bot > user->sc_top)
  429.     {
  430.     c = user->scr[user->sc_bot];
  431.     for(i = user->sc_bot; i > user->sc_top; i--)
  432.         user->scr[i] = user->scr[i-1];
  433.     user->scr[user->sc_top] = c;
  434.     for(i = 0; i < user->cols; i++)
  435.         *(c++) = ' ';
  436.     if(_rev_scroll_term
  437.     && user->rows == user->t_rows
  438.     && user->cols == user->t_cols
  439.     && user->sc_top == 0
  440.     && user->sc_bot == user->rows - 1)
  441.         _rev_scroll_term(user);
  442.     else
  443.         redraw_term(user, 0);
  444.     }
  445.     else
  446.     {
  447.     sy = user->y;
  448.     sx = user->x;
  449.     move_term(user, user->sc_top, 0);
  450.     clreol_term(user);
  451.     move_term(user, sy, sx);
  452.     }
  453. }
  454.  
  455. /* Flush window output.
  456.  */
  457. void
  458. flush_term(user)
  459.   register yuser *user;
  460. {
  461.     _flush_term(user);
  462. }
  463.  
  464. /* Rub one character.
  465.  */
  466. void
  467. rub_term(user)
  468.   register yuser *user;
  469. {
  470.     if(user->x > 0)
  471.     {
  472.     if(user->bump)
  473.     {
  474.         addch_term(user, ' ');
  475.         user->bump = 0;
  476.     }
  477.     else
  478.     {
  479.         move_term(user, user->y, user->x - 1);
  480.         addch_term(user, ' ');
  481.         move_term(user, user->y, user->x - 1);
  482.     }
  483.  
  484.     }
  485. }
  486.  
  487. /* Rub one word.
  488.  */
  489. int
  490. word_term(user)
  491.   register yuser *user;
  492. {
  493.     register int x, out;
  494.  
  495.     for(x = user->x - 1; x >= 0 && user->scr[user->y][x] == ' '; x--)
  496.     continue;
  497.     for(; x >= 0 && user->scr[user->y][x] != ' '; x--)
  498.     continue;
  499.     out = user->x - (++x);
  500.     if(out <= 0)
  501.     return 0;
  502.     move_term(user, user->y, x);
  503.     clreol_term(user);
  504.     return out;
  505. }
  506.  
  507. /* Kill current line.
  508.  */
  509. void
  510. kill_term(user)
  511.   register yuser *user;
  512. {
  513.     if(user->x > 0)
  514.     {
  515.     move_term(user, user->y, 0);
  516.     clreol_term(user);
  517.     }
  518. }
  519.  
  520. /* Expand a tab.  We use non-destructive tabs.
  521.  */
  522. void
  523. tab_term(user)
  524.   register yuser *user;
  525. {
  526.     move_term(user, user->y, (user->x + 8) & 0xfff8);
  527. }
  528.  
  529. /* Process a newline.
  530.  */
  531. void
  532. newline_term(user)
  533.   register yuser *user;
  534. {
  535.     register int new_y, next_y;
  536.  
  537.     new_y = user->y + 1;
  538.     if(user->flags & FL_RAW)
  539.     {
  540.     if(new_y >= user->rows)
  541.     {
  542.         if(user->flags & FL_SCROLL)
  543.         scroll_term(user);
  544.     }
  545.     else
  546.         move_term(user, new_y, user->x);
  547.     }
  548.     else
  549.     {
  550.     if(new_y >= user->rows)
  551.     {
  552.         if(user->flags & FL_SCROLL)
  553.         {
  554.         scroll_term(user);
  555.         move_term(user, user->y, 0);
  556.         return;
  557.         }
  558.         new_y = 0;
  559.     }
  560.     next_y = new_y + 1;
  561.     if(next_y >= user->rows)
  562.         next_y = 0;
  563.     if(next_y > 0 || !(user->flags & FL_SCROLL))
  564.     {
  565.         move_term(user, next_y, 0);
  566.         clreol_term(user);
  567.     }
  568.     move_term(user, new_y, 0);
  569.     clreol_term(user);
  570.     }
  571. }
  572.  
  573. /* Insert lines.
  574.  */
  575. void
  576. add_line_term(user, num)
  577.   register yuser *user;
  578.   int num;
  579. {
  580.     register ychar *c;
  581.     register int i;
  582.  
  583.     if(num == 1 && user->y == 0)
  584.     rev_scroll_term(user);
  585.     else
  586.     {
  587.     /* find number of remaining lines */
  588.  
  589.     i = user->rows - user->y - num;
  590.     if(i <= 0)
  591.     {
  592.         i = user->x;
  593.         move_term(user, user->y, 0);
  594.         clreos_term(user);
  595.         move_term(user, user->y, i);
  596.         return;
  597.     }
  598.  
  599.     /* swap the remaining lines to bottom */
  600.  
  601.     for(i--; i >= 0; i--)
  602.     {
  603.         c = user->scr[user->y + i];
  604.         user->scr[user->y + i] = user->scr[user->y + i + num];
  605.         user->scr[user->y + i + num] = c;
  606.     }
  607.  
  608.     /* clear the added lines */
  609.  
  610.     for(num--; num >= 0; num--)
  611.     {
  612.         c = user->scr[user->y + num];
  613.         for(i = 0; i < user->cols; i++)
  614.         *(c++) = ' ';
  615.     }
  616.     redraw_term(user, user->y);
  617.     }
  618. }
  619.  
  620. /* Delete lines.
  621.  */
  622. void
  623. del_line_term(user, num)
  624.   register yuser *user;
  625.   int num;
  626. {
  627.     register ychar *c;
  628.     register int i;
  629.  
  630.     if(num == 1 && user->y == 0)
  631.     scroll_term(user);
  632.     else
  633.     {
  634.     /* find number of remaining lines */
  635.  
  636.     i = user->rows - user->y - num;
  637.     if(i <= 0)
  638.     {
  639.         i = user->x;
  640.         move_term(user, user->y, 0);
  641.         clreos_term(user);
  642.         move_term(user, user->y, i);
  643.         return;
  644.     }
  645.  
  646.     /* swap the remaining lines to top */
  647.  
  648.     for(; i > 0; i--)
  649.     {
  650.         c = user->scr[user->rows - i];
  651.         user->scr[user->rows - i] = user->scr[user->rows - i - num];
  652.         user->scr[user->rows - i - num] = c;
  653.     }
  654.  
  655.     /* clear the remaining bottom lines */
  656.  
  657.     for(; num > 0; num--)
  658.     {
  659.         c = user->scr[user->rows - num];
  660.         for(i = 0; i < user->cols; i++)
  661.         *(c++) = ' ';
  662.     }
  663.     redraw_term(user, user->y);
  664.     }
  665. }
  666.  
  667. static void
  668. copy_text(fr, to, count)
  669.   register ychar *fr, *to;
  670.   register int count;
  671. {
  672.     if(to < fr)
  673.     {
  674.     for(; count > 0; count--)
  675.         *(to++) = *(fr++);
  676.     }
  677.     else
  678.     {
  679.     fr += count;
  680.     to += count;
  681.     for(; count > 0; count--)
  682.         *(--to) = *(--fr);
  683.     }
  684. }
  685.  
  686. /* Add chars.
  687.  */
  688. void
  689. add_char_term(user, num)
  690.   register yuser *user;
  691.   int num;
  692. {
  693.     register ychar *c;
  694.     register int i;
  695.  
  696.     /* find number of remaining non-blank chars */
  697.  
  698.     i = user->cols - user->x - num;
  699.     c = user->scr[user->y] + user->cols - num - 1;
  700.     while(i > 0 && *c == ' ')
  701.     c--, i--;
  702.     if(i <= 0)
  703.     {
  704.     clreol_term(user);
  705.     return;
  706.     }
  707.  
  708.     /* transfer the chars and clear the remaining */
  709.  
  710.     c++;
  711.     copy_text(c - i, c - i + num, i);
  712.     for(c -= i; num > 0; num--)
  713.     {
  714.     *(c++) = ' ';
  715.     _addch_term(user, ' ');
  716.     }
  717.     for(; i > 0; i--)
  718.     _addch_term(user, *(c++));
  719.     _move_term(user, user->y, user->x);
  720. }
  721.  
  722. /* Delete chars.
  723.  */
  724. void
  725. del_char_term(user, num)
  726.   register yuser *user;
  727.   int num;
  728. {
  729.     register ychar *c;
  730.     register int i;
  731.  
  732.     /* find number of remaining non-blank chars */
  733.  
  734.     i = user->cols - user->x - num;
  735.     c = user->scr[user->y] + user->cols - 1;
  736.     while(i > 0 && *c == ' ')
  737.     c--, i--;
  738.     if(i <= 0)
  739.     {
  740.     clreol_term(user);
  741.     return;
  742.     }
  743.  
  744.     /* transfer the chars and clear the remaining */
  745.  
  746.     c++;
  747.     copy_text(c - i, c - i - num, i);
  748.     for(c -= (i + num); i > 0; i--)
  749.     _addch_term(user, *(c++));
  750.     for(; num > 0; num--)
  751.     {
  752.     *(c++) = ' ';
  753.     _addch_term(user, ' ');
  754.     }
  755.     _move_term(user, user->y, user->x);
  756. }
  757.  
  758. /* Redraw a user's window.
  759.  */
  760. void
  761. redraw_term(user, y)
  762.   register yuser *user;
  763.   register int y;
  764. {
  765.     register int x, spaces;
  766.     register ychar *c;
  767.  
  768.     for(; y < user->t_rows; y++)
  769.     {
  770.     _move_term(user, y, 0);
  771.     _clreol_term(user);
  772.     spaces = 0;
  773.     c = user->scr[y];
  774.     for(x = 0; x < user->t_cols; x++, c++)
  775.     {
  776.         if(*c == ' ')
  777.         spaces++;
  778.         else
  779.         {
  780.         if(spaces)
  781.         {
  782.             if(spaces <= 3)    /* arbitrary */
  783.             {
  784.             for(; spaces > 0; spaces--)
  785.                 _addch_term(user, ' ');
  786.             }
  787.             else
  788.             {
  789.             _move_term(user, y, x);
  790.             spaces = 0;
  791.             }
  792.         }
  793.         _addch_term(user, *c);
  794.         }
  795.     }
  796.     }
  797.  
  798.     /* redisplay any active menu */
  799.  
  800.     if(menu_ptr != NULL)
  801.     update_menu();
  802.     else
  803.     _move_term(user, user->y, user->x);
  804. }
  805.  
  806. /* Return the first interesting row for a user with a window of
  807.  * the given height and width.
  808.  */
  809. static int
  810. first_interesting_row(user, height, width)
  811.   yuser *user;
  812.   int height, width;
  813. {
  814.     register int j, i;
  815.     register ychar *c;
  816.  
  817.     if(height < user->t_rows)
  818.     {
  819.     j = (user->y + 1) - height;
  820.     if(j < 0)
  821.         j += user->t_rows;
  822.     }
  823.     else
  824.     {
  825.     j = user->y + 1;
  826.     if(j >= user->t_rows)
  827.         j = 0;
  828.     }
  829.     while(j != user->y)
  830.     {
  831.     i = (width > user->t_cols) ? user->t_cols : width;
  832.     for(c = user->scr[j]; i > 0; i--, c++)
  833.         if(*c != ' ')
  834.         break;
  835.     if(i > 0)
  836.         break;
  837.     if(++j >= user->t_rows)
  838.         j = 0;
  839.     }
  840.     return j;
  841. }
  842.  
  843. /* Called when a user's window has been resized.
  844.  */
  845. void
  846. resize_win(user, height, width)
  847.   yuser *user;
  848.   int height, width;
  849. {
  850.     register int j, i;
  851.     register ychar *c, **newscr;
  852.     int new_y, y_pos;
  853.  
  854.     if(height == user->t_rows && width == user->t_cols)
  855.     return;
  856.  
  857.     /* resize the user terminal buffer */
  858.  
  859.     new_y = -1;
  860.     y_pos = 0;
  861.     newscr = (ychar **)get_mem(height * sizeof(ychar *));
  862.     if(user->scr == NULL)
  863.     {
  864.     user->t_rows = user->rows = 0;
  865.     user->t_cols = user->cols = 0;
  866.     }
  867.     else if(user->region_set)
  868.     {
  869.     /* save as many top lines as possible */
  870.  
  871.     for(j = 0; j < height && j < user->t_rows; j++)
  872.         newscr[j] = user->scr[j];
  873.     new_y = j - 1;
  874.     y_pos = user->y;
  875.     for(; j < user->t_rows; j++)
  876.         free(user->scr[j]);
  877.     free(user->scr);
  878.     }
  879.     else
  880.     {
  881.     /* shift all recent lines to top of screen */
  882.  
  883.     j = first_interesting_row(user, height, width);
  884.     for(i = 0; i < height; i++)
  885.     {
  886.         newscr[++new_y] = user->scr[j];
  887.         if(j == user->y)
  888.         break;
  889.         if(++j >= user->t_rows)
  890.         j = 0;
  891.     }
  892.     for(i++; i < user->t_rows; i++)
  893.     {
  894.         if(++j >= user->t_rows)
  895.         j = 0;
  896.         free(user->scr[j]);
  897.     }
  898.     y_pos = new_y;
  899.     free(user->scr);
  900.     }
  901.     user->scr = newscr;
  902.  
  903.     /* fill in the missing portions */
  904.  
  905.     if(width > user->t_cols)
  906.     for(i = 0; i <= new_y; i++)
  907.     {
  908.         user->scr[i] = (ychar *)realloc_mem(user->scr[i], width);
  909.         for(j = user->t_cols; j < width; j++)
  910.         user->scr[i][j] = ' ';
  911.     }
  912.     for(i = new_y + 1; i < height; i++)
  913.     {
  914.     c = user->scr[i] = (ychar *)get_mem(width);
  915.     for(j = 0; j < width; j++)
  916.         *(c++) = ' ';
  917.     }
  918.  
  919.     /* reset window values */
  920.  
  921.     user->t_rows = user->rows = height;
  922.     user->t_cols = user->cols = width;
  923.     user->sc_top = 0;
  924.     user->sc_bot = height - 1;
  925.     move_term(user, y_pos, user->x);
  926.     send_winch(user);
  927.     redraw_term(user, 0);
  928.     flush_term(user);
  929. }
  930.  
  931. /* Draw a nice box.
  932.  */
  933. static void
  934. draw_box(user, height, width, c)
  935.   yuser *user;
  936.   int height, width;
  937.   char c;
  938. {
  939.     register int i;
  940.  
  941.     if(width < user->t_cols)
  942.     {
  943.     for(i = 0; i < height; i++)
  944.     {
  945.         move_term(user, i, width);
  946.         addch_term(user, c);
  947.         if(width + 1 < user->t_cols)
  948.         clreol_term(user);
  949.     }
  950.     }
  951.     if(height < user->t_rows)
  952.     {
  953.     move_term(user, height, 0);
  954.     for(i = 0; i < width; i++)
  955.         addch_term(user, c);
  956.     if(width < user->t_cols)
  957.         addch_term(user, c);
  958.     if(width + 1 < user->t_cols)
  959.         clreol_term(user);
  960.     if(height + 1 < user->t_rows)
  961.     {
  962.         move_term(user, height + 1, 0);
  963.         clreos_term(user);
  964.     }
  965.     }
  966. }
  967.  
  968. /* Set the virtual terminal size, ie: the display region.
  969.  */
  970. void
  971. set_win_region(user, height, width)
  972.   yuser *user;
  973.   int height, width;
  974. {
  975.     register int x, y;
  976.     int old_height, old_width;
  977.  
  978.     if(height < 2 || height > user->t_rows)
  979.     height = user->t_rows;
  980.     if(width < 2 || width > user->t_cols)
  981.     width = user->t_cols;
  982.  
  983.     /* Don't check if they're already equal; always perform processing.
  984.      * Just because it's equal over here doesn't mean it's equal for all
  985.      * ytalk connections.  We still need to clear the screen.
  986.      */
  987.  
  988.     old_height = user->rows;
  989.     old_width = user->cols;
  990.     user->rows = user->t_rows;
  991.     user->cols = user->t_cols;
  992.     if(user->region_set)
  993.     {
  994.     x = user->x;
  995.     y = user->y;
  996.     if(width > old_width || height > old_height)
  997.         draw_box(user, old_height, old_width, ' ');
  998.     }
  999.     else
  1000.     {
  1001.     x = y = 0;
  1002.     move_term(user, 0, 0);
  1003.     clreos_term(user);
  1004.     user->region_set = 1;
  1005.     }
  1006.     draw_box(user, height, width, '%');
  1007.  
  1008.     /* set the display region */
  1009.  
  1010.     user->rows = height;
  1011.     user->cols = width;
  1012.     user->sc_top = 0;
  1013.     user->sc_bot = height - 1;
  1014.     move_term(user, y, x);
  1015.     flush_term(user);
  1016.     
  1017.     if(user == me)
  1018.     send_region();
  1019. }
  1020.  
  1021. /* Set the virtual terminal size, ie: the display region.
  1022.  */
  1023. void
  1024. end_win_region(user)
  1025.   yuser *user;
  1026. {
  1027.     int old_height, old_width;
  1028.  
  1029.     old_height = user->rows;
  1030.     old_width = user->cols;
  1031.     user->rows = user->t_rows;
  1032.     user->cols = user->t_cols;
  1033.     user->sc_top = 0;
  1034.     user->sc_bot = user->rows - 1;
  1035.     if(old_height < user->t_rows || old_width < user->t_cols)
  1036.     draw_box(user, old_height, old_width, ' ');
  1037.     user->region_set = 0;
  1038.     if(user == me)
  1039.     send_end_region();
  1040. }
  1041.  
  1042. /* Set the scrolling region.
  1043.  */
  1044. void
  1045. set_scroll_region(user, top, bottom)
  1046.   yuser *user;
  1047.   int top, bottom;
  1048. {
  1049.     if(top < 0 || top >= user->rows)
  1050.     return;
  1051.     if(bottom < top || bottom >= user->rows)
  1052.     return;
  1053.     user->sc_top = top;
  1054.     user->sc_bot = bottom;
  1055. }
  1056.  
  1057. /* Send a message to the terminal.
  1058.  */
  1059. void
  1060. msg_term(user, str)
  1061.   yuser *user;
  1062.   char *str;
  1063. {
  1064.     int y;
  1065.  
  1066.     if((y = user->y + 1) >= user->rows)
  1067.     y = 0;
  1068.     _move_term(user, y, 0);
  1069.     _addch_term(user, '[');
  1070.     while(*str)
  1071.     _addch_term(user, *(str++));
  1072.     _addch_term(user, ']');
  1073.     _clreol_term(user);
  1074.     _move_term(user, user->y, user->x);
  1075.     _flush_term(user);
  1076. }
  1077.  
  1078. /* Spew terminal contents to a file descriptor.
  1079.  */
  1080. void
  1081. spew_term(user, fd, rows, cols)
  1082.   yuser *user;
  1083.   int fd, rows, cols;
  1084. {
  1085.     register ychar *c, *e;
  1086.     register int len;
  1087.     int y;
  1088.     static char tmp[20];
  1089.  
  1090.     if(user->region_set)
  1091.     {
  1092.     y = 0;
  1093.     if(cols > user->cols)
  1094.         cols = user->cols;
  1095.     if(rows > user->rows)
  1096.         rows = user->rows;
  1097.     for(;;)
  1098.     {
  1099.         for(c = e = user->scr[y], len = cols; len > 0; len--, c++)
  1100.         if(*c != ' ')
  1101.             e = c + 1;
  1102.         if(e != user->scr[y])
  1103.         (void)write(fd, user->scr[y], e - user->scr[y]);
  1104.         if(++y >= rows)
  1105.         break;
  1106.         (void)write(fd, "\n", 1);
  1107.     }
  1108.  
  1109.     /* move the cursor to the correct place */
  1110.  
  1111.     sprintf(tmp, "%c[%d;%dH", 27, user->y + 1, user->x + 1);
  1112.     (void)write(fd, tmp, strlen(tmp));
  1113.     }
  1114.     else
  1115.     {
  1116.     y = first_interesting_row(user, rows, cols);
  1117.     for(;;)
  1118.     {
  1119.         if(y == user->y)
  1120.         {
  1121.         if(user->x > 0)
  1122.             (void)write(fd, user->scr[y], user->x);
  1123.         break;
  1124.         }
  1125.         for(c = e = user->scr[y], len = user->t_cols; len > 0; len--, c++)
  1126.         if(*c != ' ')
  1127.             e = c + 1;
  1128.         if(e != user->scr[y])
  1129.         (void)write(fd, user->scr[y], e - user->scr[y]);
  1130.         (void)write(fd, "\n", 1);
  1131.         if(++y >= user->t_rows)
  1132.         y = 0;
  1133.     }
  1134.     }
  1135. }
  1136.  
  1137. /* Draw some raw characters to the screen without updating any buffers.
  1138.  * Whoever uses this should know what they're doing.  It should always
  1139.  * be followed by a redraw_term() before calling any of the normal
  1140.  * term functions again.
  1141.  *
  1142.  * If the given string is not as long as the given length, then the
  1143.  * string is repeated to fill the given length.
  1144.  *
  1145.  * This is an unadvertised function.
  1146.  */
  1147. void
  1148. raw_term(user, y, x, str, len)
  1149.   yuser *user;
  1150.   int y, x;
  1151.   ychar *str;
  1152.   int len;
  1153. {
  1154.     register ychar *c;
  1155.  
  1156.     if(y < 0 || y >= user->t_rows)
  1157.     return;
  1158.     if(x < 0 || x >= user->t_cols)
  1159.     return;
  1160.     _move_term(user, y, x);
  1161.  
  1162.     for(c = str; len > 0; len--, c++)
  1163.     {
  1164.     if(*c == '\0')
  1165.         c = str;
  1166.     if(*c < ' ' || *c > '~')
  1167.         return;
  1168.     _addch_term(user, *c);
  1169.     }
  1170. }
  1171.  
  1172. int
  1173. center(width, n)
  1174.   int width, n;
  1175. {
  1176.     if(n >= width)
  1177.     return 0;
  1178.     return (width - n) >> 1;
  1179. }
  1180.  
  1181. void
  1182. redraw_all_terms()
  1183. {
  1184.     register yuser *u;
  1185.  
  1186.     switch(term_type)
  1187.     {
  1188.     case 1:        /* curses */
  1189.         redisplay_curses();
  1190.         break;
  1191.     default:
  1192.         redraw_term(me, 0);
  1193.         flush_term(me);
  1194.         for(u = connect_list; u; u = u->next)
  1195.         {
  1196.         redraw_term(u, 0);
  1197.         flush_term(u);
  1198.         }
  1199.     }
  1200. }
  1201.  
  1202. void
  1203. set_raw_term()
  1204. {
  1205.     /* only some terminal systems need to do this */
  1206.  
  1207.     switch(term_type)
  1208.     {
  1209.     case 1:        /* curses */
  1210.         set_raw_curses();
  1211.         break;
  1212.     }
  1213. }
  1214.  
  1215. void
  1216. set_cooked_term()
  1217. {
  1218.     /* only some terminal systems need to do this */
  1219.  
  1220.     switch(term_type)
  1221.     {
  1222.     case 1:        /* curses */
  1223.         set_cooked_curses();
  1224.         break;
  1225.     }
  1226. }
  1227.  
  1228. int
  1229. term_does_asides()
  1230. {
  1231.     /* only some terminal systems can do this */
  1232.  
  1233.     switch(term_type)
  1234.     {
  1235.     case 2:        /* X11 */
  1236.         return 1;
  1237.     }
  1238.     return 0;
  1239. }
  1240.