mirror of
https://github.com/rfivet/uemacs.git
synced 2024-11-14 00:26:01 -05:00
1585 lines
33 KiB
C
1585 lines
33 KiB
C
/* display.c -- implements display.h */
|
|
|
|
#include "display.h"
|
|
|
|
#define REVSTA 1 /* Status line appears in reverse video */
|
|
|
|
/* display.c
|
|
*
|
|
* The functions in this file handle redisplay. There are two halves, the
|
|
* ones that update the virtual display screen, and the ones that make the
|
|
* physical display screen the same as the virtual display screen. These
|
|
* functions use hints that are left in the windows by the commands.
|
|
*
|
|
* Modified by Petri Kutvonen
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "buffer.h"
|
|
#include "estruct.h"
|
|
#include "input.h"
|
|
#include "line.h"
|
|
#include "termio.h"
|
|
#include "terminal.h"
|
|
#include "version.h"
|
|
#include "wrapper.h"
|
|
#include "utf8.h"
|
|
#include "window.h"
|
|
|
|
struct video {
|
|
int v_flag; /* Flags */
|
|
#if COLOR
|
|
int v_fcolor; /* current forground color */
|
|
int v_bcolor; /* current background color */
|
|
int v_rfcolor; /* requested forground color */
|
|
int v_rbcolor; /* requested background color */
|
|
#endif
|
|
unicode_t v_text[1]; /* Screen data. */
|
|
};
|
|
|
|
#define VFCHG 0x0001 /* Changed flag */
|
|
#define VFEXT 0x0002 /* extended (beyond column 80) */
|
|
#define VFREV 0x0004 /* reverse video status */
|
|
#define VFREQ 0x0008 /* reverse video request */
|
|
#define VFCOL 0x0010 /* color change requested */
|
|
|
|
static struct video **vscreen; /* Virtual screen. */
|
|
#if MEMMAP == 0 || SCROLLCODE
|
|
static struct video **pscreen; /* Physical screen. */
|
|
#endif
|
|
|
|
static int displaying = TRUE;
|
|
#if UNIX
|
|
#include <signal.h>
|
|
#endif
|
|
#ifdef SIGWINCH
|
|
#include <sys/ioctl.h>
|
|
/* for window size changes */
|
|
int chg_width, chg_height;
|
|
#endif
|
|
|
|
static int currow ; /* Cursor row */
|
|
static int curcol ; /* Cursor column */
|
|
static int vtrow = 0 ; /* Row location of SW cursor */
|
|
static int vtcol = 0 ; /* Column location of SW cursor */
|
|
static int lbound = 0 ; /* leftmost column of current line being displayed */
|
|
static int taboff = 0 ; /* tab offset for display */
|
|
|
|
int mpresf = FALSE ; /* TRUE if message in last line */
|
|
int scrollcount = 1 ; /* number of lines to scroll */
|
|
int discmd = TRUE ; /* display command flag */
|
|
|
|
static int reframe(struct window *wp);
|
|
static void updone(struct window *wp);
|
|
static void updall(struct window *wp);
|
|
static int scrolls(int inserts);
|
|
static void scrscroll(int from, int to, int count);
|
|
static int texttest(int vrow, int prow);
|
|
static int endofline(unicode_t *s, int n);
|
|
static void updext(void);
|
|
static int updateline(int row, struct video *vp1, struct video *vp2);
|
|
static void modeline(struct window *wp);
|
|
static void mlputi(int i, int r);
|
|
static void mlputli(long l, int r);
|
|
static void mlputf(int s);
|
|
static void mlputs( unsigned char *s) ;
|
|
#if SIGWINCH
|
|
static int newscreensize(int h, int w);
|
|
#endif
|
|
|
|
#if RAINBOW
|
|
static void putline(int row, int col, char *buf);
|
|
#endif
|
|
|
|
/*
|
|
* Initialize the data structures used by the display code. The edge vectors
|
|
* used to access the screens are set up. The operating system's terminal I/O
|
|
* channel is set up. All the other things get initialized at compile time.
|
|
* The original window has "WFCHG" set, so that it will get completely
|
|
* redrawn on the first call to "update".
|
|
*/
|
|
void vtinit(void)
|
|
{
|
|
int i;
|
|
struct video *vp;
|
|
|
|
TTopen(); /* open the screen */
|
|
TTkopen(); /* open the keyboard */
|
|
TTrev(FALSE);
|
|
vscreen = xmalloc(term.t_mrow * sizeof(struct video *));
|
|
|
|
#if MEMMAP == 0 || SCROLLCODE
|
|
pscreen = xmalloc(term.t_mrow * sizeof(struct video *));
|
|
#endif
|
|
for (i = 0; i < term.t_mrow; ++i) {
|
|
vp = xmalloc(sizeof(struct video) + term.t_mcol*4);
|
|
vp->v_flag = 0;
|
|
#if COLOR
|
|
vp->v_rfcolor = 7;
|
|
vp->v_rbcolor = 0;
|
|
#endif
|
|
vscreen[i] = vp;
|
|
#if MEMMAP == 0 || SCROLLCODE
|
|
vp = xmalloc(sizeof(struct video) + term.t_mcol*4);
|
|
vp->v_flag = 0;
|
|
pscreen[i] = vp;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if CLEAN
|
|
/* free up all the dynamically allocated video structures */
|
|
|
|
void vtfree(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < term.t_mrow; ++i) {
|
|
free(vscreen[i]);
|
|
#if MEMMAP == 0 || SCROLLCODE
|
|
free(pscreen[i]);
|
|
#endif
|
|
}
|
|
free(vscreen);
|
|
#if MEMMAP == 0 || SCROLLCODE
|
|
free(pscreen);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Clean up the virtual terminal system, in anticipation for a return to the
|
|
* operating system. Move down to the last line and clear it out (the next
|
|
* system prompt will be written in the line). Shut down the channel to the
|
|
* terminal.
|
|
*/
|
|
void vttidy(void)
|
|
{
|
|
mlerase() ; /* ends with movecursor( term.t_nrow, 0) and TTflush() */
|
|
TTclose();
|
|
TTkclose();
|
|
#ifdef PKCODE
|
|
{
|
|
int ret ;
|
|
ret = write( 1, "\r", 1) ;
|
|
if( ret != 1) {
|
|
/* some error handling here */
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Set the virtual cursor to the specified row and column on the virtual
|
|
* screen. There is no checking for nonsense values; this might be a good
|
|
* idea during the early stages.
|
|
*/
|
|
void vtmove(int row, int col)
|
|
{
|
|
vtrow = row;
|
|
vtcol = col;
|
|
}
|
|
|
|
/*
|
|
* Write a character to the virtual screen. The virtual row and
|
|
* column are updated. If we are not yet on left edge, don't print
|
|
* it yet. If the line is too long put a "$" in the last column.
|
|
*
|
|
* This routine only puts printing characters into the virtual
|
|
* terminal buffers. Only column overflow is checked.
|
|
*/
|
|
static void vtputc(int c)
|
|
{
|
|
struct video *vp; /* ptr to line being updated */
|
|
|
|
/* In case somebody passes us a signed char.. */
|
|
if (c < 0) {
|
|
c += 256;
|
|
if (c < 0)
|
|
return;
|
|
}
|
|
|
|
vp = vscreen[vtrow];
|
|
|
|
if (vtcol >= term.t_ncol) {
|
|
++vtcol;
|
|
vp->v_text[term.t_ncol - 1] = '$';
|
|
return;
|
|
}
|
|
|
|
if (c == '\t') {
|
|
do {
|
|
vtputc(' ');
|
|
} while (((vtcol + taboff) & tabmask) != 0);
|
|
return;
|
|
}
|
|
|
|
if (c < 0x20) {
|
|
vtputc('^');
|
|
vtputc(c ^ 0x40);
|
|
return;
|
|
}
|
|
|
|
if (c == 0x7f) {
|
|
vtputc('^');
|
|
vtputc('?');
|
|
return;
|
|
}
|
|
|
|
if (c >= 0x80 && c <= 0xA0) {
|
|
static const char hex[] = "0123456789abcdef";
|
|
vtputc('\\');
|
|
vtputc(hex[c >> 4]);
|
|
vtputc(hex[c & 15]);
|
|
return;
|
|
}
|
|
|
|
if (vtcol >= 0)
|
|
vp->v_text[vtcol] = c;
|
|
++vtcol;
|
|
}
|
|
|
|
static int vtputs( const char *s) {
|
|
int n = 0 ;
|
|
|
|
while( *s) {
|
|
vtputc( *s++) ;
|
|
n += 1 ;
|
|
}
|
|
|
|
return n ;
|
|
}
|
|
|
|
/*
|
|
* Erase from the end of the software cursor to the end of the line on which
|
|
* the software cursor is located.
|
|
*/
|
|
static void vteeol(void)
|
|
{
|
|
/* struct video *vp; */
|
|
unicode_t *vcp = vscreen[vtrow]->v_text;
|
|
|
|
/* vp = vscreen[vtrow]; */
|
|
while (vtcol < term.t_ncol)
|
|
/* vp->v_text[vtcol++] = ' '; */
|
|
vcp[vtcol++] = ' ';
|
|
}
|
|
|
|
/*
|
|
* upscreen:
|
|
* user routine to force a screen update
|
|
* always finishes complete update
|
|
*/
|
|
int upscreen(int f, int n)
|
|
{
|
|
update(TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
#if SCROLLCODE
|
|
static int scrflags;
|
|
#endif
|
|
|
|
/*
|
|
* Make sure that the display is right. This is a three part process. First,
|
|
* scan through all of the windows looking for dirty ones. Check the framing,
|
|
* and refresh the screen. Second, make sure that "currow" and "curcol" are
|
|
* correct for the current window. Third, make the virtual and physical
|
|
* screens the same.
|
|
*
|
|
* int force; force update past type ahead?
|
|
*/
|
|
int update(int force)
|
|
{
|
|
struct window *wp;
|
|
|
|
#if TYPEAH && ! PKCODE
|
|
if (force == FALSE && typahead())
|
|
return TRUE;
|
|
#endif
|
|
#if VISMAC == 0
|
|
if (force == FALSE && kbdmode == PLAY)
|
|
return TRUE;
|
|
#endif
|
|
|
|
displaying = TRUE;
|
|
|
|
#if SCROLLCODE
|
|
|
|
/* first, propagate mode line changes to all instances of
|
|
a buffer displayed in more than one window */
|
|
wp = wheadp;
|
|
while (wp != NULL) {
|
|
if (wp->w_flag & WFMODE) {
|
|
if (wp->w_bufp->b_nwnd > 1) {
|
|
/* make sure all previous windows have this */
|
|
struct window *owp;
|
|
owp = wheadp;
|
|
while (owp != NULL) {
|
|
if (owp->w_bufp == wp->w_bufp)
|
|
owp->w_flag |= WFMODE;
|
|
owp = owp->w_wndp;
|
|
}
|
|
}
|
|
}
|
|
wp = wp->w_wndp;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* update any windows that need refreshing */
|
|
wp = wheadp;
|
|
while (wp != NULL) {
|
|
if (wp->w_flag) {
|
|
/* if the window has changed, service it */
|
|
reframe(wp); /* check the framing */
|
|
#if SCROLLCODE
|
|
if (wp->w_flag & (WFKILLS | WFINS)) {
|
|
scrflags |=
|
|
(wp->w_flag & (WFINS | WFKILLS));
|
|
wp->w_flag &= ~(WFKILLS | WFINS);
|
|
}
|
|
#endif
|
|
if ((wp->w_flag & ~WFMODE) == WFEDIT)
|
|
updone(wp); /* update EDITed line */
|
|
else if (wp->w_flag & ~WFMOVE)
|
|
updall(wp); /* update all lines */
|
|
#if SCROLLCODE
|
|
if (scrflags || (wp->w_flag & WFMODE))
|
|
#else
|
|
if (wp->w_flag & WFMODE)
|
|
#endif
|
|
modeline(wp); /* update modeline */
|
|
wp->w_flag = 0;
|
|
wp->w_force = 0;
|
|
}
|
|
/* on to the next window */
|
|
wp = wp->w_wndp;
|
|
}
|
|
|
|
/* recalc the current hardware cursor location */
|
|
updpos();
|
|
|
|
#if MEMMAP && ! SCROLLCODE
|
|
/* update the cursor and flush the buffers */
|
|
movecursor(currow, curcol - lbound);
|
|
#endif
|
|
|
|
/* check for lines to de-extend */
|
|
upddex();
|
|
|
|
/* if screen is garbage, re-plot it */
|
|
if (sgarbf != FALSE)
|
|
updgar();
|
|
|
|
/* update the virtual screen to the physical screen */
|
|
updupd(force);
|
|
|
|
/* update the cursor and flush the buffers */
|
|
movecursor(currow, curcol - lbound);
|
|
TTflush();
|
|
displaying = FALSE;
|
|
#if SIGWINCH
|
|
while (chg_width || chg_height)
|
|
newscreensize(chg_height, chg_width);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* reframe:
|
|
* check to see if the cursor is on in the window
|
|
* and re-frame it if needed or wanted
|
|
*/
|
|
static int reframe(struct window *wp)
|
|
{
|
|
struct line *lp, *lp0;
|
|
int i = 0;
|
|
|
|
/* if not a requested reframe, check for a needed one */
|
|
if ((wp->w_flag & WFFORCE) == 0) {
|
|
#if SCROLLCODE
|
|
/* loop from one line above the window to one line after */
|
|
lp = wp->w_linep;
|
|
lp0 = lback(lp);
|
|
if (lp0 == wp->w_bufp->b_linep)
|
|
i = 0;
|
|
else {
|
|
i = -1;
|
|
lp = lp0;
|
|
}
|
|
for (; i <= (int) (wp->w_ntrows); i++)
|
|
#else
|
|
lp = wp->w_linep;
|
|
for (i = 0; i < wp->w_ntrows; i++)
|
|
#endif
|
|
{
|
|
/* if the line is in the window, no reframe */
|
|
if (lp == wp->w_dotp) {
|
|
#if SCROLLCODE
|
|
/* if not _quite_ in, we'll reframe gently */
|
|
if (i < 0 || i == wp->w_ntrows) {
|
|
/* if the terminal can't help, then
|
|
we're simply outside */
|
|
if (term.t_scroll == NULL)
|
|
i = wp->w_force;
|
|
break;
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
/* if we are at the end of the file, reframe */
|
|
if (lp == wp->w_bufp->b_linep)
|
|
break;
|
|
|
|
/* on to the next line */
|
|
lp = lforw(lp);
|
|
}
|
|
}
|
|
#if SCROLLCODE
|
|
if (i == -1) { /* we're just above the window */
|
|
i = scrollcount; /* put dot at first line */
|
|
scrflags |= WFINS;
|
|
} else if (i == wp->w_ntrows) { /* we're just below the window */
|
|
i = -scrollcount; /* put dot at last line */
|
|
scrflags |= WFKILLS;
|
|
} else /* put dot where requested */
|
|
#endif
|
|
i = wp->w_force; /* (is 0, unless reposition() was called) */
|
|
|
|
wp->w_flag |= WFMODE;
|
|
|
|
/* how far back to reframe? */
|
|
if (i > 0) { /* only one screen worth of lines max */
|
|
if (--i >= wp->w_ntrows)
|
|
i = wp->w_ntrows - 1;
|
|
} else if (i < 0) { /* negative update???? */
|
|
i += wp->w_ntrows;
|
|
if (i < 0)
|
|
i = 0;
|
|
} else
|
|
i = wp->w_ntrows / 2;
|
|
|
|
/* backup to new line at top of window */
|
|
lp = wp->w_dotp;
|
|
while (i != 0 && lback(lp) != wp->w_bufp->b_linep) {
|
|
--i;
|
|
lp = lback(lp);
|
|
}
|
|
|
|
/* and reset the current line at top of window */
|
|
wp->w_linep = lp;
|
|
wp->w_flag |= WFHARD;
|
|
wp->w_flag &= ~WFFORCE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void show_line(struct line *lp)
|
|
{
|
|
int i = 0, len = llength(lp);
|
|
|
|
while (i < len) {
|
|
unicode_t c;
|
|
i += utf8_to_unicode(lp->l_text, i, len, &c);
|
|
vtputc(c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* updone:
|
|
* update the current line to the virtual screen
|
|
*
|
|
* struct window *wp; window to update current line in
|
|
*/
|
|
static void updone(struct window *wp)
|
|
{
|
|
struct line *lp; /* line to update */
|
|
int sline; /* physical screen line to update */
|
|
|
|
/* search down the line we want */
|
|
lp = wp->w_linep;
|
|
sline = wp->w_toprow;
|
|
while (lp != wp->w_dotp) {
|
|
++sline;
|
|
lp = lforw(lp);
|
|
}
|
|
|
|
/* and update the virtual line */
|
|
vscreen[sline]->v_flag |= VFCHG;
|
|
vscreen[sline]->v_flag &= ~VFREQ;
|
|
vtmove(sline, 0);
|
|
show_line(lp);
|
|
#if COLOR
|
|
vscreen[sline]->v_rfcolor = wp->w_fcolor;
|
|
vscreen[sline]->v_rbcolor = wp->w_bcolor;
|
|
#endif
|
|
vteeol();
|
|
}
|
|
|
|
/*
|
|
* updall:
|
|
* update all the lines in a window on the virtual screen
|
|
*
|
|
* struct window *wp; window to update lines in
|
|
*/
|
|
static void updall(struct window *wp)
|
|
{
|
|
struct line *lp; /* line to update */
|
|
int sline; /* physical screen line to update */
|
|
|
|
/* search down the lines, updating them */
|
|
lp = wp->w_linep;
|
|
sline = wp->w_toprow;
|
|
while (sline < wp->w_toprow + wp->w_ntrows) {
|
|
|
|
/* and update the virtual line */
|
|
vscreen[sline]->v_flag |= VFCHG;
|
|
vscreen[sline]->v_flag &= ~VFREQ;
|
|
vtmove(sline, 0);
|
|
if (lp != wp->w_bufp->b_linep) {
|
|
/* if we are not at the end */
|
|
show_line(lp);
|
|
lp = lforw(lp);
|
|
}
|
|
|
|
/* on to the next one */
|
|
#if COLOR
|
|
vscreen[sline]->v_rfcolor = wp->w_fcolor;
|
|
vscreen[sline]->v_rbcolor = wp->w_bcolor;
|
|
#endif
|
|
vteeol();
|
|
++sline;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* updpos:
|
|
* update the position of the hardware cursor and handle extended
|
|
* lines. This is the only update for simple moves.
|
|
*/
|
|
void updpos(void)
|
|
{
|
|
struct line *lp;
|
|
int i;
|
|
|
|
/* find the current row */
|
|
lp = curwp->w_linep;
|
|
currow = curwp->w_toprow;
|
|
while (lp != curwp->w_dotp) {
|
|
++currow;
|
|
lp = lforw(lp);
|
|
}
|
|
|
|
/* find the current column */
|
|
curcol = 0;
|
|
i = 0;
|
|
while (i < curwp->w_doto) {
|
|
unicode_t c;
|
|
|
|
i += utf8_to_unicode( lp->l_text, i, curwp->w_doto, &c) ;
|
|
if( c == '\t')
|
|
curcol |= tabmask ;
|
|
else if( c < 0x20 || c == 0x7F)
|
|
curcol += 1 ; /* displayed as ^c */
|
|
else if( c >= 0x80 && c <= 0xA0)
|
|
curcol += 2 ; /* displayed as \xx */
|
|
|
|
++curcol;
|
|
}
|
|
|
|
/* if extended, flag so and update the virtual line image */
|
|
if (curcol >= term.t_ncol - 1) {
|
|
vscreen[currow]->v_flag |= (VFEXT | VFCHG);
|
|
updext();
|
|
} else
|
|
lbound = 0;
|
|
}
|
|
|
|
/*
|
|
* upddex:
|
|
* de-extend any line that derserves it
|
|
*/
|
|
void upddex(void)
|
|
{
|
|
struct window *wp;
|
|
struct line *lp;
|
|
int i;
|
|
|
|
wp = wheadp;
|
|
|
|
while (wp != NULL) {
|
|
lp = wp->w_linep;
|
|
i = wp->w_toprow;
|
|
|
|
while (i < wp->w_toprow + wp->w_ntrows) {
|
|
if (vscreen[i]->v_flag & VFEXT) {
|
|
if ((wp != curwp) || (lp != wp->w_dotp) ||
|
|
(curcol < term.t_ncol - 1)) {
|
|
vtmove(i, 0);
|
|
show_line(lp);
|
|
vteeol();
|
|
|
|
/* this line no longer is extended */
|
|
vscreen[i]->v_flag &= ~VFEXT;
|
|
vscreen[i]->v_flag |= VFCHG;
|
|
}
|
|
}
|
|
lp = lforw(lp);
|
|
++i;
|
|
}
|
|
/* and onward to the next window */
|
|
wp = wp->w_wndp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* updgar:
|
|
* if the screen is garbage, clear the physical screen and
|
|
* the virtual screen and force a full update
|
|
*/
|
|
void updgar(void)
|
|
{
|
|
unicode_t *txt;
|
|
int i, j;
|
|
|
|
for (i = 0; i < term.t_nrow; ++i) {
|
|
vscreen[i]->v_flag |= VFCHG;
|
|
#if REVSTA
|
|
vscreen[i]->v_flag &= ~VFREV;
|
|
#endif
|
|
#if COLOR
|
|
vscreen[i]->v_fcolor = gfcolor;
|
|
vscreen[i]->v_bcolor = gbcolor;
|
|
#endif
|
|
#if MEMMAP == 0 || SCROLLCODE
|
|
txt = pscreen[i]->v_text;
|
|
for (j = 0; j < term.t_ncol; ++j)
|
|
txt[j] = ' ';
|
|
#endif
|
|
}
|
|
|
|
movecursor(0, 0); /* Erase the screen. */
|
|
(*term.t_eeop) ();
|
|
sgarbf = FALSE; /* Erase-page clears */
|
|
mpresf = FALSE; /* the message area. */
|
|
#if COLOR
|
|
mlerase(); /* needs to be cleared if colored */
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* updupd:
|
|
* update the physical screen from the virtual screen
|
|
*
|
|
* int force; forced update flag
|
|
*/
|
|
int updupd(int force)
|
|
{
|
|
struct video *vp1;
|
|
int i;
|
|
|
|
#if SCROLLCODE
|
|
if (scrflags & WFKILLS)
|
|
scrolls(FALSE);
|
|
if (scrflags & WFINS)
|
|
scrolls(TRUE);
|
|
scrflags = 0;
|
|
#endif
|
|
|
|
for (i = 0; i < term.t_nrow; ++i) {
|
|
vp1 = vscreen[i];
|
|
|
|
/* for each line that needs to be updated */
|
|
if ((vp1->v_flag & VFCHG) != 0) {
|
|
#if TYPEAH && ! PKCODE
|
|
if (force == FALSE && typahead())
|
|
return TRUE;
|
|
#endif
|
|
#if MEMMAP && ! SCROLLCODE
|
|
updateline(i, vp1);
|
|
#else
|
|
updateline(i, vp1, pscreen[i]);
|
|
#endif
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#if SCROLLCODE
|
|
|
|
/*
|
|
* optimize out scrolls (line breaks, and newlines)
|
|
* arg. chooses between looking for inserts or deletes
|
|
*/
|
|
static int scrolls(int inserts)
|
|
{ /* returns true if it does something */
|
|
struct video *vpv; /* virtual screen image */
|
|
struct video *vpp; /* physical screen image */
|
|
int i, j, k;
|
|
int rows, cols;
|
|
int first, match, count, target, end;
|
|
int longmatch, longcount;
|
|
int from, to;
|
|
|
|
if (!term.t_scroll) /* no way to scroll */
|
|
return FALSE;
|
|
|
|
rows = term.t_nrow;
|
|
cols = term.t_ncol;
|
|
|
|
first = -1;
|
|
for (i = 0; i < rows; i++) { /* find first wrong line */
|
|
if (!texttest(i, i)) {
|
|
first = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (first < 0)
|
|
return FALSE; /* no text changes */
|
|
|
|
vpv = vscreen[first];
|
|
vpp = pscreen[first];
|
|
|
|
if (inserts) {
|
|
/* determine types of potential scrolls */
|
|
end = endofline(vpv->v_text, cols);
|
|
if (end == 0)
|
|
target = first; /* newlines */
|
|
else if (memcmp(vpp->v_text, vpv->v_text, 4*end) == 0)
|
|
target = first + 1; /* broken line newlines */
|
|
else
|
|
target = first;
|
|
} else {
|
|
target = first + 1;
|
|
}
|
|
|
|
/* find the matching shifted area */
|
|
match = -1;
|
|
longmatch = -1;
|
|
longcount = 0;
|
|
from = target;
|
|
for (i = from + 1; i < rows - longcount /* P.K. */ ; i++) {
|
|
if (inserts ? texttest(i, from) : texttest(from, i)) {
|
|
match = i;
|
|
count = 1;
|
|
for (j = match + 1, k = from + 1;
|
|
j < rows && k < rows; j++, k++) {
|
|
if (inserts ? texttest(j, k) :
|
|
texttest(k, j))
|
|
count++;
|
|
else
|
|
break;
|
|
}
|
|
if (longcount < count) {
|
|
longcount = count;
|
|
longmatch = match;
|
|
}
|
|
}
|
|
}
|
|
match = longmatch;
|
|
count = longcount;
|
|
|
|
if (!inserts) {
|
|
/* full kill case? */
|
|
if (match > 0 && texttest(first, match - 1)) {
|
|
target--;
|
|
match--;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
/* do the scroll */
|
|
if (match > 0 && count > 2) { /* got a scroll */
|
|
/* move the count lines starting at target to match */
|
|
if (inserts) {
|
|
from = target;
|
|
to = match;
|
|
} else {
|
|
from = match;
|
|
to = target;
|
|
}
|
|
if (2 * count < abs(from - to))
|
|
return FALSE;
|
|
scrscroll(from, to, count);
|
|
for (i = 0; i < count; i++) {
|
|
vpp = pscreen[to + i];
|
|
vpv = vscreen[to + i];
|
|
memcpy(vpp->v_text, vpv->v_text, 4*cols);
|
|
vpp->v_flag = vpv->v_flag; /* XXX */
|
|
if (vpp->v_flag & VFREV) {
|
|
vpp->v_flag &= ~VFREV;
|
|
vpp->v_flag |= ~VFREQ;
|
|
}
|
|
#if MEMMAP
|
|
vscreen[to + i]->v_flag &= ~VFCHG;
|
|
#endif
|
|
}
|
|
if (inserts) {
|
|
from = target;
|
|
to = match;
|
|
} else {
|
|
from = target + count;
|
|
to = match + count;
|
|
}
|
|
#if MEMMAP == 0
|
|
for (i = from; i < to; i++) {
|
|
unicode_t *txt;
|
|
txt = pscreen[i]->v_text;
|
|
for (j = 0; j < term.t_ncol; ++j)
|
|
txt[j] = ' ';
|
|
vscreen[i]->v_flag |= VFCHG;
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* move the "count" lines starting at "from" to "to" */
|
|
static void scrscroll(int from, int to, int count)
|
|
{
|
|
ttrow = ttcol = -1;
|
|
(*term.t_scroll) (from, to, count);
|
|
}
|
|
|
|
/*
|
|
* return TRUE on text match
|
|
*
|
|
* int vrow, prow; virtual, physical rows
|
|
*/
|
|
static int texttest(int vrow, int prow)
|
|
{
|
|
struct video *vpv = vscreen[vrow]; /* virtual screen image */
|
|
struct video *vpp = pscreen[prow]; /* physical screen image */
|
|
|
|
return !memcmp(vpv->v_text, vpp->v_text, 4*term.t_ncol);
|
|
}
|
|
|
|
/*
|
|
* return the index of the first blank of trailing whitespace
|
|
*/
|
|
static int endofline(unicode_t *s, int n)
|
|
{
|
|
int i;
|
|
for (i = n - 1; i >= 0; i--)
|
|
if (s[i] != ' ')
|
|
return i + 1;
|
|
return 0;
|
|
}
|
|
|
|
#endif /* SCROLLCODE */
|
|
|
|
/*
|
|
* updext:
|
|
* update the extended line which the cursor is currently
|
|
* on at a column greater than the terminal width. The line
|
|
* will be scrolled right or left to let the user see where
|
|
* the cursor is
|
|
*/
|
|
static void updext(void)
|
|
{
|
|
int rcursor; /* real cursor location */
|
|
struct line *lp; /* pointer to current line */
|
|
|
|
/* calculate what column the real cursor will end up in */
|
|
rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
|
|
taboff = lbound = curcol - rcursor + 1;
|
|
|
|
/* scan through the line outputing characters to the virtual screen */
|
|
/* once we reach the left edge */
|
|
vtmove(currow, -lbound); /* start scanning offscreen */
|
|
lp = curwp->w_dotp; /* line to output */
|
|
show_line(lp);
|
|
|
|
/* truncate the virtual line, restore tab offset */
|
|
vteeol();
|
|
taboff = 0;
|
|
|
|
/* and put a '$' in column 1 */
|
|
vscreen[currow]->v_text[0] = '$';
|
|
}
|
|
|
|
/*
|
|
* Update a single line. This does not know how to use insert or delete
|
|
* character sequences; we are using VT52 functionality. Update the physical
|
|
* row and column variables. It does try an exploit erase to end of line. The
|
|
* RAINBOW version of this routine uses fast video.
|
|
*/
|
|
#if MEMMAP
|
|
/* UPDATELINE specific code for the IBM-PC and other compatables */
|
|
|
|
static int updateline(int row, struct video *vp1, struct video *vp2)
|
|
{
|
|
#if SCROLLCODE
|
|
unicode_t *cp1;
|
|
unicode_t *cp2;
|
|
int nch;
|
|
|
|
cp1 = &vp1->v_text[0];
|
|
cp2 = &vp2->v_text[0];
|
|
nch = term.t_ncol;
|
|
do {
|
|
*cp2 = *cp1;
|
|
++cp2;
|
|
++cp1;
|
|
}
|
|
while (--nch);
|
|
#endif
|
|
#if COLOR
|
|
scwrite(row, vp1->v_text, vp1->v_rfcolor, vp1->v_rbcolor);
|
|
vp1->v_fcolor = vp1->v_rfcolor;
|
|
vp1->v_bcolor = vp1->v_rbcolor;
|
|
#else
|
|
if (vp1->v_flag & VFREQ)
|
|
scwrite(row, vp1->v_text, 0, 7);
|
|
else
|
|
scwrite(row, vp1->v_text, 7, 0);
|
|
#endif
|
|
vp1->v_flag &= ~(VFCHG | VFCOL); /* flag this line as changed */
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
* updateline()
|
|
*
|
|
* int row; row of screen to update
|
|
* struct video *vp1; virtual screen image
|
|
* struct video *vp2; physical screen image
|
|
*/
|
|
static int updateline(int row, struct video *vp1, struct video *vp2)
|
|
{
|
|
#if RAINBOW
|
|
/* UPDATELINE specific code for the DEC rainbow 100 micro */
|
|
|
|
unicode_t *cp1;
|
|
unicode_t *cp2;
|
|
int nch;
|
|
|
|
/* since we don't know how to make the rainbow do this, turn it off */
|
|
flags &= (~VFREV & ~VFREQ);
|
|
|
|
cp1 = &vp1->v_text[0]; /* Use fast video. */
|
|
cp2 = &vp2->v_text[0];
|
|
putline(row + 1, 1, cp1);
|
|
nch = term.t_ncol;
|
|
|
|
do {
|
|
*cp2 = *cp1;
|
|
++cp2;
|
|
++cp1;
|
|
}
|
|
while (--nch);
|
|
*flags &= ~VFCHG;
|
|
#else
|
|
/* UPDATELINE code for all other versions */
|
|
|
|
unicode_t *cp1;
|
|
unicode_t *cp2;
|
|
unicode_t *cp3;
|
|
unicode_t *cp4;
|
|
unicode_t *cp5;
|
|
int nbflag; /* non-blanks to the right flag? */
|
|
#if REVSTA
|
|
int rev; /* reverse video flag */
|
|
#endif
|
|
int req = FALSE ; /* reverse video request flag */
|
|
|
|
|
|
/* set up pointers to virtual and physical lines */
|
|
cp1 = &vp1->v_text[0];
|
|
cp2 = &vp2->v_text[0];
|
|
|
|
#if COLOR
|
|
TTforg(vp1->v_rfcolor);
|
|
TTbacg(vp1->v_rbcolor);
|
|
#endif
|
|
|
|
#if REVSTA | COLOR
|
|
/* if we need to change the reverse video status of the
|
|
current line, we need to re-write the entire line */
|
|
rev = (vp1->v_flag & VFREV) == VFREV;
|
|
req = (vp1->v_flag & VFREQ) == VFREQ;
|
|
if ((rev != req)
|
|
#if COLOR
|
|
|| (vp1->v_fcolor != vp1->v_rfcolor)
|
|
|| (vp1->v_bcolor != vp1->v_rbcolor)
|
|
#endif
|
|
) {
|
|
movecursor(row, 0); /* Go to start of line. */
|
|
/* set rev video if needed */
|
|
if (rev != req)
|
|
(*term.t_rev) (req);
|
|
|
|
/* scan through the line and dump it to the screen and
|
|
the virtual screen array */
|
|
cp3 = &vp1->v_text[term.t_ncol];
|
|
while (cp1 < cp3) {
|
|
TTputc(*cp1);
|
|
++ttcol;
|
|
*cp2++ = *cp1++;
|
|
}
|
|
/* turn rev video off */
|
|
if (rev != req)
|
|
(*term.t_rev) (FALSE);
|
|
|
|
/* update the needed flags */
|
|
vp1->v_flag &= ~VFCHG;
|
|
if (req)
|
|
vp1->v_flag |= VFREV;
|
|
else
|
|
vp1->v_flag &= ~VFREV;
|
|
#if COLOR
|
|
vp1->v_fcolor = vp1->v_rfcolor;
|
|
vp1->v_bcolor = vp1->v_rbcolor;
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
/* advance past any common chars at the left */
|
|
while (cp1 != &vp1->v_text[term.t_ncol] && cp1[0] == cp2[0]) {
|
|
++cp1;
|
|
++cp2;
|
|
}
|
|
|
|
/* This can still happen, even though we only call this routine on changed
|
|
* lines. A hard update is always done when a line splits, a massive
|
|
* change is done, or a buffer is displayed twice. This optimizes out most
|
|
* of the excess updating. A lot of computes are used, but these tend to
|
|
* be hard operations that do a lot of update, so I don't really care.
|
|
*/
|
|
/* if both lines are the same, no update needs to be done */
|
|
if (cp1 == &vp1->v_text[term.t_ncol]) {
|
|
vp1->v_flag &= ~VFCHG; /* flag this line is changed */
|
|
return TRUE;
|
|
}
|
|
|
|
/* find out if there is a match on the right */
|
|
nbflag = FALSE;
|
|
cp3 = &vp1->v_text[term.t_ncol];
|
|
cp4 = &vp2->v_text[term.t_ncol];
|
|
|
|
while (cp3[-1] == cp4[-1]) {
|
|
--cp3;
|
|
--cp4;
|
|
if (cp3[0] != ' ') /* Note if any nonblank */
|
|
nbflag = TRUE; /* in right match. */
|
|
}
|
|
|
|
cp5 = cp3;
|
|
|
|
/* Erase to EOL ? */
|
|
if (nbflag == FALSE && eolexist == TRUE && (req != TRUE)) {
|
|
while (cp5 != cp1 && cp5[-1] == ' ')
|
|
--cp5;
|
|
|
|
if (cp3 - cp5 <= 3) /* Use only if erase is */
|
|
cp5 = cp3; /* fewer characters. */
|
|
}
|
|
|
|
movecursor(row, cp1 - &vp1->v_text[0]); /* Go to start of line. */
|
|
#if REVSTA
|
|
TTrev(rev);
|
|
#endif
|
|
|
|
while (cp1 != cp5) { /* Ordinary. */
|
|
TTputc(*cp1);
|
|
++ttcol;
|
|
*cp2++ = *cp1++;
|
|
}
|
|
|
|
if (cp5 != cp3) { /* Erase. */
|
|
TTeeol();
|
|
while (cp1 != cp3)
|
|
*cp2++ = *cp1++;
|
|
}
|
|
#if REVSTA
|
|
TTrev(FALSE);
|
|
#endif
|
|
vp1->v_flag &= ~VFCHG; /* flag this line as updated */
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Redisplay the mode line for the window pointed to by the "wp". This is the
|
|
* only routine that has any idea of how the modeline is formatted. You can
|
|
* change the modeline format by hacking at this routine. Called by "update"
|
|
* any time there is a dirty window.
|
|
*/
|
|
static void modeline(struct window *wp)
|
|
{
|
|
int n; /* cursor position count */
|
|
struct buffer *bp;
|
|
int i; /* loop index */
|
|
int lchar; /* character to draw line in buffer with */
|
|
int firstm; /* is this the first mode? */
|
|
|
|
n = wp->w_toprow + wp->w_ntrows; /* Location. */
|
|
vscreen[n]->v_flag |= VFCHG | VFREQ | VFCOL; /* Redraw next time. */
|
|
#if COLOR
|
|
vscreen[n]->v_rfcolor = 0; /* black on */
|
|
vscreen[n]->v_rbcolor = 7; /* white..... */
|
|
#endif
|
|
vtmove(n, 0); /* Seek to right line. */
|
|
if (wp == curwp) /* mark the current buffer */
|
|
#if PKCODE && REVSTA
|
|
lchar = '-';
|
|
#else
|
|
lchar = '=';
|
|
#endif
|
|
else
|
|
#if REVSTA
|
|
if (revexist)
|
|
lchar = ' ';
|
|
else
|
|
#endif
|
|
lchar = '-';
|
|
|
|
bp = wp->w_bufp;
|
|
#if PKCODE == 0
|
|
if ((bp->b_flag & BFTRUNC) != 0)
|
|
vtputc('#');
|
|
else
|
|
#endif
|
|
vtputc(lchar);
|
|
|
|
if ((bp->b_flag & BFCHG) != 0) /* "*" if changed. */
|
|
vtputc('*');
|
|
else
|
|
vtputc(lchar);
|
|
|
|
vtputc( ' ') ;
|
|
|
|
if( n == term.t_nrow - 1)
|
|
n = 3 + vtputs( PROGRAM_NAME_LONG " " VERSION ": ") ;
|
|
else
|
|
n = 3 ;
|
|
|
|
n += vtputs( bp->b_bname) ;
|
|
n += vtputs( " (") ;
|
|
|
|
/* display the modes */
|
|
|
|
if ((bp->b_flag & BFTRUNC) != 0) {
|
|
firstm = FALSE;
|
|
n += vtputs( "Truncated") ;
|
|
} else
|
|
firstm = TRUE ;
|
|
|
|
for (i = 0; i < NUMMODES; i++) /* add in the mode flags */
|
|
if (wp->w_bufp->b_mode & (1 << i)) {
|
|
if (firstm != TRUE)
|
|
n += vtputs( " ") ;
|
|
else
|
|
firstm = FALSE ;
|
|
|
|
n += vtputs( modename[ i]) ;
|
|
}
|
|
|
|
n += vtputs( ") ") ;
|
|
|
|
#if PKCODE
|
|
if (bp->b_fname[0] != 0 && strcmp(bp->b_bname, bp->b_fname) != 0) {
|
|
#else
|
|
if (bp->b_fname[0] != 0) { /* File name. */
|
|
n += vtputs( "File: ") ;
|
|
#endif
|
|
n += vtputs( bp->b_fname) ;
|
|
vtputc(' ');
|
|
++n;
|
|
}
|
|
|
|
while (n < term.t_ncol) { /* Pad to full width. */
|
|
vtputc(lchar);
|
|
++n;
|
|
}
|
|
|
|
{ /* determine if top line, bottom line, or both are visible */
|
|
struct line *lp = wp->w_linep;
|
|
int rows = wp->w_ntrows;
|
|
char *msg = NULL;
|
|
char tline[ 6] ; /* buffer for part of mode line */
|
|
|
|
vtcol = n - 7; /* strlen(" top ") plus a couple */
|
|
while (rows--) {
|
|
lp = lforw(lp);
|
|
if (lp == wp->w_bufp->b_linep) {
|
|
msg = " Bot ";
|
|
break;
|
|
}
|
|
}
|
|
if (lback(wp->w_linep) == wp->w_bufp->b_linep) {
|
|
if (msg) {
|
|
if (wp->w_linep == wp->w_bufp->b_linep)
|
|
msg = " Emp ";
|
|
else
|
|
msg = " All ";
|
|
} else {
|
|
msg = " Top ";
|
|
}
|
|
}
|
|
if (!msg) {
|
|
struct line *lp;
|
|
int numlines, predlines ;
|
|
|
|
lp = lforw(bp->b_linep);
|
|
numlines = 0;
|
|
predlines = 0;
|
|
while (lp != bp->b_linep) {
|
|
if (lp == wp->w_linep) {
|
|
predlines = numlines;
|
|
}
|
|
++numlines;
|
|
lp = lforw(lp);
|
|
}
|
|
if (wp->w_dotp == bp->b_linep) {
|
|
msg = " Bot ";
|
|
} else {
|
|
int ratio = 0 ;
|
|
|
|
if (numlines != 0)
|
|
ratio =
|
|
(100L * predlines) / numlines;
|
|
if (ratio > 99)
|
|
ratio = 99;
|
|
|
|
tline[ 0] = ' ' ;
|
|
tline[ 1] = ratio / 10 + '0' ;
|
|
tline[ 2] = ratio % 10 + '0' ;
|
|
tline[ 3] = '%' ;
|
|
tline[ 4] = ' ' ;
|
|
tline[ 5] = 0 ;
|
|
if( tline[ 1] == '0')
|
|
tline[ 1] = ' ' ;
|
|
|
|
msg = tline;
|
|
}
|
|
}
|
|
|
|
n += vtputs( msg) ;
|
|
}
|
|
}
|
|
|
|
void upmode(void)
|
|
{ /* update all the mode lines */
|
|
struct window *wp;
|
|
|
|
wp = wheadp;
|
|
while (wp != NULL) {
|
|
wp->w_flag |= WFMODE;
|
|
wp = wp->w_wndp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send a command to the terminal to move the hardware cursor to row "row"
|
|
* and column "col". The row and column arguments are origin 0. Optimize out
|
|
* random calls. Update "ttrow" and "ttcol".
|
|
*/
|
|
void movecursor(int row, int col)
|
|
{
|
|
if (row != ttrow || col != ttcol) {
|
|
ttrow = row;
|
|
ttcol = col;
|
|
TTmove(row, col);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Erase the message line. This is a special routine because the message line
|
|
* is not considered to be part of the virtual screen. It always works
|
|
* immediately; the terminal buffer is flushed via a call to the flusher.
|
|
*/
|
|
void mlerase( void) {
|
|
movecursor( term.t_nrow, 0) ;
|
|
if( discmd != FALSE) {
|
|
#if COLOR
|
|
TTforg( 7) ;
|
|
TTbacg( 0) ;
|
|
#endif
|
|
if( eolexist == TRUE)
|
|
TTeeol() ;
|
|
else {
|
|
for( ttcol = 0 ; ttcol < term.t_ncol ; ttcol++)
|
|
TTputc( ' ') ;
|
|
|
|
movecursor( term.t_nrow, 0) ;
|
|
}
|
|
|
|
mpresf = FALSE ;
|
|
}
|
|
|
|
TTflush() ;
|
|
}
|
|
|
|
static void mlputc( char c) {
|
|
if( ttcol < term.t_ncol) {
|
|
TTputc( c) ;
|
|
++ttcol ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write a message into the message line. Keep track of the physical cursor
|
|
* position. A small class of printf like format items is handled. Assumes the
|
|
* stack grows down; this assumption is made by the "++" in the argument scan
|
|
* loop. Set the "message line" flag TRUE.
|
|
*
|
|
* char *fmt; format string for output
|
|
* char *arg; pointer to first argument to print
|
|
*/
|
|
void vmlwrite( const char *fmt, va_list ap) {
|
|
int c; /* current char in format string */
|
|
|
|
/* if we are not currently echoing on the command line, abort this */
|
|
if (discmd == FALSE) {
|
|
movecursor(term.t_nrow, 0);
|
|
return;
|
|
}
|
|
#if COLOR
|
|
/* set up the proper colors for the command line */
|
|
TTforg(7);
|
|
TTbacg(0);
|
|
#endif
|
|
|
|
/* if we can not erase to end-of-line, do it manually */
|
|
if( eolexist == FALSE)
|
|
mlerase() ; /* ends with movecursor( term.t_nrow, 0) and TTflush() */
|
|
else
|
|
movecursor( term.t_nrow, 0) ;
|
|
|
|
mpresf = *fmt ? TRUE : FALSE ; /* flag if line has content or not */
|
|
while ((c = *fmt++) != 0) {
|
|
if (c != '%')
|
|
mlputc( c) ;
|
|
else {
|
|
c = *fmt++;
|
|
switch (c) {
|
|
case 'd':
|
|
mlputi(va_arg(ap, int), 10);
|
|
break;
|
|
|
|
case 'o':
|
|
mlputi(va_arg(ap, int), 8);
|
|
break;
|
|
|
|
case 'x':
|
|
mlputi(va_arg(ap, int), 16);
|
|
break;
|
|
|
|
case 'D':
|
|
mlputli(va_arg(ap, long), 10);
|
|
break;
|
|
|
|
case 's':
|
|
mlputs( (unsigned char *) va_arg( ap, char *)) ;
|
|
break;
|
|
|
|
case 'f':
|
|
mlputf(va_arg(ap, int));
|
|
break;
|
|
|
|
case 'B': /* ring a bell */
|
|
TTbeep() ;
|
|
break ;
|
|
|
|
default:
|
|
mlputc( c) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if we can, erase to the end of screen */
|
|
if( eolexist == TRUE && ttcol < term.t_ncol)
|
|
TTeeol() ;
|
|
|
|
TTflush();
|
|
}
|
|
|
|
void mlwrite( const char *fmt, ...) {
|
|
va_list ap ;
|
|
|
|
va_start( ap, fmt) ;
|
|
vmlwrite( fmt, ap) ;
|
|
va_end( ap) ;
|
|
}
|
|
|
|
/*
|
|
* Write out a string. Update the physical cursor position. This assumes that
|
|
* the characters in the string all have width "1"; if this is not the case
|
|
* things will get screwed up a little.
|
|
*/
|
|
static void mlputs( unsigned char *s) {
|
|
unicode_t c ;
|
|
|
|
while( ((c = *s++) != 0) && (ttcol < term.t_ncol)) {
|
|
if( c == '\t') /* Don't render tabulation */
|
|
c = ' ' ;
|
|
else if( c > 0xC1 && c <= 0xF4) { /* Accept UTF-8 sequence */
|
|
char utf[ 4] ;
|
|
char cc ;
|
|
int bytes ;
|
|
|
|
utf[ 0] = c ;
|
|
utf[ 1] = cc = *s ;
|
|
if( (c & 0x20) && ((cc & 0xC0) == 0x80)) { /* at least 3 bytes and a valid encoded char */
|
|
utf[ 2] = cc = s[ 1] ;
|
|
if( (c & 0x10) && ((cc & 0xC0) == 0x80)) /* at least 4 bytes and a valid encoded char */
|
|
utf[ 3] = s[ 2] ;
|
|
}
|
|
|
|
bytes = utf8_to_unicode( utf, 0, sizeof utf, (unicode_t *) &c) ;
|
|
s += bytes - 1 ;
|
|
}
|
|
|
|
TTputc( c) ;
|
|
++ttcol ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write out an integer, in the specified radix. Update the physical cursor
|
|
* position.
|
|
*/
|
|
static void mlputi( int i, int r) {
|
|
int q ;
|
|
unsigned u ;
|
|
static char hexdigits[] = "0123456789ABCDEF" ;
|
|
|
|
if( r == 16 || i >= 0)
|
|
u = i ;
|
|
else {
|
|
u = -i ;
|
|
mlputc( '-') ;
|
|
}
|
|
|
|
q = u / r ;
|
|
|
|
if( q != 0)
|
|
mlputi( q, r) ;
|
|
|
|
mlputc( hexdigits[ u % r]) ;
|
|
}
|
|
|
|
/*
|
|
* do the same except as a long integer.
|
|
*/
|
|
static void mlputli( long l, int r) {
|
|
long q ;
|
|
|
|
if( l < 0) {
|
|
l = -l ;
|
|
mlputc( '-') ;
|
|
}
|
|
|
|
q = l / r ;
|
|
|
|
if( q != 0)
|
|
mlputli( q, r) ;
|
|
|
|
mlputc( (int) (l % r) + '0') ;
|
|
}
|
|
|
|
/*
|
|
* write out a scaled integer with two decimal places
|
|
*
|
|
* int s; scaled integer to output
|
|
*/
|
|
static void mlputf( int s) {
|
|
int i ; /* integer portion of number */
|
|
int f ; /* fractional portion of number */
|
|
|
|
/* break it up */
|
|
i = s / 100 ;
|
|
f = s % 100 ;
|
|
|
|
/* send out the integer portion */
|
|
mlputi( i, 10) ;
|
|
mlputc('.') ;
|
|
mlputc((f / 10) + '0') ;
|
|
mlputc((f % 10) + '0') ;
|
|
}
|
|
|
|
#if RAINBOW
|
|
|
|
static void putline(int row, int col, char *buf)
|
|
{
|
|
int n;
|
|
|
|
n = strlen(buf);
|
|
if (col + n - 1 > term.t_ncol)
|
|
n = term.t_ncol - col + 1;
|
|
Put_Data(row, col, n, buf);
|
|
}
|
|
#endif
|
|
|
|
/* Get terminal size from system.
|
|
Store number of lines into *heightp and width into *widthp.
|
|
If zero or a negative number is stored, the value is not valid. */
|
|
|
|
void getscreensize(int *widthp, int *heightp)
|
|
{
|
|
#ifdef TIOCGWINSZ
|
|
struct winsize size;
|
|
*widthp = 0;
|
|
*heightp = 0;
|
|
if (ioctl(0, TIOCGWINSZ, &size) < 0)
|
|
return;
|
|
*widthp = size.ws_col;
|
|
*heightp = size.ws_row;
|
|
#else
|
|
*widthp = 0;
|
|
*heightp = 0;
|
|
#endif
|
|
}
|
|
|
|
#ifdef SIGWINCH
|
|
void sizesignal(int signr)
|
|
{
|
|
int w, h;
|
|
int old_errno = errno;
|
|
|
|
getscreensize(&w, &h);
|
|
|
|
if( h > 0 && w > 0) {
|
|
term.t_mrow = h = h < MAXROW ? h : MAXROW ;
|
|
term.t_mcol = w = w < MAXCOL ? w : MAXCOL ;
|
|
if( h - 1 != term.t_nrow || w != term.t_ncol)
|
|
newscreensize( h, w) ;
|
|
}
|
|
|
|
signal(SIGWINCH, sizesignal);
|
|
errno = old_errno;
|
|
}
|
|
|
|
static int newscreensize(int h, int w)
|
|
{
|
|
/* do the change later */
|
|
if (displaying) {
|
|
chg_width = w;
|
|
chg_height = h;
|
|
return FALSE;
|
|
}
|
|
chg_width = chg_height = 0;
|
|
if( h <= term.t_mrow)
|
|
newsize(TRUE, h);
|
|
if( w <= term.t_mcol)
|
|
newwidth(TRUE, w);
|
|
|
|
update(TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|