home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume27
/
ytalk-3.0
/
part01
/
term.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-20
|
22KB
|
1,170 lines
/* term.c */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#ifdef USE_SGTTY
# include <sys/ioctl.h>
# ifdef hpux
# include <sys/bsdtty.h>
# include <sgtty.h>
# endif
#else
# include <termios.h>
#endif
#include "curses.h"
#include "xwin.h"
#include "menu.h"
static int (*_open_term)(); /* open a new terminal */
static void (*_close_term)(); /* close a terminal */
static void (*_addch_term)(); /* write a char to a terminal */
static void (*_move_term)(); /* move cursor to Y,X position */
static void (*_clreol_term)(); /* clear to end of line */
static void (*_clreos_term)(); /* clear to end of screen */
static void (*_scroll_term)(); /* scroll up one line */
static void (*_rev_scroll_term)(); /* scroll down one line */
static void (*_flush_term)(); /* flush pending output */
static int term_type = 0;
#ifdef USE_SGTTY
static int line_discipline;
static int local_mode;
static struct sgttyb sgttyb;
static struct tchars tchars;
static struct ltchars ltchars;
#else
static struct termios tio;
#endif
#ifdef USE_SGTTY
static void
init_sgtty()
{
if(ioctl(0, TIOCGETD, &line_discipline) < 0)
{
show_error("TIOCGETD");
bail(YTE_INIT);
}
if(ioctl(0, TIOCLGET, &local_mode) < 0)
{
show_error("TIOCGETP");
bail(YTE_INIT);
}
if(ioctl(0, TIOCGETP, &sgttyb) < 0)
{
show_error("TIOCGETP");
bail(YTE_INIT);
}
if(ioctl(0, TIOCGETC, &tchars) < 0)
{
show_error("TIOCGETC");
bail(YTE_INIT);
}
if(ioctl(0, TIOCGLTC, <chars) < 0)
{
show_error("TIOCGLTC");
bail(YTE_INIT);
}
me->old_rub = sgttyb.sg_erase;
me->RUB = RUBDEF;
me->KILL = sgttyb.sg_kill;
me->WORD = ltchars.t_werasc;
me->CLR = '\024'; /* ^T */
}
#else
static void
init_termios()
{
/* get edit chars */
if(tcgetattr(0, &tio) < 0)
{
show_error("tcgetattr failed");
bail(YTE_INIT);
}
me->old_rub = tio.c_cc[VERASE];
me->RUB = RUBDEF;
#ifdef VKILL
me->KILL = tio.c_cc[VKILL];
#else
me->KILL = '\025'; /* ^U */
#endif
#ifdef VWERASE
me->WORD = tio.c_cc[VWERASE];
if(me->WORD == 0xff)
me->WORD = '\027'; /* ^W */
#else
me->WORD = '\027'; /* ^W */
#endif
me->CLR = '\024'; /* ^T */
}
#endif
/* Initialize terminal and input characteristics.
*/
void
init_term()
{
char tmpstr[64];
#ifdef USE_SGTTY
init_sgtty();
#else
init_termios();
#endif
/* Decide on a terminal (window) system and set up the
* function pointers.
*/
term_type = 0; /* nothing selected yet */
#ifdef USE_X11
if(term_type == 0 && (def_flags & FL_XWIN) && getenv("DISPLAY"))
{
_open_term = open_xwin;
_close_term = close_xwin;
_addch_term = addch_xwin;
_move_term = move_xwin;
_clreol_term = clreol_xwin;
_clreos_term = clreos_xwin;
_scroll_term = scroll_xwin;
_rev_scroll_term = rev_scroll_xwin;
_flush_term = flush_xwin;
init_xwin();
term_type = 2; /* using xwin */
}
#endif
/* if no window system found, default to curses */
if(term_type == 0)
{
_open_term = open_curses;
_close_term = close_curses;
_addch_term = addch_curses;
_move_term = move_curses;
_clreol_term = clreol_curses;
_clreos_term = clreos_curses;
_scroll_term = scroll_curses;
_rev_scroll_term = NULL;
_flush_term = flush_curses;
init_curses();
term_type = 1; /* using curses */
}
/* set me up a terminal */
sprintf(tmpstr, "YTalk version %d.%d", VMAJOR, VMINOR);
if(open_term(me, tmpstr) < 0)
{
end_term();
show_error("init_term: open_term() failed");
bail(0);
}
}
/* Set terminal size.
*/
void
set_terminal_size(fd, rows, cols)
int fd, rows, cols;
{
#ifdef TIOCSWINSZ
struct winsize winsize;
winsize.ws_row = rows;
winsize.ws_col = cols;
ioctl(fd, TIOCSWINSZ, &winsize);
#endif
}
/* Set terminal and input characteristics for slave terminals.
*/
void
set_terminal_flags(fd)
int fd;
{
#ifdef USE_SGTTY
(void)ioctl(fd, TIOCSETD, &line_discipline);
(void)ioctl(fd, TIOCLSET, &local_mode);
(void)ioctl(fd, TIOCSETP, &sgttyb);
(void)ioctl(fd, TIOCSETC, &tchars);
(void)ioctl(fd, TIOCSLTC, <chars);
#else
if(tcsetattr(fd, TCSANOW, &tio) < 0)
show_error("tcsetattr failed");
#endif
}
int
what_term()
{
return term_type;
}
/* Abort all terminal processing.
*/
void
end_term()
{
switch(term_type)
{
case 1: /* curses */
end_curses();
break;
#ifdef USE_X11
case 2: /* xwin */
end_xwin();
break;
#endif
}
term_type = 0;
}
/* Open a new user window.
*/
int
open_term(user, title)
register yuser *user;
register char *title;
{
if(_open_term(user, title) != 0)
return -1;
user->x = user->y = 0;
if(user->scr == NULL)
resize_win(user, 24, 80);
return 0;
}
/* Close a user window.
*/
void
close_term(user)
register yuser *user;
{
register int i;
if(user->scr)
{
_close_term(user);
for(i = 0; i < user->t_rows; i++)
free(user->scr[i]);
free(user->scr);
user->scr = NULL;
user->t_rows = user->rows = 0;
user->t_cols = user->cols = 0;
}
}
/* Place a character.
*/
void
addch_term(user, c)
register yuser *user;
register ychar c;
{
if(c >= ' ' && c <= '~')
{
_addch_term(user, c);
user->scr[user->y][user->x] = c;
if(++(user->x) >= user->cols)
{
user->bump = 1;
user->x = user->cols - 1;
if(user->cols < user->t_cols)
_move_term(user, user->y, user->x);
}
}
}
/* Move the cursor.
*/
void
move_term(user, y, x)
register yuser *user;
register int y, x;
{
if(y < 0 || y >= user->rows)
y = user->rows - 1;
if(x < 0 || x >= user->cols)
{
user->bump = 1;
x = user->cols - 1;
}
else
user->bump = 0;
_move_term(user, y, x);
user->y = y;
user->x = x;
}
/* Clear to EOL.
*/
void
clreol_term(user)
register yuser *user;
{
register int j;
register ychar *c;
if(user->cols < user->t_cols)
{
c = user->scr[user->y] + user->x;
for(j = user->x; j < user->cols; j++)
{
*(c++) = ' ';
_addch_term(user, ' ');
}
move_term(user, user->y, user->x);
}
else
{
_clreol_term(user);
c = user->scr[user->y] + user->x;
for(j = user->x; j < user->cols; j++)
*(c++) = ' ';
}
}
/* Clear to EOS.
*/
void
clreos_term(user)
register yuser *user;
{
register int j, i;
register ychar *c;
int x, y;
if(user->cols < user->t_cols || user->rows < user->t_rows)
{
x = user->x;
y = user->y;
clreol_term(user);
for(i = user->y + 1; i < user->rows; i++)
{
move_term(user, i, 0);
clreol_term(user);
}
move_term(user, y, x);
}
else
{
_clreos_term(user);
j = user->x;
for(i = user->y; i < user->rows; i++)
{
c = user->scr[i] + j;
for(; j < user->cols; j++)
*(c++) = ' ';
j = 0;
}
}
}
/* Scroll window.
*/
void
scroll_term(user)
register yuser *user;
{
register int i;
register ychar *c;
if(user->sc_bot > user->sc_top)
{
c = user->scr[user->sc_top];
for(i = user->sc_top; i < user->sc_bot; i++)
user->scr[i] = user->scr[i+1];
user->scr[user->sc_bot] = c;
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
if(_scroll_term
&& user->rows == user->t_rows
&& user->cols == user->t_cols
&& user->sc_top == 0
&& user->sc_bot == user->rows - 1)
_scroll_term(user);
else
redraw_term(user, 0);
}
else
{
move_term(user, user->sc_top, 0);
clreol_term(user);
}
}
/* Reverse-scroll window.
*/
void
rev_scroll_term(user)
register yuser *user;
{
register int i;
register ychar *c;
if(user->sc_bot > user->sc_top)
{
c = user->scr[user->sc_bot];
for(i = user->sc_bot; i > user->sc_top; i--)
user->scr[i] = user->scr[i-1];
user->scr[user->sc_top] = c;
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
if(_rev_scroll_term
&& user->rows == user->t_rows
&& user->cols == user->t_cols
&& user->sc_top == 0
&& user->sc_bot == user->rows - 1)
_rev_scroll_term(user);
else
redraw_term(user, 0);
}
else
{
move_term(user, user->sc_top, 0);
clreol_term(user);
}
}
/* Flush window output.
*/
void
flush_term(user)
register yuser *user;
{
_flush_term(user);
}
/* Rub one character.
*/
void
rub_term(user)
register yuser *user;
{
if(user->x > 0)
{
if(user->bump)
{
addch_term(user, ' ');
user->bump = 0;
}
else
{
move_term(user, user->y, user->x - 1);
addch_term(user, ' ');
move_term(user, user->y, user->x - 1);
}
}
}
/* Rub one word.
*/
int
word_term(user)
register yuser *user;
{
register int x, out;
for(x = user->x - 1; x >= 0 && user->scr[user->y][x] == ' '; x--)
continue;
for(; x >= 0 && user->scr[user->y][x] != ' '; x--)
continue;
out = user->x - (++x);
if(out <= 0)
return 0;
move_term(user, user->y, x);
clreol_term(user);
return out;
}
/* Kill current line.
*/
void
kill_term(user)
register yuser *user;
{
if(user->x > 0)
{
move_term(user, user->y, 0);
clreol_term(user);
}
}
/* Expand a tab. We use non-destructive tabs.
*/
void
tab_term(user)
register yuser *user;
{
move_term(user, user->y, (user->x + 8) & 0xfff8);
}
/* Process a newline.
*/
void
newline_term(user)
register yuser *user;
{
register int new_y, next_y;
new_y = user->y + 1;
if(user->flags & FL_RAW)
{
if(new_y >= user->rows)
{
if(user->flags & FL_SCROLL)
scroll_term(user);
}
else
move_term(user, new_y, user->x);
}
else
{
if(new_y >= user->rows)
{
if(user->flags & FL_SCROLL)
{
scroll_term(user);
move_term(user, user->y, 0);
return;
}
new_y = 0;
}
next_y = new_y + 1;
if(next_y >= user->rows)
next_y = 0;
if(next_y > 0 || !(user->flags & FL_SCROLL))
{
move_term(user, next_y, 0);
clreol_term(user);
}
move_term(user, new_y, 0);
clreol_term(user);
}
}
/* Insert lines.
*/
void
add_line_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
if(num == 1 && user->y == 0)
rev_scroll_term(user);
else
{
/* find number of remaining lines */
i = user->rows - user->y - num;
if(i <= 0)
{
i = user->x;
move_term(user, user->y, 0);
clreos_term(user);
move_term(user, user->y, i);
return;
}
/* swap the remaining lines to bottom */
for(i--; i >= 0; i--)
{
c = user->scr[user->y + i];
user->scr[user->y + i] = user->scr[user->y + i + num];
user->scr[user->y + i + num] = c;
}
/* clear the added lines */
for(num--; num >= 0; num--)
{
c = user->scr[user->y + num];
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
}
redraw_term(user, user->y);
}
}
/* Delete lines.
*/
void
del_line_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
if(num == 1 && user->y == 0)
scroll_term(user);
else
{
/* find number of remaining lines */
i = user->rows - user->y - num;
if(i <= 0)
{
i = user->x;
move_term(user, user->y, 0);
clreos_term(user);
move_term(user, user->y, i);
return;
}
/* swap the remaining lines to top */
for(; i > 0; i--)
{
c = user->scr[user->rows - i];
user->scr[user->rows - i] = user->scr[user->rows - i - num];
user->scr[user->rows - i - num] = c;
}
/* clear the remaining bottom lines */
for(; num > 0; num--)
{
c = user->scr[user->rows - num];
for(i = 0; i < user->cols; i++)
*(c++) = ' ';
}
redraw_term(user, user->y);
}
}
static void
copy_text(fr, to, count)
register ychar *fr, *to;
register int count;
{
if(to < fr)
{
for(; count > 0; count--)
*(to++) = *(fr++);
}
else
{
fr += count;
to += count;
for(; count > 0; count--)
*(--to) = *(--fr);
}
}
/* Add chars.
*/
void
add_char_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
/* find number of remaining non-blank chars */
i = user->cols - user->x - num;
c = user->scr[user->y] + user->cols - num - 1;
while(i > 0 && *c == ' ')
c--, i--;
if(i <= 0)
{
clreol_term(user);
return;
}
/* transfer the chars and clear the remaining */
c++;
copy_text(c - i, c - i + num, i);
for(c -= i; num > 0; num--)
{
*(c++) = ' ';
_addch_term(user, ' ');
}
for(; i > 0; i--)
_addch_term(user, *(c++));
_move_term(user, user->y, user->x);
}
/* Delete chars.
*/
void
del_char_term(user, num)
register yuser *user;
int num;
{
register ychar *c;
register int i;
/* find number of remaining non-blank chars */
i = user->cols - user->x - num;
c = user->scr[user->y] + user->cols - 1;
while(i > 0 && *c == ' ')
c--, i--;
if(i <= 0)
{
clreol_term(user);
return;
}
/* transfer the chars and clear the remaining */
c++;
copy_text(c - i, c - i - num, i);
for(c -= (i + num); i > 0; i--)
_addch_term(user, *(c++));
for(; num > 0; num--)
{
*(c++) = ' ';
_addch_term(user, ' ');
}
_move_term(user, user->y, user->x);
}
/* Redraw a user's window.
*/
void
redraw_term(user, y)
register yuser *user;
register int y;
{
register int x, spaces;
register ychar *c;
for(; y < user->t_rows; y++)
{
_move_term(user, y, 0);
_clreol_term(user);
spaces = 0;
c = user->scr[y];
for(x = 0; x < user->t_cols; x++, c++)
{
if(*c == ' ')
spaces++;
else
{
if(spaces)
{
if(spaces <= 3) /* arbitrary */
{
for(; spaces > 0; spaces--)
_addch_term(user, ' ');
}
else
{
_move_term(user, y, x);
spaces = 0;
}
}
_addch_term(user, *c);
}
}
}
/* redisplay any active menu */
if(menu_ptr != NULL)
update_menu();
else
_move_term(user, user->y, user->x);
}
/* Return the first interesting row for a user with a window of
* the given height and width.
*/
static int
first_interesting_row(user, height, width)
yuser *user;
int height, width;
{
register int j, i;
register ychar *c;
if(height < user->t_rows)
{
j = (user->y + 1) - height;
if(j < 0)
j += user->t_rows;
}
else
{
j = user->y + 1;
if(j >= user->t_rows)
j = 0;
}
while(j != user->y)
{
i = (width > user->t_cols) ? user->t_cols : width;
for(c = user->scr[j]; i > 0; i--, c++)
if(*c != ' ')
break;
if(i > 0)
break;
if(++j >= user->t_rows)
j = 0;
}
return j;
}
/* Called when a user's window has been resized.
*/
void
resize_win(user, height, width)
yuser *user;
int height, width;
{
register int j, i;
register ychar *c, **newscr;
int new_y, y_pos;
if(height == user->t_rows && width == user->t_cols)
return;
/* resize the user terminal buffer */
new_y = -1;
y_pos = 0;
newscr = (ychar **)get_mem(height * sizeof(ychar *));
if(user->scr == NULL)
{
user->t_rows = user->rows = 0;
user->t_cols = user->cols = 0;
}
else if(user->region_set)
{
/* save as many top lines as possible */
for(j = 0; j < height && j < user->t_rows; j++)
newscr[j] = user->scr[j];
new_y = j - 1;
y_pos = user->y;
for(; j < user->t_rows; j++)
free(user->scr[j]);
free(user->scr);
}
else
{
/* shift all recent lines to top of screen */
j = first_interesting_row(user, height, width);
for(i = 0; i < height; i++)
{
newscr[++new_y] = user->scr[j];
if(j == user->y)
break;
if(++j >= user->t_rows)
j = 0;
}
for(i++; i < user->t_rows; i++)
{
if(++j >= user->t_rows)
j = 0;
free(user->scr[j]);
}
y_pos = new_y;
free(user->scr);
}
user->scr = newscr;
/* fill in the missing portions */
if(width > user->t_cols)
for(i = 0; i <= new_y; i++)
{
user->scr[i] = (ychar *)realloc_mem(user->scr[i], width);
for(j = user->t_cols; j < width; j++)
user->scr[i][j] = ' ';
}
for(i = new_y + 1; i < height; i++)
{
c = user->scr[i] = (ychar *)get_mem(width);
for(j = 0; j < width; j++)
*(c++) = ' ';
}
/* reset window values */
user->t_rows = user->rows = height;
user->t_cols = user->cols = width;
user->sc_top = 0;
user->sc_bot = height - 1;
move_term(user, y_pos, user->x);
send_winch(user);
redraw_term(user, 0);
flush_term(user);
}
/* Draw a nice box.
*/
static void
draw_box(user, height, width, c)
yuser *user;
int height, width;
char c;
{
register int i;
if(width < user->t_cols)
{
for(i = 0; i < height; i++)
{
move_term(user, i, width);
addch_term(user, c);
if(width + 1 < user->t_cols)
clreol_term(user);
}
}
if(height < user->t_rows)
{
move_term(user, height, 0);
for(i = 0; i < width; i++)
addch_term(user, c);
if(width < user->t_cols)
addch_term(user, c);
if(width + 1 < user->t_cols)
clreol_term(user);
if(height + 1 < user->t_rows)
{
move_term(user, height + 1, 0);
clreos_term(user);
}
}
}
/* Set the virtual terminal size, ie: the display region.
*/
void
set_win_region(user, height, width)
yuser *user;
int height, width;
{
register int x, y;
int old_height, old_width;
if(height < 2 || height > user->t_rows)
height = user->t_rows;
if(width < 2 || width > user->t_cols)
width = user->t_cols;
/* Don't check if they're already equal; always perform processing.
* Just because it's equal over here doesn't mean it's equal for all
* ytalk connections. We still need to clear the screen.
*/
old_height = user->rows;
old_width = user->cols;
user->rows = user->t_rows;
user->cols = user->t_cols;
if(user->region_set)
{
x = user->x;
y = user->y;
if(width > old_width || height > old_height)
draw_box(user, old_height, old_width, ' ');
}
else
{
x = y = 0;
move_term(user, 0, 0);
clreos_term(user);
user->region_set = 1;
}
draw_box(user, height, width, '%');
/* set the display region */
user->rows = height;
user->cols = width;
user->sc_top = 0;
user->sc_bot = height - 1;
move_term(user, y, x);
flush_term(user);
if(user == me)
send_region();
}
/* Set the virtual terminal size, ie: the display region.
*/
void
end_win_region(user)
yuser *user;
{
int old_height, old_width;
old_height = user->rows;
old_width = user->cols;
user->rows = user->t_rows;
user->cols = user->t_cols;
if(old_height < user->t_rows || old_width < user->t_cols)
draw_box(user, old_height, old_width, ' ');
user->region_set = 0;
if(user == me)
send_end_region();
}
/* Set the scrolling region.
*/
void
set_scroll_region(user, top, bottom)
yuser *user;
int top, bottom;
{
if(top < 0 || top >= user->rows)
return;
if(bottom < top || bottom >= user->rows)
return;
user->sc_top = top;
user->sc_bot = bottom;
}
/* Send a message to the terminal.
*/
void
msg_term(user, str)
yuser *user;
char *str;
{
int y;
if((y = user->y + 1) >= user->rows)
y = 0;
_move_term(user, y, 0);
_addch_term(user, '[');
while(*str)
_addch_term(user, *(str++));
_addch_term(user, ']');
_clreol_term(user);
_move_term(user, user->y, user->x);
_flush_term(user);
}
/* Spew terminal contents to a file descriptor.
*/
void
spew_term(user, fd, rows, cols)
yuser *user;
int fd, rows, cols;
{
register ychar *c, *e;
register int len;
int y;
static char tmp[20];
if(user->region_set)
{
y = 0;
if(cols > user->cols)
cols = user->cols;
if(rows > user->rows)
rows = user->rows;
for(;;)
{
for(c = e = user->scr[y], len = cols; len > 0; len--, c++)
if(*c != ' ')
e = c + 1;
if(e != user->scr[y])
(void)write(fd, user->scr[y], e - user->scr[y]);
if(++y >= rows)
break;
(void)write(fd, "\n", 1);
}
/* move the cursor to the correct place */
sprintf(tmp, "%c[%d;%dH", 27, user->y + 1, user->x + 1);
(void)write(fd, tmp, strlen(tmp));
}
else
{
y = first_interesting_row(user, rows, cols);
for(;;)
{
if(y == user->y)
{
if(user->x > 0)
(void)write(fd, user->scr[y], user->x);
break;
}
for(c = e = user->scr[y], len = user->t_cols; len > 0; len--, c++)
if(*c != ' ')
e = c + 1;
if(e != user->scr[y])
(void)write(fd, user->scr[y], e - user->scr[y]);
(void)write(fd, "\n", 1);
if(++y >= user->t_rows)
y = 0;
}
}
}
/* Draw some raw characters to the screen without updating any buffers.
* Whoever uses this should know what they're doing. It should always
* be followed by a redraw_term() before calling any of the normal
* term functions again.
*
* If the given string is not as long as the given length, then the
* string is repeated to fill the given length.
*
* This is an unadvertised function.
*/
void
raw_term(user, y, x, str, len)
yuser *user;
int y, x;
ychar *str;
int len;
{
register ychar *c;
if(y < 0 || y >= user->t_rows)
return;
if(x < 0 || x >= user->t_cols)
return;
_move_term(user, y, x);
for(c = str; len > 0; len--, c++)
{
if(*c == '\0')
c = str;
if(*c < ' ' || *c > '~')
return;
_addch_term(user, *c);
}
}
int
center(width, n)
int width, n;
{
if(n >= width)
return 0;
return (width - n) >> 1;
}