4083 lines
86 KiB
C
4083 lines
86 KiB
C
/* Copyright (c) 2008, 2009
|
|
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
|
|
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
|
|
* Micah Cowan (micah@cowan.name)
|
|
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
|
|
* Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
|
|
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
|
|
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
|
|
* Copyright (c) 1987 Oliver Laumann
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (see the file COPYING); if not, see
|
|
* https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
|
|
*
|
|
****************************************************************
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#ifndef sun
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#include "config.h"
|
|
#include "screen.h"
|
|
#include "extern.h"
|
|
#include "braille.h"
|
|
#include "canvas.h"
|
|
|
|
/* CSI parsing status */
|
|
enum
|
|
{
|
|
CSI_PB=0, CSI_PX=1, CSI_PY=2, CSI_DONE=3,
|
|
CSI_ESC_SEEN, CSI_BEGIN, CSI_INACTIVE, CSI_INVALID
|
|
};
|
|
|
|
static int CountChars __P((int));
|
|
static int DoAddChar __P((int));
|
|
static int BlankResize __P((int, int));
|
|
static int CallRewrite __P((int, int, int, int));
|
|
static void disp_readev_fn __P((struct event *, char *));
|
|
static void disp_processinput __P((struct display *, unsigned char *, int));
|
|
static void disp_writeev_fn __P((struct event *, char *));
|
|
#ifdef linux
|
|
static void disp_writeev_eagain __P((struct event *, char *));
|
|
#endif
|
|
static void disp_status_fn __P((struct event *, char *));
|
|
static void disp_hstatus_fn __P((struct event *, char *));
|
|
static void disp_blocked_fn __P((struct event *, char *));
|
|
#ifdef MAPKEYS
|
|
static void disp_map_fn __P((struct event *, char *));
|
|
#endif
|
|
static void disp_idle_fn __P((struct event *, char *));
|
|
#ifdef BLANKER_PRG
|
|
static void disp_blanker_fn __P((struct event *, char *));
|
|
#endif
|
|
static void WriteLP __P((int, int));
|
|
static void INSERTCHAR __P((int));
|
|
static void RAW_PUTCHAR __P((int));
|
|
#ifdef COLOR
|
|
static void SetBackColor __P((int));
|
|
#endif
|
|
static void RemoveStatusMinWait __P((void));
|
|
|
|
|
|
extern struct layer *flayer;
|
|
extern struct win *windows, *fore;
|
|
extern struct LayFuncs WinLf;
|
|
|
|
extern int use_hardstatus;
|
|
extern int MsgWait, MsgMinWait;
|
|
extern const int Z0width, Z1width;
|
|
extern unsigned char *blank, *null;
|
|
extern struct mline mline_blank, mline_null, mline_old;
|
|
extern struct mchar mchar_null, mchar_blank, mchar_so;
|
|
extern struct NewWindow nwin_default;
|
|
extern struct action idleaction;
|
|
|
|
/* XXX shouldn't be here */
|
|
extern char *hstatusstring;
|
|
extern char *captionstring;
|
|
|
|
extern int pastefont;
|
|
extern int idletimo;
|
|
|
|
#ifdef BLANKER_PRG
|
|
extern int pty_preopen;
|
|
#if defined(TIOCSWINSZ) || defined(TIOCGWINSZ)
|
|
extern struct winsize glwz;
|
|
#endif
|
|
extern char **NewEnv;
|
|
extern int real_uid, real_gid;
|
|
extern int ServerSocket, eff_uid, eff_gid;
|
|
#endif
|
|
|
|
/*
|
|
* tputs needs this to calculate the padding
|
|
*/
|
|
#ifndef NEED_OSPEED
|
|
extern
|
|
#endif /* NEED_OSPEED */
|
|
short ospeed;
|
|
|
|
|
|
struct display *display, *displays;
|
|
#ifdef COLOR
|
|
int attr2color[8][4];
|
|
int nattr2color;
|
|
#endif
|
|
|
|
#ifndef MULTI
|
|
struct display TheDisplay;
|
|
#endif
|
|
|
|
/*
|
|
* The default values
|
|
*/
|
|
int defobuflimit = OBUF_MAX;
|
|
int defnonblock = -1;
|
|
int defmousetrack = 0;
|
|
#ifdef AUTO_NUKE
|
|
int defautonuke = 0;
|
|
#endif
|
|
int captionalways;
|
|
int hardstatusemu = HSTATUS_IGNORE;
|
|
|
|
int focusminwidth, focusminheight;
|
|
|
|
/*
|
|
* Default layer management
|
|
*/
|
|
|
|
void
|
|
DefProcess(bufp, lenp)
|
|
char **bufp;
|
|
int *lenp;
|
|
{
|
|
*bufp += *lenp;
|
|
*lenp = 0;
|
|
}
|
|
|
|
void
|
|
DefRedisplayLine(y, xs, xe, isblank)
|
|
int y, xs, xe, isblank;
|
|
{
|
|
if (isblank == 0 && y >= 0)
|
|
DefClearLine(y, xs, xe, 0);
|
|
}
|
|
|
|
void
|
|
DefClearLine(y, xs, xe, bce)
|
|
int y, xs, xe, bce;
|
|
{
|
|
LClearLine(flayer, y, xs, xe, bce, (struct mline *)0);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
DefRewrite(y, xs, xe, rend, doit)
|
|
int y, xs, xe, doit;
|
|
struct mchar *rend;
|
|
{
|
|
return EXPENSIVE;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
DefResize(wi, he)
|
|
int wi, he;
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
DefRestore()
|
|
{
|
|
LAY_DISPLAYS(flayer, InsertMode(0));
|
|
/* ChangeScrollRegion(0, D_height - 1); */
|
|
LKeypadMode(flayer, 0);
|
|
LCursorkeysMode(flayer, 0);
|
|
LCursorVisibility(flayer, 0);
|
|
LMouseMode(flayer, 0);
|
|
LSetRendition(flayer, &mchar_null);
|
|
LSetFlow(flayer, nwin_default.flowflag & FLOW_NOW);
|
|
}
|
|
|
|
/*
|
|
* Blank layer management
|
|
*/
|
|
|
|
struct LayFuncs BlankLf =
|
|
{
|
|
DefProcess,
|
|
0,
|
|
DefRedisplayLine,
|
|
DefClearLine,
|
|
DefRewrite,
|
|
BlankResize,
|
|
DefRestore,
|
|
0
|
|
};
|
|
|
|
/*ARGSUSED*/
|
|
static int
|
|
BlankResize(wi, he)
|
|
int wi, he;
|
|
{
|
|
flayer->l_width = wi;
|
|
flayer->l_height = he;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Generate new display, start with a blank layer.
|
|
* The termcap arrays are not initialised here.
|
|
* The new display is placed in the displays list.
|
|
*/
|
|
|
|
struct display *
|
|
MakeDisplay(uname, utty, term, fd, pid, Mode)
|
|
char *uname, *utty, *term;
|
|
int fd, pid;
|
|
struct mode *Mode;
|
|
{
|
|
struct acluser **u;
|
|
struct baud_values *b;
|
|
|
|
if (!*(u = FindUserPtr(uname)) && UserAdd(uname, (char *)0, u))
|
|
return 0; /* could not find or add user */
|
|
|
|
#ifdef MULTI
|
|
if ((display = (struct display *)calloc(1, sizeof(*display))) == 0)
|
|
return 0;
|
|
#else
|
|
if (displays)
|
|
return 0;
|
|
bzero((char *)&TheDisplay, sizeof(TheDisplay));
|
|
display = &TheDisplay;
|
|
#endif
|
|
display->d_next = displays;
|
|
displays = display;
|
|
D_flow = 1;
|
|
D_nonblock = defnonblock;
|
|
D_userfd = fd;
|
|
D_readev.fd = D_writeev.fd = fd;
|
|
D_readev.type = EV_READ;
|
|
D_writeev.type = EV_WRITE;
|
|
D_readev.data = D_writeev.data = (char *)display;
|
|
D_readev.handler = disp_readev_fn;
|
|
D_writeev.handler = disp_writeev_fn;
|
|
evenq(&D_readev);
|
|
D_writeev.condpos = &D_obuflen;
|
|
D_writeev.condneg = &D_obuffree;
|
|
evenq(&D_writeev);
|
|
D_statusev.type = EV_TIMEOUT;
|
|
D_statusev.data = (char *)display;
|
|
D_statusev.handler = disp_status_fn;
|
|
D_hstatusev.type = EV_TIMEOUT;
|
|
D_hstatusev.data = (char *)display;
|
|
D_hstatusev.handler = disp_hstatus_fn;
|
|
D_blockedev.type = EV_TIMEOUT;
|
|
D_blockedev.data = (char *)display;
|
|
D_blockedev.handler = disp_blocked_fn;
|
|
D_blockedev.condpos = &D_obuffree;
|
|
D_blockedev.condneg = &D_obuflenmax;
|
|
D_hstatusev.handler = disp_hstatus_fn;
|
|
#ifdef MAPKEYS
|
|
D_mapev.type = EV_TIMEOUT;
|
|
D_mapev.data = (char *)display;
|
|
D_mapev.handler = disp_map_fn;
|
|
#endif
|
|
D_idleev.type = EV_TIMEOUT;
|
|
D_idleev.data = (char *)display;
|
|
D_idleev.handler = disp_idle_fn;
|
|
#ifdef BLANKER_PRG
|
|
D_blankerev.type = EV_READ;
|
|
D_blankerev.data = (char *)display;
|
|
D_blankerev.handler = disp_blanker_fn;
|
|
D_blankerev.fd = -1;
|
|
#endif
|
|
D_OldMode = *Mode;
|
|
D_status_obuffree = -1;
|
|
Resize_obuf(); /* Allocate memory for buffer */
|
|
D_obufmax = defobuflimit;
|
|
D_obuflenmax = D_obuflen - D_obufmax;
|
|
#ifdef AUTO_NUKE
|
|
D_auto_nuke = defautonuke;
|
|
#endif
|
|
D_obufp = D_obuf;
|
|
D_printfd = -1;
|
|
D_userpid = pid;
|
|
|
|
#ifdef POSIX
|
|
if ((b = lookup_baud((int)cfgetospeed(&D_OldMode.tio))))
|
|
D_dospeed = b->idx;
|
|
#else
|
|
# ifdef TERMIO
|
|
if ((b = lookup_baud(D_OldMode.tio.c_cflag & CBAUD)))
|
|
D_dospeed = b->idx;
|
|
# else
|
|
D_dospeed = (short)D_OldMode.m_ttyb.sg_ospeed;
|
|
# endif
|
|
#endif
|
|
debug1("New displays ospeed = %d\n", D_dospeed);
|
|
|
|
strncpy(D_usertty, utty, sizeof(D_usertty) - 1);
|
|
D_usertty[sizeof(D_usertty) - 1] = 0;
|
|
strncpy(D_termname, term, MAXTERMLEN);
|
|
D_termname[MAXTERMLEN] = 0;
|
|
D_user = *u;
|
|
D_processinput = ProcessInput;
|
|
D_mousetrack = defmousetrack;
|
|
return display;
|
|
}
|
|
|
|
|
|
void
|
|
FreeDisplay()
|
|
{
|
|
struct win *p;
|
|
#ifdef MULTI
|
|
struct display *d, **dp;
|
|
#endif
|
|
|
|
#ifdef FONT
|
|
FreeTransTable();
|
|
#endif
|
|
#ifdef BLANKER_PRG
|
|
KillBlanker();
|
|
#endif
|
|
if (D_userfd >= 0)
|
|
{
|
|
Flush(3);
|
|
if (!display)
|
|
return;
|
|
SetTTY(D_userfd, &D_OldMode);
|
|
fcntl(D_userfd, F_SETFL, 0);
|
|
}
|
|
freetty();
|
|
if (D_tentry)
|
|
free(D_tentry);
|
|
D_tentry = 0;
|
|
if (D_processinputdata)
|
|
free(D_processinputdata);
|
|
D_processinputdata = 0;
|
|
D_tcinited = 0;
|
|
evdeq(&D_hstatusev);
|
|
evdeq(&D_statusev);
|
|
evdeq(&D_readev);
|
|
evdeq(&D_writeev);
|
|
evdeq(&D_blockedev);
|
|
#ifdef MAPKEYS
|
|
evdeq(&D_mapev);
|
|
if (D_kmaps)
|
|
{
|
|
free(D_kmaps);
|
|
D_kmaps = 0;
|
|
D_aseqs = 0;
|
|
D_nseqs = 0;
|
|
D_seqp = 0;
|
|
D_seql = 0;
|
|
D_seqh = 0;
|
|
}
|
|
#endif
|
|
evdeq(&D_idleev);
|
|
#ifdef BLANKER_PRG
|
|
evdeq(&D_blankerev);
|
|
#endif
|
|
#ifdef HAVE_BRAILLE
|
|
if (bd.bd_dpy == display)
|
|
{
|
|
bd.bd_start_braille = 0;
|
|
StartBraille();
|
|
}
|
|
#endif
|
|
|
|
#ifdef MULTI
|
|
for (dp = &displays; (d = *dp) ; dp = &d->d_next)
|
|
if (d == display)
|
|
break;
|
|
ASSERT(d);
|
|
if (D_status_lastmsg)
|
|
free(D_status_lastmsg);
|
|
if (D_obuf)
|
|
free(D_obuf);
|
|
*dp = display->d_next;
|
|
#else /* MULTI */
|
|
ASSERT(display == displays);
|
|
ASSERT(display == &TheDisplay);
|
|
displays = 0;
|
|
#endif /* MULTI */
|
|
|
|
while (D_canvas.c_slperp)
|
|
FreeCanvas(D_canvas.c_slperp);
|
|
D_cvlist = 0;
|
|
|
|
for (p = windows; p; p = p->w_next)
|
|
{
|
|
if (p->w_pdisplay == display)
|
|
p->w_pdisplay = 0;
|
|
if (p->w_lastdisp == display)
|
|
p->w_lastdisp = 0;
|
|
if (p->w_readev.condneg == &D_status || p->w_readev.condneg == &D_obuflenmax)
|
|
p->w_readev.condpos = p->w_readev.condneg = 0;
|
|
}
|
|
#ifdef ZMODEM
|
|
for (p = windows; p; p = p->w_next)
|
|
if (p->w_zdisplay == display)
|
|
zmodem_abort(p, 0);
|
|
#endif
|
|
if (D_mousetrack)
|
|
{
|
|
D_mousetrack = 0;
|
|
MouseMode(0);
|
|
}
|
|
#ifdef MULTI
|
|
free((char *)display);
|
|
#endif
|
|
display = 0;
|
|
}
|
|
|
|
/*
|
|
* if the adaptflag is on, we keep the size of this display, else
|
|
* we may try to restore our old window sizes.
|
|
*/
|
|
void
|
|
InitTerm(adapt)
|
|
int adapt;
|
|
{
|
|
ASSERT(display);
|
|
ASSERT(D_tcinited);
|
|
D_top = D_bot = -1;
|
|
AddCStr(D_IS);
|
|
AddCStr(D_TI);
|
|
/* Check for toggle */
|
|
if (D_IM && strcmp(D_IM, D_EI))
|
|
AddCStr(D_EI);
|
|
D_insert = 0;
|
|
#ifdef MAPKEYS
|
|
AddCStr(D_KS);
|
|
AddCStr(D_CCS);
|
|
#else
|
|
/* Check for toggle */
|
|
if (D_KS && strcmp(D_KS, D_KE))
|
|
AddCStr(D_KE);
|
|
if (D_CCS && strcmp(D_CCS, D_CCE))
|
|
AddCStr(D_CCE);
|
|
#endif
|
|
D_keypad = 0;
|
|
D_cursorkeys = 0;
|
|
AddCStr(D_ME);
|
|
AddCStr(D_EA);
|
|
AddCStr(D_CE0);
|
|
D_rend = mchar_null;
|
|
D_atyp = 0;
|
|
if (adapt == 0)
|
|
ResizeDisplay(D_defwidth, D_defheight);
|
|
ChangeScrollRegion(0, D_height - 1);
|
|
D_x = D_y = 0;
|
|
Flush(3);
|
|
ClearAll();
|
|
debug1("we %swant to adapt all our windows to the display\n",
|
|
(adapt) ? "" : "don't ");
|
|
/* In case the size was changed by a init sequence */
|
|
CheckScreenSize((adapt) ? 2 : 0);
|
|
}
|
|
|
|
void
|
|
FinitTerm()
|
|
{
|
|
ASSERT(display);
|
|
#ifdef BLANKER_PRG
|
|
KillBlanker();
|
|
#endif
|
|
if (D_tcinited)
|
|
{
|
|
ResizeDisplay(D_defwidth, D_defheight);
|
|
InsertMode(0);
|
|
ChangeScrollRegion(0, D_height - 1);
|
|
KeypadMode(0);
|
|
CursorkeysMode(0);
|
|
CursorVisibility(0);
|
|
if (D_mousetrack)
|
|
D_mousetrack = 0;
|
|
MouseMode(0);
|
|
ExtMouseMode(0);
|
|
SetRendition(&mchar_null);
|
|
SetFlow(FLOW_NOW);
|
|
#ifdef MAPKEYS
|
|
AddCStr(D_KE);
|
|
AddCStr(D_CCE);
|
|
#endif
|
|
if (D_hstatus)
|
|
ShowHStatus((char *)0);
|
|
#ifdef RXVT_OSC
|
|
ClearAllXtermOSC();
|
|
#endif
|
|
D_x = D_y = -1;
|
|
GotoPos(0, D_height - 1);
|
|
AddChar('\r');
|
|
AddChar('\n');
|
|
AddCStr(D_TE);
|
|
}
|
|
Flush(3);
|
|
}
|
|
|
|
|
|
static void
|
|
INSERTCHAR(c)
|
|
int c;
|
|
{
|
|
ASSERT(display);
|
|
if (!D_insert && D_x < D_width - 1)
|
|
{
|
|
if (D_IC || D_CIC)
|
|
{
|
|
if (D_IC)
|
|
AddCStr(D_IC);
|
|
else
|
|
AddCStr2(D_CIC, 1);
|
|
RAW_PUTCHAR(c);
|
|
return;
|
|
}
|
|
InsertMode(1);
|
|
if (!D_insert)
|
|
{
|
|
RefreshLine(D_y, D_x, D_width-1, 0);
|
|
return;
|
|
}
|
|
}
|
|
RAW_PUTCHAR(c);
|
|
}
|
|
|
|
void
|
|
PUTCHAR(c)
|
|
int c;
|
|
{
|
|
ASSERT(display);
|
|
if (D_insert && D_x < D_width - 1)
|
|
InsertMode(0);
|
|
RAW_PUTCHAR(c);
|
|
}
|
|
|
|
void
|
|
PUTCHARLP(c)
|
|
int c;
|
|
{
|
|
if (D_x < D_width - 1)
|
|
{
|
|
if (D_insert)
|
|
InsertMode(0);
|
|
RAW_PUTCHAR(c);
|
|
return;
|
|
}
|
|
if (D_CLP || D_y != D_bot)
|
|
{
|
|
int y = D_y;
|
|
RAW_PUTCHAR(c);
|
|
if (D_AM && !D_CLP)
|
|
GotoPos(D_width - 1, y);
|
|
return;
|
|
}
|
|
debug("PUTCHARLP: lp_missing!\n");
|
|
D_lp_missing = 1;
|
|
D_rend.image = c;
|
|
D_lpchar = D_rend;
|
|
#ifdef DW_CHARS
|
|
/* XXX -> PutChar ? */
|
|
if (D_mbcs)
|
|
{
|
|
D_lpchar.mbcs = c;
|
|
D_lpchar.image = D_mbcs;
|
|
D_mbcs = 0;
|
|
D_x--;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* RAW_PUTCHAR() is for all text that will be displayed.
|
|
* NOTE: charset Nr. 0 has a conversion table, but c1, c2, ... don't.
|
|
*/
|
|
|
|
STATIC void
|
|
RAW_PUTCHAR(c)
|
|
int c;
|
|
{
|
|
ASSERT(display);
|
|
|
|
#ifdef FONT
|
|
# ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
{
|
|
c = (c & 255) | (unsigned char)D_rend.font << 8 | (unsigned char)D_rend.fontx << 16;
|
|
# ifdef DW_CHARS
|
|
if (D_mbcs)
|
|
{
|
|
c = D_mbcs;
|
|
if (D_x == D_width)
|
|
D_x += D_AM ? 1 : -1;
|
|
D_mbcs = 0;
|
|
}
|
|
else if (utf8_isdouble(c))
|
|
{
|
|
D_mbcs = c;
|
|
D_x++;
|
|
return;
|
|
}
|
|
# endif
|
|
if (c < 32)
|
|
{
|
|
AddCStr2(D_CS0, '0');
|
|
AddChar(c + 0x5f);
|
|
AddCStr(D_CE0);
|
|
goto addedutf8;
|
|
}
|
|
if (c < 0x80)
|
|
{
|
|
if (D_xtable && D_xtable[(int)(unsigned char)D_rend.font] && D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c])
|
|
AddStr(D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c]);
|
|
else
|
|
AddChar(c);
|
|
}
|
|
else
|
|
AddUtf8(c);
|
|
goto addedutf8;
|
|
}
|
|
# endif
|
|
# ifdef DW_CHARS
|
|
if (is_dw_font(D_rend.font))
|
|
{
|
|
int t = c;
|
|
if (D_mbcs == 0)
|
|
{
|
|
D_mbcs = c;
|
|
D_x++;
|
|
return;
|
|
}
|
|
D_x--;
|
|
if (D_x == D_width - 1)
|
|
D_x += D_AM ? 1 : -1;
|
|
c = D_mbcs;
|
|
D_mbcs = t;
|
|
}
|
|
# endif
|
|
# if defined(ENCODINGS) && defined(DW_CHARS)
|
|
if (D_encoding)
|
|
c = PrepareEncodedChar(c);
|
|
# endif
|
|
# ifdef DW_CHARS
|
|
kanjiloop:
|
|
# endif
|
|
if (D_xtable && D_xtable[(int)(unsigned char)D_rend.font] && D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c])
|
|
AddStr(D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c]);
|
|
else
|
|
AddChar(D_rend.font != '0' ? c : D_c0_tab[(int)(unsigned char)c]);
|
|
#else /* FONT */
|
|
AddChar(c);
|
|
#endif /* FONT */
|
|
|
|
#ifdef UTF8
|
|
addedutf8:
|
|
#endif
|
|
if (++D_x >= D_width)
|
|
{
|
|
if (D_AM == 0)
|
|
D_x = D_width - 1;
|
|
else if (!D_CLP || D_x > D_width)
|
|
{
|
|
D_x -= D_width;
|
|
if (D_y < D_height-1 && D_y != D_bot)
|
|
D_y++;
|
|
}
|
|
}
|
|
#ifdef DW_CHARS
|
|
if (D_mbcs)
|
|
{
|
|
c = D_mbcs;
|
|
D_mbcs = 0;
|
|
goto kanjiloop;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
DoAddChar(c)
|
|
int c;
|
|
{
|
|
/* this is for ESC-sequences only (AddChar is a macro) */
|
|
AddChar(c);
|
|
return c;
|
|
}
|
|
|
|
void
|
|
AddCStr(s)
|
|
char *s;
|
|
{
|
|
if (display && s && *s)
|
|
{
|
|
ospeed = D_dospeed;
|
|
tputs(s, 1, DoAddChar);
|
|
}
|
|
}
|
|
|
|
void
|
|
AddCStr2(s, c)
|
|
char *s;
|
|
int c;
|
|
{
|
|
if (display && s && *s)
|
|
{
|
|
ospeed = D_dospeed;
|
|
tputs(tgoto(s, 0, c), 1, DoAddChar);
|
|
}
|
|
}
|
|
|
|
|
|
/* Insert mode is a toggle on some terminals, so we need this hack:
|
|
*/
|
|
void
|
|
InsertMode(on)
|
|
int on;
|
|
{
|
|
if (display && on != D_insert && D_IM)
|
|
{
|
|
D_insert = on;
|
|
if (on)
|
|
AddCStr(D_IM);
|
|
else
|
|
AddCStr(D_EI);
|
|
}
|
|
}
|
|
|
|
/* ...and maybe keypad application mode is a toggle, too:
|
|
*/
|
|
void
|
|
KeypadMode(on)
|
|
int on;
|
|
{
|
|
#ifdef MAPKEYS
|
|
if (display)
|
|
D_keypad = on;
|
|
#else
|
|
if (display && D_keypad != on && D_KS)
|
|
{
|
|
D_keypad = on;
|
|
if (on)
|
|
AddCStr(D_KS);
|
|
else
|
|
AddCStr(D_KE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
CursorkeysMode(on)
|
|
int on;
|
|
{
|
|
#ifdef MAPKEYS
|
|
if (display)
|
|
D_cursorkeys = on;
|
|
#else
|
|
if (display && D_cursorkeys != on && D_CCS)
|
|
{
|
|
D_cursorkeys = on;
|
|
if (on)
|
|
AddCStr(D_CCS);
|
|
else
|
|
AddCStr(D_CCE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ReverseVideo(on)
|
|
int on;
|
|
{
|
|
if (display && D_revvid != on && D_CVR)
|
|
{
|
|
D_revvid = on;
|
|
if (D_revvid)
|
|
AddCStr(D_CVR);
|
|
else
|
|
AddCStr(D_CVN);
|
|
}
|
|
}
|
|
|
|
void
|
|
CursorVisibility(v)
|
|
int v;
|
|
{
|
|
if (display && D_curvis != v)
|
|
{
|
|
if (D_curvis)
|
|
AddCStr(D_VE); /* do this always, just to be safe */
|
|
D_curvis = 0;
|
|
if (v == -1 && D_VI)
|
|
AddCStr(D_VI);
|
|
else if (v == 1 && D_VS)
|
|
AddCStr(D_VS);
|
|
else
|
|
return;
|
|
D_curvis = v;
|
|
}
|
|
}
|
|
|
|
void
|
|
MouseMode(mode)
|
|
int mode;
|
|
{
|
|
if (!display)
|
|
return;
|
|
|
|
if (mode < D_mousetrack)
|
|
mode = D_mousetrack;
|
|
|
|
if (D_mouse != mode)
|
|
{
|
|
char mousebuf[20];
|
|
if (!D_CXT)
|
|
return;
|
|
if (D_mouse)
|
|
{
|
|
sprintf(mousebuf, "\033[?%dl", D_mouse);
|
|
AddStr(mousebuf);
|
|
}
|
|
if (mode)
|
|
{
|
|
sprintf(mousebuf, "\033[?%dh", mode);
|
|
AddStr(mousebuf);
|
|
}
|
|
D_mouse = mode;
|
|
D_mouse_parse.state = CSI_INACTIVE;
|
|
}
|
|
}
|
|
|
|
void
|
|
ExtMouseMode(mode)
|
|
int mode;
|
|
{
|
|
if (display && D_extmouse != mode)
|
|
{
|
|
char mousebuf[20];
|
|
if (!D_CXT)
|
|
return;
|
|
if (D_extmouse)
|
|
{
|
|
sprintf(mousebuf, "\033[?%dl", D_extmouse);
|
|
AddStr(mousebuf);
|
|
}
|
|
if (mode)
|
|
{
|
|
sprintf(mousebuf, "\033[?%dh", mode);
|
|
AddStr(mousebuf);
|
|
}
|
|
D_extmouse = mode;
|
|
D_mouse_parse.state = CSI_INACTIVE;
|
|
}
|
|
}
|
|
|
|
static int StrCost;
|
|
|
|
/* ARGSUSED */
|
|
static int
|
|
CountChars(c)
|
|
int c;
|
|
{
|
|
StrCost++;
|
|
return c;
|
|
}
|
|
|
|
int
|
|
CalcCost(s)
|
|
register char *s;
|
|
{
|
|
ASSERT(display);
|
|
if (s)
|
|
{
|
|
StrCost = 0;
|
|
ospeed = D_dospeed;
|
|
tputs(s, 1, CountChars);
|
|
return StrCost;
|
|
}
|
|
else
|
|
return EXPENSIVE;
|
|
}
|
|
|
|
static int
|
|
CallRewrite(y, xs, xe, doit)
|
|
int y, xs, xe, doit;
|
|
{
|
|
struct canvas *cv, *cvlist, *cvlnext;
|
|
struct viewport *vp;
|
|
struct layer *oldflayer;
|
|
int cost;
|
|
|
|
debug3("CallRewrite %d %d %d\n", y, xs, xe);
|
|
ASSERT(display);
|
|
ASSERT(xe >= xs);
|
|
|
|
vp = 0;
|
|
for (cv = D_cvlist; cv; cv = cv->c_next)
|
|
{
|
|
if (y < cv->c_ys || y > cv->c_ye || xe < cv->c_xs || xs > cv->c_xe)
|
|
continue;
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next)
|
|
if (y >= vp->v_ys && y <= vp->v_ye && xe >= vp->v_xs && xs <= vp->v_xe)
|
|
break;
|
|
if (vp)
|
|
break;
|
|
}
|
|
if (doit)
|
|
{
|
|
oldflayer = flayer;
|
|
flayer = cv->c_layer;
|
|
cvlist = flayer->l_cvlist;
|
|
cvlnext = cv->c_lnext;
|
|
flayer->l_cvlist = cv;
|
|
cv->c_lnext = 0;
|
|
LayRewrite(y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff, &D_rend, 1);
|
|
flayer->l_cvlist = cvlist;
|
|
cv->c_lnext = cvlnext;
|
|
flayer = oldflayer;
|
|
return 0;
|
|
}
|
|
if (cv == 0 || cv->c_layer == 0)
|
|
return EXPENSIVE; /* not found or nothing on it */
|
|
if (xs < vp->v_xs || xe > vp->v_xe)
|
|
return EXPENSIVE; /* crosses viewport boundaries */
|
|
if (y - vp->v_yoff < 0 || y - vp->v_yoff >= cv->c_layer->l_height)
|
|
return EXPENSIVE; /* line not on layer */
|
|
if (xs - vp->v_xoff < 0 || xe - vp->v_xoff >= cv->c_layer->l_width)
|
|
return EXPENSIVE; /* line not on layer */
|
|
#ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
D_rend.font = 0;
|
|
#endif
|
|
oldflayer = flayer;
|
|
flayer = cv->c_layer;
|
|
debug3("Calling Rewrite %d %d %d\n", y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff);
|
|
cost = LayRewrite(y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff, &D_rend, 0);
|
|
flayer = oldflayer;
|
|
if (D_insert)
|
|
cost += D_EIcost + D_IMcost;
|
|
return cost;
|
|
}
|
|
|
|
|
|
void
|
|
GotoPos(x2, y2)
|
|
int x2, y2;
|
|
{
|
|
register int dy, dx, x1, y1;
|
|
register int costx, costy;
|
|
register int m;
|
|
register char *s;
|
|
int CMcost;
|
|
enum move_t xm = M_NONE, ym = M_NONE;
|
|
|
|
if (!display)
|
|
return;
|
|
|
|
x1 = D_x;
|
|
y1 = D_y;
|
|
|
|
if (x1 == D_width)
|
|
{
|
|
if (D_CLP && D_AM)
|
|
x1 = -1; /* don't know how the terminal treats this */
|
|
else
|
|
x1--;
|
|
}
|
|
if (x2 == D_width)
|
|
x2--;
|
|
dx = x2 - x1;
|
|
dy = y2 - y1;
|
|
if (dy == 0 && dx == 0)
|
|
return;
|
|
debug2("GotoPos (%d,%d)", x1, y1);
|
|
debug2(" -> (%d,%d)\n", x2, y2);
|
|
if (!D_MS) /* Safe to move ? */
|
|
SetRendition(&mchar_null);
|
|
if (y1 < 0 /* don't know the y position */
|
|
|| (y2 > D_bot && y1 <= D_bot) /* have to cross border */
|
|
|| (y2 < D_top && y1 >= D_top)) /* of scrollregion ? */
|
|
{
|
|
DoCM:
|
|
if (D_HO && !x2 && !y2)
|
|
AddCStr(D_HO);
|
|
else
|
|
AddCStr(tgoto(D_CM, x2, y2));
|
|
D_x = x2;
|
|
D_y = y2;
|
|
return;
|
|
}
|
|
|
|
/* some scrollregion implementations don't allow movements
|
|
* away from the region. sigh.
|
|
*/
|
|
if ((y1 > D_bot && y2 > y1) || (y1 < D_top && y2 < y1))
|
|
goto DoCM;
|
|
|
|
/* Calculate CMcost */
|
|
if (D_HO && !x2 && !y2)
|
|
s = D_HO;
|
|
else
|
|
s = tgoto(D_CM, x2, y2);
|
|
CMcost = CalcCost(s);
|
|
|
|
/* Calculate the cost to move the cursor to the right x position */
|
|
costx = EXPENSIVE;
|
|
if (x1 >= 0) /* relative x positioning only if we know where we are */
|
|
{
|
|
if (dx > 0)
|
|
{
|
|
if (D_CRI && (dx > 1 || !D_ND))
|
|
{
|
|
costx = CalcCost(tgoto(D_CRI, 0, dx));
|
|
xm = M_CRI;
|
|
}
|
|
if ((m = D_NDcost * dx) < costx)
|
|
{
|
|
costx = m;
|
|
xm = M_RI;
|
|
}
|
|
/* Speedup: dx <= LayRewrite() */
|
|
if (dx < costx && (m = CallRewrite(y1, x1, x2 - 1, 0)) < costx)
|
|
{
|
|
costx = m;
|
|
xm = M_RW;
|
|
}
|
|
}
|
|
else if (dx < 0)
|
|
{
|
|
if (D_CLE && (dx < -1 || !D_BC))
|
|
{
|
|
costx = CalcCost(tgoto(D_CLE, 0, -dx));
|
|
xm = M_CLE;
|
|
}
|
|
if ((m = -dx * D_LEcost) < costx)
|
|
{
|
|
costx = m;
|
|
xm = M_LE;
|
|
}
|
|
}
|
|
else
|
|
costx = 0;
|
|
}
|
|
/* Speedup: LayRewrite() >= x2 */
|
|
if (x2 + D_CRcost < costx && (m = (x2 ? CallRewrite(y1, 0, x2 - 1, 0) : 0) + D_CRcost) < costx)
|
|
{
|
|
costx = m;
|
|
xm = M_CR;
|
|
}
|
|
|
|
/* Check if it is already cheaper to do CM */
|
|
if (costx >= CMcost)
|
|
goto DoCM;
|
|
|
|
/* Calculate the cost to move the cursor to the right y position */
|
|
costy = EXPENSIVE;
|
|
if (dy > 0)
|
|
{
|
|
if (D_CDO && dy > 1) /* DO & NL are always != 0 */
|
|
{
|
|
costy = CalcCost(tgoto(D_CDO, 0, dy));
|
|
ym = M_CDO;
|
|
}
|
|
if ((m = dy * ((x2 == 0) ? D_NLcost : D_DOcost)) < costy)
|
|
{
|
|
costy = m;
|
|
ym = M_DO;
|
|
}
|
|
}
|
|
else if (dy < 0)
|
|
{
|
|
if (D_CUP && (dy < -1 || !D_UP))
|
|
{
|
|
costy = CalcCost(tgoto(D_CUP, 0, -dy));
|
|
ym = M_CUP;
|
|
}
|
|
if ((m = -dy * D_UPcost) < costy)
|
|
{
|
|
costy = m;
|
|
ym = M_UP;
|
|
}
|
|
}
|
|
else
|
|
costy = 0;
|
|
|
|
/* Finally check if it is cheaper to do CM */
|
|
if (costx + costy >= CMcost)
|
|
goto DoCM;
|
|
|
|
switch (xm)
|
|
{
|
|
case M_LE:
|
|
while (dx++ < 0)
|
|
AddCStr(D_BC);
|
|
break;
|
|
case M_CLE:
|
|
AddCStr2(D_CLE, -dx);
|
|
break;
|
|
case M_RI:
|
|
while (dx-- > 0)
|
|
AddCStr(D_ND);
|
|
break;
|
|
case M_CRI:
|
|
AddCStr2(D_CRI, dx);
|
|
break;
|
|
case M_CR:
|
|
AddCStr(D_CR);
|
|
D_x = 0;
|
|
x1 = 0;
|
|
/* FALLTHROUGH */
|
|
case M_RW:
|
|
if (x1 < x2)
|
|
(void) CallRewrite(y1, x1, x2 - 1, 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (ym)
|
|
{
|
|
case M_UP:
|
|
while (dy++ < 0)
|
|
AddCStr(D_UP);
|
|
break;
|
|
case M_CUP:
|
|
AddCStr2(D_CUP, -dy);
|
|
break;
|
|
case M_DO:
|
|
s = (x2 == 0) ? D_NL : D_DO;
|
|
while (dy-- > 0)
|
|
AddCStr(s);
|
|
break;
|
|
case M_CDO:
|
|
AddCStr2(D_CDO, dy);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
D_x = x2;
|
|
D_y = y2;
|
|
}
|
|
|
|
void
|
|
ClearAll()
|
|
{
|
|
ASSERT(display);
|
|
ClearArea(0, 0, 0, D_width - 1, D_width - 1, D_height - 1, 0, 0);
|
|
}
|
|
|
|
void
|
|
ClearArea(x1, y1, xs, xe, x2, y2, bce, uselayfn)
|
|
int x1, y1, xs, xe, x2, y2, bce, uselayfn;
|
|
{
|
|
int y, xxe;
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
|
|
debug2("Clear %d,%d", x1, y1);
|
|
debug2(" %d-%d", xs, xe);
|
|
debug2(" %d,%d", x2, y2);
|
|
debug2(" uselayfn=%d bce=%d\n", uselayfn, bce);
|
|
ASSERT(display);
|
|
if (x1 == D_width)
|
|
x1--;
|
|
if (x2 == D_width)
|
|
x2--;
|
|
if (xs == -1)
|
|
xs = x1;
|
|
if (xe == -1)
|
|
xe = x2;
|
|
if (D_UT) /* Safe to erase ? */
|
|
SetRendition(&mchar_null);
|
|
#ifdef COLOR
|
|
if (D_BE)
|
|
SetBackColor(bce);
|
|
#endif
|
|
if (D_lp_missing && y1 <= D_bot && xe >= D_width - 1)
|
|
{
|
|
if (y2 > D_bot || (y2 == D_bot && x2 >= D_width - 1))
|
|
D_lp_missing = 0;
|
|
}
|
|
if (x2 == D_width - 1 && (xs == 0 || y1 == y2) && xe == D_width - 1 && y2 == D_height - 1 && (!bce || D_BE))
|
|
{
|
|
#ifdef AUTO_NUKE
|
|
if (x1 == 0 && y1 == 0 && D_auto_nuke)
|
|
NukePending();
|
|
#endif
|
|
if (x1 == 0 && y1 == 0 && D_CL)
|
|
{
|
|
AddCStr(D_CL);
|
|
D_y = D_x = 0;
|
|
return;
|
|
}
|
|
/*
|
|
* Workaround a hp700/22 terminal bug. Do not use CD where CE
|
|
* is also appropriate.
|
|
*/
|
|
if (D_CD && (y1 < y2 || !D_CE))
|
|
{
|
|
GotoPos(x1, y1);
|
|
AddCStr(D_CD);
|
|
return;
|
|
}
|
|
}
|
|
if (x1 == 0 && xs == 0 && (xe == D_width - 1 || y1 == y2) && y1 == 0 && D_CCD && (!bce || D_BE))
|
|
{
|
|
GotoPos(x1, y1);
|
|
AddCStr(D_CCD);
|
|
return;
|
|
}
|
|
xxe = xe;
|
|
for (y = y1; y <= y2; y++, x1 = xs)
|
|
{
|
|
if (y == y2)
|
|
xxe = x2;
|
|
if (x1 == 0 && D_CB && (xxe != D_width - 1 || (D_x == xxe && D_y == y)) && (!bce || D_BE))
|
|
{
|
|
GotoPos(xxe, y);
|
|
AddCStr(D_CB);
|
|
continue;
|
|
}
|
|
if (xxe == D_width - 1 && D_CE && (!bce || D_BE))
|
|
{
|
|
GotoPos(x1, y);
|
|
AddCStr(D_CE);
|
|
continue;
|
|
}
|
|
if (uselayfn)
|
|
{
|
|
vp = 0;
|
|
for (cv = D_cvlist; cv; cv = cv->c_next)
|
|
{
|
|
if (y < cv->c_ys || y > cv->c_ye || xxe < cv->c_xs || x1 > cv->c_xe)
|
|
continue;
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next)
|
|
if (y >= vp->v_ys && y <= vp->v_ye && xxe >= vp->v_xs && x1 <= vp->v_xe)
|
|
break;
|
|
if (vp)
|
|
break;
|
|
}
|
|
if (cv && cv->c_layer && x1 >= vp->v_xs && xxe <= vp->v_xe &&
|
|
y - vp->v_yoff >= 0 && y - vp->v_yoff < cv->c_layer->l_height &&
|
|
xxe - vp->v_xoff >= 0 && x1 - vp->v_xoff < cv->c_layer->l_width)
|
|
{
|
|
struct layer *oldflayer = flayer;
|
|
struct canvas *cvlist, *cvlnext;
|
|
flayer = cv->c_layer;
|
|
cvlist = flayer->l_cvlist;
|
|
cvlnext = cv->c_lnext;
|
|
flayer->l_cvlist = cv;
|
|
cv->c_lnext = 0;
|
|
LayClearLine(y - vp->v_yoff, x1 - vp->v_xoff, xxe - vp->v_xoff, bce);
|
|
flayer->l_cvlist = cvlist;
|
|
cv->c_lnext = cvlnext;
|
|
flayer = oldflayer;
|
|
continue;
|
|
}
|
|
}
|
|
ClearLine((struct mline *)0, y, x1, xxe, bce);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* if cur_only > 0, we only redisplay current line, as a full refresh is
|
|
* too expensive over a low baud line.
|
|
*/
|
|
void
|
|
Redisplay(cur_only)
|
|
int cur_only;
|
|
{
|
|
ASSERT(display);
|
|
|
|
/* XXX do em all? */
|
|
InsertMode(0);
|
|
ChangeScrollRegion(0, D_height - 1);
|
|
KeypadMode(0);
|
|
CursorkeysMode(0);
|
|
CursorVisibility(0);
|
|
MouseMode(0);
|
|
ExtMouseMode(0);
|
|
SetRendition(&mchar_null);
|
|
SetFlow(FLOW_NOW);
|
|
|
|
ClearAll();
|
|
#ifdef RXVT_OSC
|
|
RefreshXtermOSC();
|
|
#endif
|
|
if (cur_only > 0 && D_fore)
|
|
RefreshArea(0, D_fore->w_y, D_width - 1, D_fore->w_y, 1);
|
|
else
|
|
RefreshAll(1);
|
|
RefreshHStatus();
|
|
CV_CALL(D_forecv, LayRestore();LaySetCursor());
|
|
}
|
|
|
|
void
|
|
RedisplayDisplays(cur_only)
|
|
int cur_only;
|
|
{
|
|
struct display *olddisplay = display;
|
|
for (display = displays; display; display = display->d_next)
|
|
Redisplay(cur_only);
|
|
display = olddisplay;
|
|
}
|
|
|
|
|
|
/* XXX: use oml! */
|
|
void
|
|
ScrollH(y, xs, xe, n, bce, oml)
|
|
int y, xs, xe, n, bce;
|
|
struct mline *oml;
|
|
{
|
|
int i;
|
|
|
|
if (n == 0)
|
|
return;
|
|
if (xe != D_width - 1)
|
|
{
|
|
RefreshLine(y, xs, xe, 0);
|
|
/* UpdateLine(oml, y, xs, xe); */
|
|
return;
|
|
}
|
|
GotoPos(xs, y);
|
|
if (D_UT)
|
|
SetRendition(&mchar_null);
|
|
#ifdef COLOR
|
|
if (D_BE)
|
|
SetBackColor(bce);
|
|
#endif
|
|
if (n > 0)
|
|
{
|
|
if (n >= xe - xs + 1)
|
|
n = xe - xs + 1;
|
|
if (D_CDC && !(n == 1 && D_DC))
|
|
AddCStr2(D_CDC, n);
|
|
else if (D_DC)
|
|
{
|
|
for (i = n; i--; )
|
|
AddCStr(D_DC);
|
|
}
|
|
else
|
|
{
|
|
RefreshLine(y, xs, xe, 0);
|
|
/* UpdateLine(oml, y, xs, xe); */
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (-n >= xe - xs + 1)
|
|
n = -(xe - xs + 1);
|
|
if (!D_insert)
|
|
{
|
|
if (D_CIC && !(n == -1 && D_IC))
|
|
AddCStr2(D_CIC, -n);
|
|
else if (D_IC)
|
|
{
|
|
for (i = -n; i--; )
|
|
AddCStr(D_IC);
|
|
}
|
|
else if (D_IM)
|
|
{
|
|
InsertMode(1);
|
|
SetRendition(&mchar_null);
|
|
#ifdef COLOR
|
|
SetBackColor(bce);
|
|
#endif
|
|
for (i = -n; i--; )
|
|
INSERTCHAR(' ');
|
|
bce = 0; /* all done */
|
|
}
|
|
else
|
|
{
|
|
/* UpdateLine(oml, y, xs, xe); */
|
|
RefreshLine(y, xs, xe, 0);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetRendition(&mchar_null);
|
|
#ifdef COLOR
|
|
SetBackColor(bce);
|
|
#endif
|
|
for (i = -n; i--; )
|
|
INSERTCHAR(' ');
|
|
bce = 0; /* all done */
|
|
}
|
|
}
|
|
if (bce && !D_BE)
|
|
{
|
|
if (n > 0)
|
|
ClearLine((struct mline *)0, y, xe - n + 1, xe, bce);
|
|
else
|
|
ClearLine((struct mline *)0, y, xs, xs - n - 1, bce);
|
|
}
|
|
if (D_lp_missing && y == D_bot)
|
|
{
|
|
if (n > 0)
|
|
WriteLP(D_width - 1 - n, y);
|
|
D_lp_missing = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
ScrollV(xs, ys, xe, ye, n, bce)
|
|
int xs, ys, xe, ye, n, bce;
|
|
{
|
|
int i;
|
|
int up;
|
|
int oldbot;
|
|
int alok, dlok, aldlfaster;
|
|
int missy = 0;
|
|
|
|
ASSERT(display);
|
|
if (n == 0)
|
|
return;
|
|
if (n >= ye - ys + 1 || -n >= ye - ys + 1)
|
|
{
|
|
ClearArea(xs, ys, xs, xe, xe, ye, bce, 0);
|
|
return;
|
|
}
|
|
if (xs > D_vpxmin || xe < D_vpxmax)
|
|
{
|
|
RefreshArea(xs, ys, xe, ye, 0);
|
|
return;
|
|
}
|
|
|
|
if (D_lp_missing)
|
|
{
|
|
if (D_bot > ye || D_bot < ys)
|
|
missy = D_bot;
|
|
else
|
|
{
|
|
missy = D_bot - n;
|
|
if (missy > ye || missy < ys)
|
|
D_lp_missing = 0;
|
|
}
|
|
}
|
|
|
|
up = 1;
|
|
if (n < 0)
|
|
{
|
|
up = 0;
|
|
n = -n;
|
|
}
|
|
if (n >= ye - ys + 1)
|
|
n = ye - ys + 1;
|
|
|
|
oldbot = D_bot;
|
|
if (ys < D_top || D_bot != ye)
|
|
ChangeScrollRegion(ys, ye);
|
|
alok = (D_AL || D_CAL || (ys >= D_top && ye == D_bot && up));
|
|
dlok = (D_DL || D_CDL || (ys >= D_top && ye == D_bot && !up));
|
|
if (D_top != ys && !(alok && dlok))
|
|
ChangeScrollRegion(ys, ye);
|
|
|
|
if (D_lp_missing &&
|
|
(oldbot != D_bot ||
|
|
(oldbot == D_bot && up && D_top == ys && D_bot == ye)))
|
|
{
|
|
WriteLP(D_width - 1, oldbot);
|
|
if (oldbot == D_bot) /* have scrolled */
|
|
{
|
|
if (--n == 0)
|
|
{
|
|
/* XXX
|
|
ChangeScrollRegion(oldtop, oldbot);
|
|
*/
|
|
if (bce && !D_BE)
|
|
ClearLine((struct mline *)0, ye, xs, xe, bce);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (D_UT)
|
|
SetRendition(&mchar_null);
|
|
#ifdef COLOR
|
|
if (D_BE)
|
|
SetBackColor(bce);
|
|
#endif
|
|
|
|
aldlfaster = (n > 1 && ys >= D_top && ye == D_bot && ((up && D_CDL) || (!up && D_CAL)));
|
|
|
|
if ((up || D_SR) && D_top == ys && D_bot == ye && !aldlfaster)
|
|
{
|
|
if (up)
|
|
{
|
|
GotoPos(0, ye);
|
|
for(i = n; i-- > 0; )
|
|
AddCStr(D_NL); /* was SF, I think NL is faster */
|
|
}
|
|
else
|
|
{
|
|
GotoPos(0, ys);
|
|
for(i = n; i-- > 0; )
|
|
AddCStr(D_SR);
|
|
}
|
|
}
|
|
else if (alok && dlok)
|
|
{
|
|
if (up || ye != D_bot)
|
|
{
|
|
GotoPos(0, up ? ys : ye+1-n);
|
|
if (D_CDL && !(n == 1 && D_DL))
|
|
AddCStr2(D_CDL, n);
|
|
else
|
|
for(i = n; i--; )
|
|
AddCStr(D_DL);
|
|
}
|
|
if (!up || ye != D_bot)
|
|
{
|
|
GotoPos(0, up ? ye+1-n : ys);
|
|
if (D_CAL && !(n == 1 && D_AL))
|
|
AddCStr2(D_CAL, n);
|
|
else
|
|
for(i = n; i--; )
|
|
AddCStr(D_AL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RefreshArea(xs, ys, xe, ye, 0);
|
|
return;
|
|
}
|
|
if (bce && !D_BE)
|
|
{
|
|
if (up)
|
|
ClearArea(xs, ye - n + 1, xs, xe, xe, ye, bce, 0);
|
|
else
|
|
ClearArea(xs, ys, xs, xe, xe, ys + n - 1, bce, 0);
|
|
}
|
|
if (D_lp_missing && missy != D_bot)
|
|
WriteLP(D_width - 1, missy);
|
|
/* XXX
|
|
ChangeScrollRegion(oldtop, oldbot);
|
|
if (D_lp_missing && missy != D_bot)
|
|
WriteLP(D_width - 1, missy);
|
|
*/
|
|
}
|
|
|
|
void
|
|
SetAttr(new)
|
|
register int new;
|
|
{
|
|
register int i, j, old, typ;
|
|
|
|
if (!display || (old = D_rend.attr) == new)
|
|
return;
|
|
#ifdef COLORS16
|
|
D_col16change = (old ^ new) & (A_BFG | A_BBG);
|
|
new ^= D_col16change;
|
|
if (old == new)
|
|
return;
|
|
#endif
|
|
#if defined(TERMINFO) && defined(USE_SGR)
|
|
if (D_SA)
|
|
{
|
|
char *tparm();
|
|
SetFont(ASCII);
|
|
ospeed = D_dospeed;
|
|
tputs(tparm(D_SA, new & A_SO, new & A_US, new & A_RV, new & A_BL,
|
|
new & A_DI, new & A_BD, 0 , 0 ,
|
|
0), 1, DoAddChar);
|
|
D_rend.attr = new;
|
|
D_atyp = 0;
|
|
# ifdef COLOR
|
|
if (D_hascolor)
|
|
rend_setdefault(&D_rend);
|
|
# endif
|
|
return;
|
|
}
|
|
#endif
|
|
D_rend.attr = new;
|
|
typ = D_atyp;
|
|
if ((new & old) != old)
|
|
{
|
|
if ((typ & ATYP_U))
|
|
AddCStr(D_UE);
|
|
if ((typ & ATYP_S))
|
|
AddCStr(D_SE);
|
|
if ((typ & ATYP_M))
|
|
{
|
|
AddCStr(D_ME);
|
|
#ifdef COLOR
|
|
/* ansi attrib handling: \E[m resets color, too */
|
|
if (D_hascolor)
|
|
rend_setdefault(&D_rend);
|
|
#endif
|
|
#ifdef FONT
|
|
if (!D_CG0)
|
|
{
|
|
/* D_ME may also reset the alternate charset */
|
|
D_rend.font = 0;
|
|
# ifdef ENCODINGS
|
|
D_realfont = 0;
|
|
# endif
|
|
}
|
|
#endif
|
|
}
|
|
old = 0;
|
|
typ = 0;
|
|
}
|
|
old ^= new;
|
|
for (i = 0, j = 1; old && i < NATTR; i++, j <<= 1)
|
|
{
|
|
if ((old & j) == 0)
|
|
continue;
|
|
old ^= j;
|
|
if (D_attrtab[i])
|
|
{
|
|
AddCStr(D_attrtab[i]);
|
|
typ |= D_attrtyp[i];
|
|
}
|
|
}
|
|
D_atyp = typ;
|
|
}
|
|
|
|
#ifdef FONT
|
|
void
|
|
SetFont(new)
|
|
int new;
|
|
{
|
|
int old = D_rend.font;
|
|
if (!display || old == new)
|
|
return;
|
|
D_rend.font = new;
|
|
#ifdef ENCODINGS
|
|
if (D_encoding && CanEncodeFont(D_encoding, new))
|
|
return;
|
|
if (new == D_realfont)
|
|
return;
|
|
D_realfont = new;
|
|
#endif
|
|
if (D_xtable && D_xtable[(int)(unsigned char)new] &&
|
|
D_xtable[(int)(unsigned char)new][256])
|
|
{
|
|
AddCStr(D_xtable[(int)(unsigned char)new][256]);
|
|
return;
|
|
}
|
|
|
|
if (!D_CG0 && new != '0')
|
|
{
|
|
new = ASCII;
|
|
if (old == new)
|
|
return;
|
|
}
|
|
|
|
if (new == ASCII)
|
|
AddCStr(D_CE0);
|
|
#ifdef DW_CHARS
|
|
else if (new < ' ')
|
|
{
|
|
AddStr("\033$");
|
|
if (new > 2)
|
|
AddChar('(');
|
|
AddChar(new + '@');
|
|
}
|
|
#endif
|
|
else
|
|
AddCStr2(D_CS0, new);
|
|
}
|
|
#endif
|
|
|
|
#ifdef COLOR
|
|
|
|
int
|
|
color256to16(jj)
|
|
int jj;
|
|
{
|
|
int min, max;
|
|
int r, g, b;
|
|
|
|
if (jj >= 232)
|
|
{
|
|
jj = (jj - 232) / 6;
|
|
jj = (jj & 1) << 3 | (jj & 2 ? 7 : 0);
|
|
}
|
|
else if (jj >= 16)
|
|
{
|
|
jj -= 16;
|
|
r = jj / 36;
|
|
g = (jj / 6) % 6;
|
|
b = jj % 6;
|
|
min = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
|
max = r > g ? (r > b ? r : b) : (g > b ? g : b);
|
|
if (min == max)
|
|
jj = ((max + 1) & 2) << 2 | ((max + 1) & 4 ? 7 : 0);
|
|
else
|
|
jj = (b - min) / (max - min) << 2 | (g - min) / (max - min) << 1 | (r -
|
|
min) / (max - min) | (max > 3 ? 8 : 0);
|
|
}
|
|
return jj;
|
|
}
|
|
|
|
#ifdef COLORS256
|
|
int
|
|
color256to88(jj)
|
|
int jj;
|
|
{
|
|
int r, g, b;
|
|
|
|
if (jj >= 232)
|
|
return (jj - 232) / 3 + 80;
|
|
if (jj >= 16)
|
|
{
|
|
jj -= 16;
|
|
r = jj / 36;
|
|
g = (jj / 6) % 6;
|
|
b = jj % 6;
|
|
return ((r + 1) / 2) * 16 + ((g + 1) / 2) * 4 + ((b + 1) / 2) + 16;
|
|
}
|
|
return jj;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
SetColor(f, b)
|
|
int f, b;
|
|
{
|
|
int of, ob;
|
|
static unsigned char sftrans[8] = {0,4,2,6,1,5,3,7};
|
|
|
|
if (!display)
|
|
return;
|
|
|
|
of = rend_getfg(&D_rend);
|
|
ob = rend_getbg(&D_rend);
|
|
|
|
#ifdef COLORS16
|
|
/* intense default not invented yet */
|
|
if (f == 0x100)
|
|
f = 0;
|
|
if (b == 0x100)
|
|
b = 0;
|
|
#endif
|
|
debug2("SetColor %d %d", coli2e(of), coli2e(ob));
|
|
debug2(" -> %d %d\n", coli2e(f), coli2e(b));
|
|
debug2("(%d %d", of, ob);
|
|
debug2(" -> %d %d)\n", f, b);
|
|
|
|
if (!D_CAX && D_hascolor && ((f == 0 && f != of) || (b == 0 && b != ob)))
|
|
{
|
|
if (D_OP)
|
|
AddCStr(D_OP);
|
|
else
|
|
{
|
|
int oattr;
|
|
oattr = D_rend.attr;
|
|
AddCStr(D_ME ? D_ME : "\033[m");
|
|
#ifdef FONT
|
|
if (D_ME && !D_CG0)
|
|
{
|
|
/* D_ME may also reset the alternate charset */
|
|
D_rend.font = 0;
|
|
# ifdef ENCODINGS
|
|
D_realfont = 0;
|
|
# endif
|
|
}
|
|
#endif
|
|
D_atyp = 0;
|
|
D_rend.attr = 0;
|
|
SetAttr(oattr);
|
|
}
|
|
of = ob = 0;
|
|
}
|
|
rend_setfg(&D_rend, f);
|
|
rend_setbg(&D_rend, b);
|
|
#ifdef COLORS16
|
|
D_col16change = 0;
|
|
#endif
|
|
if (!D_hascolor)
|
|
return;
|
|
f = f ? coli2e(f) : -1;
|
|
b = b ? coli2e(b) : -1;
|
|
of = of ? coli2e(of) : -1;
|
|
ob = ob ? coli2e(ob) : -1;
|
|
#ifdef COLORS256
|
|
if (f != of && f > 15 && D_CCO != 256)
|
|
f = D_CCO == 88 && D_CAF ? color256to88(f) : color256to16(f);
|
|
if (f != of && f > 15 && D_CAF)
|
|
{
|
|
AddCStr2(D_CAF, f);
|
|
of = f;
|
|
}
|
|
if (b != ob && b > 15 && D_CCO != 256)
|
|
b = D_CCO == 88 && D_CAB ? color256to88(b) : color256to16(b);
|
|
if (b != ob && b > 15 && D_CAB)
|
|
{
|
|
AddCStr2(D_CAB, b);
|
|
ob = b;
|
|
}
|
|
#endif
|
|
if (f != of && f != (of | 8))
|
|
{
|
|
if (f == -1)
|
|
AddCStr("\033[39m"); /* works because AX is set */
|
|
else if (D_CAF)
|
|
AddCStr2(D_CAF, f & 7);
|
|
else if (D_CSF)
|
|
AddCStr2(D_CSF, sftrans[f & 7]);
|
|
}
|
|
if (b != ob && b != (ob | 8))
|
|
{
|
|
if (b == -1)
|
|
AddCStr("\033[49m"); /* works because AX is set */
|
|
else if (D_CAB)
|
|
AddCStr2(D_CAB, b & 7);
|
|
else if (D_CSB)
|
|
AddCStr2(D_CSB, sftrans[b & 7]);
|
|
}
|
|
#ifdef COLORS16
|
|
if (f != of && D_CXT && (f & 8) != 0 && f != -1)
|
|
{
|
|
# ifdef TERMINFO
|
|
AddCStr2("\033[9%p1%dm", f & 7);
|
|
# else
|
|
AddCStr2("\033[9%dm", f & 7);
|
|
# endif
|
|
}
|
|
if (b != ob && D_CXT && (b & 8) != 0 && b != -1)
|
|
{
|
|
# ifdef TERMINFO
|
|
AddCStr2("\033[10%p1%dm", b & 7);
|
|
# else
|
|
AddCStr2("\033[10%dm", b & 7);
|
|
# endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
SetBackColor(new)
|
|
int new;
|
|
{
|
|
if (!display)
|
|
return;
|
|
SetColor(rend_getfg(&D_rend), new);
|
|
}
|
|
#endif /* COLOR */
|
|
|
|
void
|
|
SetRendition(mc)
|
|
struct mchar *mc;
|
|
{
|
|
if (!display)
|
|
return;
|
|
#ifdef COLOR
|
|
if (nattr2color && D_hascolor && (mc->attr & nattr2color) != 0)
|
|
{
|
|
static struct mchar mmc;
|
|
int i;
|
|
mmc = *mc;
|
|
for (i = 0; i < 8; i++)
|
|
if (attr2color[i] && (mc->attr & (1 << i)) != 0)
|
|
{
|
|
if (mc->color == 0 && attr2color[i][3])
|
|
ApplyAttrColor(attr2color[i][3], &mmc);
|
|
else if ((mc->color & 0x0f) == 0 && attr2color[i][2])
|
|
ApplyAttrColor(attr2color[i][2], &mmc);
|
|
else if ((mc->color & 0xf0) == 0 && attr2color[i][1])
|
|
ApplyAttrColor(attr2color[i][1], &mmc);
|
|
else
|
|
ApplyAttrColor(attr2color[i][0], &mmc);
|
|
}
|
|
mc = &mmc;
|
|
debug2("SetRendition: mapped to %02x %02x\n", (unsigned char)mc->attr, 0x99 - (unsigned char)mc->color);
|
|
}
|
|
# ifdef COLORS16
|
|
if (D_hascolor && D_CC8 && (mc->attr & (A_BFG|A_BBG)))
|
|
{
|
|
int a = mc->attr;
|
|
if ((mc->attr & A_BFG) && D_MD)
|
|
a |= A_BD;
|
|
if ((mc->attr & A_BBG) && D_MB)
|
|
a |= A_BL;
|
|
if (D_rend.attr != a)
|
|
SetAttr(a);
|
|
}
|
|
else
|
|
# endif /* COLORS16 */
|
|
#endif /* COLOR */
|
|
if (D_rend.attr != mc->attr)
|
|
SetAttr(mc->attr);
|
|
|
|
#ifdef COLOR
|
|
if (D_rend.color != mc->color
|
|
# ifdef COLORS256
|
|
|| D_rend.colorx != mc->colorx
|
|
# endif
|
|
# ifdef COLORS16
|
|
|| D_col16change
|
|
# endif
|
|
)
|
|
SetColor(rend_getfg(mc), rend_getbg(mc));
|
|
#endif
|
|
#ifdef FONT
|
|
if (D_rend.font != mc->font)
|
|
SetFont(mc->font);
|
|
#ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
D_rend.fontx = mc->fontx;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void
|
|
SetRenditionMline(ml, x)
|
|
struct mline *ml;
|
|
int x;
|
|
{
|
|
if (!display)
|
|
return;
|
|
#ifdef COLOR
|
|
if (nattr2color && D_hascolor && (ml->attr[x] & nattr2color) != 0)
|
|
{
|
|
struct mchar mc;
|
|
copy_mline2mchar(&mc, ml, x);
|
|
SetRendition(&mc);
|
|
return;
|
|
}
|
|
# ifdef COLORS16
|
|
if (D_hascolor && D_CC8 && (ml->attr[x] & (A_BFG|A_BBG)))
|
|
{
|
|
int a = ml->attr[x];
|
|
if ((ml->attr[x] & A_BFG) && D_MD)
|
|
a |= A_BD;
|
|
if ((ml->attr[x] & A_BBG) && D_MB)
|
|
a |= A_BL;
|
|
if (D_rend.attr != a)
|
|
SetAttr(a);
|
|
}
|
|
else
|
|
# endif /* COLORS16 */
|
|
#endif /* COLOR */
|
|
if (D_rend.attr != ml->attr[x])
|
|
SetAttr(ml->attr[x]);
|
|
#ifdef COLOR
|
|
if (D_rend.color != ml->color[x]
|
|
# ifdef COLORS256
|
|
|| D_rend.colorx != ml->colorx[x]
|
|
# endif
|
|
# ifdef COLORS16
|
|
|| D_col16change
|
|
# endif
|
|
)
|
|
{
|
|
struct mchar mc;
|
|
copy_mline2mchar(&mc, ml, x);
|
|
SetColor(rend_getfg(&mc), rend_getbg(&mc));
|
|
}
|
|
#endif
|
|
#ifdef FONT
|
|
if (D_rend.font != ml->font[x])
|
|
SetFont(ml->font[x]);
|
|
#ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
D_rend.fontx = ml->fontx[x];
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MakeStatus(msg)
|
|
char *msg;
|
|
{
|
|
register char *s, *t;
|
|
register int max;
|
|
|
|
if (!display)
|
|
return;
|
|
|
|
if (D_blocked)
|
|
return;
|
|
if (!D_tcinited)
|
|
{
|
|
debug("tc not inited, just writing msg\n");
|
|
if (D_processinputdata)
|
|
return; /* XXX: better */
|
|
AddStr(msg);
|
|
AddStr("\r\n");
|
|
Flush(0);
|
|
return;
|
|
}
|
|
if (!use_hardstatus || !D_HS)
|
|
{
|
|
max = D_width;
|
|
if (D_CLP == 0)
|
|
max--;
|
|
}
|
|
else
|
|
max = D_WS > 0 ? D_WS : (D_width - !D_CLP);
|
|
if (D_status)
|
|
{
|
|
/* same message? */
|
|
if (strcmp(msg, D_status_lastmsg) == 0)
|
|
{
|
|
debug("same message - increase timeout");
|
|
if (!D_status_obufpos)
|
|
SetTimeout(&D_statusev, MsgWait);
|
|
return;
|
|
}
|
|
RemoveStatusMinWait();
|
|
}
|
|
for (s = t = msg; *s && t - msg < max; ++s)
|
|
if (*s == BELL)
|
|
AddCStr(D_BL);
|
|
else if ((unsigned char)*s >= ' ' && *s != 0177)
|
|
*t++ = *s;
|
|
*t = '\0';
|
|
if (t == msg)
|
|
return;
|
|
if (t - msg >= D_status_buflen)
|
|
{
|
|
char *buf;
|
|
if (D_status_lastmsg)
|
|
buf = realloc(D_status_lastmsg, t - msg + 1);
|
|
else
|
|
buf = malloc(t - msg + 1);
|
|
if (buf)
|
|
{
|
|
D_status_lastmsg = buf;
|
|
D_status_buflen = t - msg + 1;
|
|
}
|
|
}
|
|
if (t - msg < D_status_buflen)
|
|
strcpy(D_status_lastmsg, msg);
|
|
D_status_len = t - msg;
|
|
D_status_lastx = D_x;
|
|
D_status_lasty = D_y;
|
|
if (!use_hardstatus || D_has_hstatus == HSTATUS_IGNORE || D_has_hstatus == HSTATUS_MESSAGE)
|
|
{
|
|
D_status = STATUS_ON_WIN;
|
|
debug1("using STATLINE %d\n", STATLINE);
|
|
GotoPos(0, STATLINE);
|
|
SetRendition(&mchar_so);
|
|
InsertMode(0);
|
|
AddStr(msg);
|
|
if (D_status_len < max)
|
|
{
|
|
/* Wayne Davison: add extra space for readability */
|
|
D_status_len++;
|
|
SetRendition(&mchar_null);
|
|
AddChar(' ');
|
|
if (D_status_len < max)
|
|
{
|
|
D_status_len++;
|
|
AddChar(' ');
|
|
AddChar('\b');
|
|
}
|
|
AddChar('\b');
|
|
}
|
|
D_x = -1;
|
|
}
|
|
else
|
|
{
|
|
D_status = STATUS_ON_HS;
|
|
ShowHStatus(msg);
|
|
}
|
|
|
|
D_status_obufpos = D_obufp - D_obuf;
|
|
ASSERT(D_status_obufpos > 0);
|
|
|
|
if (D_status == STATUS_ON_WIN)
|
|
{
|
|
struct display *olddisplay = display;
|
|
struct layer *oldflayer = flayer;
|
|
|
|
/* this is copied over from RemoveStatus() */
|
|
D_status = 0;
|
|
GotoPos(0, STATLINE);
|
|
RefreshLine(STATLINE, 0, D_status_len - 1, 0);
|
|
GotoPos(D_status_lastx, D_status_lasty);
|
|
flayer = D_forecv ? D_forecv->c_layer : 0;
|
|
if (flayer)
|
|
LaySetCursor();
|
|
display = olddisplay;
|
|
flayer = oldflayer;
|
|
D_status = STATUS_ON_WIN;
|
|
}
|
|
}
|
|
|
|
void
|
|
RemoveStatus()
|
|
{
|
|
struct display *olddisplay;
|
|
struct layer *oldflayer;
|
|
int where;
|
|
|
|
if (!display)
|
|
return;
|
|
if (!(where = D_status))
|
|
return;
|
|
|
|
debug("RemoveStatus\n");
|
|
if (D_status_obuffree >= 0)
|
|
{
|
|
D_obuflen = D_status_obuflen;
|
|
D_obuffree = D_status_obuffree;
|
|
D_status_obuffree = -1;
|
|
}
|
|
D_status = 0;
|
|
D_status_obufpos = 0;
|
|
D_status_bell = 0;
|
|
evdeq(&D_statusev);
|
|
olddisplay = display;
|
|
oldflayer = flayer;
|
|
if (where == STATUS_ON_WIN)
|
|
{
|
|
if (captionalways || (D_canvas.c_slperp && D_canvas.c_slperp->c_slnext))
|
|
{
|
|
GotoPos(0, STATLINE);
|
|
RefreshLine(STATLINE, 0, D_status_len - 1, 0);
|
|
GotoPos(D_status_lastx, D_status_lasty);
|
|
}
|
|
}
|
|
else
|
|
RefreshHStatus();
|
|
flayer = D_forecv ? D_forecv->c_layer : 0;
|
|
if (flayer)
|
|
LaySetCursor();
|
|
display = olddisplay;
|
|
flayer = oldflayer;
|
|
}
|
|
|
|
/* Remove the status but make sure that it is seen for MsgMinWait ms */
|
|
static void
|
|
RemoveStatusMinWait()
|
|
{
|
|
/* XXX: should flush output first if D_status_obufpos is set */
|
|
if (!D_status_bell && !D_status_obufpos)
|
|
{
|
|
struct timeval now;
|
|
int ti;
|
|
gettimeofday(&now, NULL);
|
|
ti = (now.tv_sec - D_status_time.tv_sec) * 1000 + (now.tv_usec - D_status_time.tv_usec) / 1000;
|
|
if (ti < MsgMinWait)
|
|
DisplaySleep1000(MsgMinWait - ti, 0);
|
|
}
|
|
RemoveStatus();
|
|
}
|
|
|
|
#ifdef UTF8
|
|
static int
|
|
strlen_onscreen(unsigned char *c, unsigned char *end)
|
|
{
|
|
int len = 0;
|
|
while (*c && (!end || c < end))
|
|
{
|
|
int v, dec = 0;
|
|
do
|
|
{
|
|
v = FromUtf8(*c++, &dec);
|
|
if (v == -2)
|
|
c--;
|
|
}
|
|
while (v < 0 && (!end || c < end));
|
|
if (!utf8_iscomb(v))
|
|
{
|
|
if (utf8_isdouble(v))
|
|
len++;
|
|
len++;
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static int
|
|
PrePutWinMsg(s, start, max)
|
|
char *s;
|
|
int start, max;
|
|
{
|
|
/* Avoid double-encoding problem for a UTF-8 message on a UTF-8 locale.
|
|
Ideally, this would not be necessary. But fixing it the Right Way will
|
|
probably take way more time. So this will have to do for now. */
|
|
if (D_encoding == UTF8)
|
|
{
|
|
int chars = strlen_onscreen((unsigned char *)(s + start), (unsigned char *)(s + max));
|
|
D_encoding = 0;
|
|
PutWinMsg(s, start, max + ((max - start) - chars)); /* Multibyte count */
|
|
D_encoding = UTF8;
|
|
D_x -= (max - chars); /* Yak! But this is necessary to count for
|
|
the fact that not every byte represents a
|
|
character. */
|
|
return start + chars;
|
|
}
|
|
else
|
|
{
|
|
PutWinMsg(s, start, max);
|
|
return max;
|
|
}
|
|
}
|
|
#else
|
|
static int
|
|
PrePutWinMsg(s, start, max)
|
|
char *s;
|
|
int start, max;
|
|
{
|
|
PutWinMsg(s, start, max);
|
|
return max;
|
|
}
|
|
#endif
|
|
|
|
/* refresh the display's hstatus line */
|
|
void
|
|
ShowHStatus(str)
|
|
char *str;
|
|
{
|
|
int l, ox, oy, max;
|
|
|
|
if (D_status == STATUS_ON_WIN && D_has_hstatus == HSTATUS_LASTLINE && STATLINE == D_height-1)
|
|
return; /* sorry, in use */
|
|
if (D_blocked)
|
|
return;
|
|
|
|
if (D_HS && D_has_hstatus == HSTATUS_HS)
|
|
{
|
|
if (!D_hstatus && (str == 0 || *str == 0))
|
|
return;
|
|
debug("ShowHStatus: using HS\n");
|
|
SetRendition(&mchar_null);
|
|
InsertMode(0);
|
|
if (D_hstatus)
|
|
AddCStr(D_DS);
|
|
D_hstatus = 0;
|
|
if (str == 0 || *str == 0)
|
|
return;
|
|
AddCStr2(D_TS, 0);
|
|
max = D_WS > 0 ? D_WS : (D_width - !D_CLP);
|
|
if ((int)strlen(str) > max)
|
|
AddStrn(str, max);
|
|
else
|
|
AddStr(str);
|
|
AddCStr(D_FS);
|
|
D_hstatus = 1;
|
|
}
|
|
else if (D_has_hstatus == HSTATUS_LASTLINE)
|
|
{
|
|
debug("ShowHStatus: using last line\n");
|
|
ox = D_x;
|
|
oy = D_y;
|
|
str = str ? str : "";
|
|
l = strlen(str);
|
|
if (l > D_width)
|
|
l = D_width;
|
|
GotoPos(0, D_height - 1);
|
|
SetRendition(captionalways || D_cvlist == 0 || D_cvlist->c_next ? &mchar_null: &mchar_so);
|
|
l = PrePutWinMsg(str, 0, l);
|
|
if (!captionalways && D_cvlist && !D_cvlist->c_next)
|
|
while (l++ < D_width)
|
|
PUTCHARLP(' ');
|
|
if (l < D_width)
|
|
ClearArea(l, D_height - 1, l, D_width - 1, D_width - 1, D_height - 1, 0, 0);
|
|
if (ox != -1 && oy != -1)
|
|
GotoPos(ox, oy);
|
|
D_hstatus = *str ? 1 : 0;
|
|
SetRendition(&mchar_null);
|
|
}
|
|
else if (D_has_hstatus == HSTATUS_FIRSTLINE)
|
|
{
|
|
debug("ShowHStatus: using first line\n");
|
|
ox = D_x;
|
|
oy = D_y;
|
|
str = str ? str : "";
|
|
l = strlen(str);
|
|
if (l > D_width)
|
|
l = D_width;
|
|
GotoPos(0, 0);
|
|
SetRendition(captionalways || D_cvlist == 0 || D_cvlist->c_next ? &mchar_null: &mchar_so);
|
|
l = PrePutWinMsg(str, 0, l);
|
|
if (!captionalways || (D_cvlist && !D_cvlist->c_next))
|
|
while (l++ < D_width)
|
|
PUTCHARLP(' ');
|
|
if (l < D_width)
|
|
ClearArea(l, 0, l, D_width - 1, D_width - 1, 0, 0, 0);
|
|
if (ox != -1 && oy != -1)
|
|
GotoPos(ox, oy);
|
|
D_hstatus = *str ? 1 : 0;
|
|
SetRendition(&mchar_null);
|
|
}
|
|
else if (str && *str && D_has_hstatus == HSTATUS_MESSAGE)
|
|
{
|
|
debug("ShowHStatus: using message\n");
|
|
Msg(0, "%s", str);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Refreshes the hardstatus of the fore window. Shouldn't be here...
|
|
*/
|
|
void
|
|
RefreshHStatus()
|
|
{
|
|
char *buf;
|
|
#ifdef UTF8
|
|
int extrabytes = strlen(hstatusstring) - strlen_onscreen((unsigned char *)hstatusstring, NULL);
|
|
#else
|
|
int extrabytes = 0;
|
|
#endif
|
|
evdeq(&D_hstatusev);
|
|
if (D_status == STATUS_ON_HS)
|
|
return;
|
|
buf = MakeWinMsgEv(hstatusstring, D_fore, '%', (D_HS && D_has_hstatus == HSTATUS_HS && D_WS > 0) ? D_WS : D_width - !D_CLP + extrabytes, &D_hstatusev, 0);
|
|
if (buf && *buf)
|
|
{
|
|
ShowHStatus(buf);
|
|
if (D_has_hstatus != HSTATUS_IGNORE && D_hstatusev.timeout.tv_sec)
|
|
evenq(&D_hstatusev);
|
|
}
|
|
else
|
|
ShowHStatus((char *)0);
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/*
|
|
* Here come the routines that refresh an arbitrary part of the screen.
|
|
*/
|
|
|
|
void
|
|
RefreshAll(isblank)
|
|
int isblank;
|
|
{
|
|
struct canvas *cv;
|
|
|
|
ASSERT(display);
|
|
debug("Signalling full refresh!\n");
|
|
for (cv = D_cvlist; cv; cv = cv->c_next)
|
|
{
|
|
CV_CALL(cv, LayRedisplayLine(-1, -1, -1, isblank));
|
|
display = cv->c_display; /* just in case! */
|
|
}
|
|
RefreshArea(0, 0, D_width - 1, D_height - 1, isblank);
|
|
}
|
|
|
|
void
|
|
RefreshArea(xs, ys, xe, ye, isblank)
|
|
int xs, ys, xe, ye, isblank;
|
|
{
|
|
int y;
|
|
ASSERT(display);
|
|
debug2("Refresh Area: %d,%d", xs, ys);
|
|
debug3(" - %d,%d (isblank=%d)\n", xe, ye, isblank);
|
|
if (!isblank && xs == 0 && xe == D_width - 1 && ye == D_height - 1 && (ys == 0 || D_CD))
|
|
{
|
|
ClearArea(xs, ys, xs, xe, xe, ye, 0, 0);
|
|
isblank = 1;
|
|
}
|
|
for (y = ys; y <= ye; y++)
|
|
RefreshLine(y, xs, xe, isblank);
|
|
}
|
|
|
|
void
|
|
RefreshLine(y, from, to, isblank)
|
|
int y, from, to, isblank;
|
|
{
|
|
struct viewport *vp, *lvp;
|
|
struct canvas *cv, *lcv, *cvlist, *cvlnext;
|
|
struct layer *oldflayer;
|
|
int xx, yy, l;
|
|
char *buf;
|
|
struct win *p;
|
|
|
|
ASSERT(display);
|
|
|
|
debug2("RefreshLine %d %d", y, from);
|
|
debug2(" %d %d\n", to, isblank);
|
|
|
|
if (D_status == STATUS_ON_WIN && y == STATLINE)
|
|
{
|
|
if (to >= D_status_len)
|
|
D_status_len = to + 1;
|
|
return; /* can't refresh status */
|
|
}
|
|
|
|
if (isblank == 0 && D_CE && to == D_width - 1 && from < to && D_status != STATUS_ON_HS)
|
|
{
|
|
GotoPos(from, y);
|
|
if (D_UT || D_BE)
|
|
SetRendition(&mchar_null);
|
|
AddCStr(D_CE);
|
|
isblank = 1;
|
|
}
|
|
|
|
if ((y == D_height - 1 && D_has_hstatus == HSTATUS_LASTLINE) || (y == 0 && D_has_hstatus == HSTATUS_FIRSTLINE) )
|
|
{
|
|
RefreshHStatus();
|
|
return;
|
|
}
|
|
|
|
while (from <= to)
|
|
{
|
|
lcv = 0;
|
|
lvp = 0;
|
|
for (cv = display->d_cvlist; cv; cv = cv->c_next)
|
|
{
|
|
if (y == cv->c_ye + 1 && from >= cv->c_xs && from <= cv->c_xe)
|
|
{
|
|
#ifdef UTF8
|
|
int extrabytes = strlen(captionstring) - strlen_onscreen((unsigned char *)captionstring, NULL);
|
|
#else
|
|
int extrabytes = 0;
|
|
#endif
|
|
p = Layer2Window(cv->c_layer);
|
|
buf = MakeWinMsgEv(captionstring, p, '%', cv->c_xe - cv->c_xs + (cv->c_xe + 1 < D_width || D_CLP) + extrabytes, &cv->c_captev, 0);
|
|
if (cv->c_captev.timeout.tv_sec)
|
|
evenq(&cv->c_captev);
|
|
xx = to > cv->c_xe ? cv->c_xe : to;
|
|
l = strlen(buf);
|
|
GotoPos(from, y);
|
|
SetRendition(&mchar_so);
|
|
if (l > xx - cv->c_xs + 1)
|
|
l = xx - cv->c_xs + 1;
|
|
l = PrePutWinMsg(buf, from - cv->c_xs, l + extrabytes);
|
|
from = cv->c_xs + l;
|
|
for (; from <= xx; from++)
|
|
PUTCHARLP(' ');
|
|
break;
|
|
}
|
|
if (from == cv->c_xe + 1 && y >= cv->c_ys && y <= cv->c_ye + 1)
|
|
{
|
|
GotoPos(from, y);
|
|
SetRendition(&mchar_so);
|
|
PUTCHARLP(' ');
|
|
from++;
|
|
break;
|
|
}
|
|
if (y < cv->c_ys || y > cv->c_ye || to < cv->c_xs || from > cv->c_xe)
|
|
continue;
|
|
debug2("- canvas hit: %d %d", cv->c_xs, cv->c_ys);
|
|
debug2(" %d %d\n", cv->c_xe, cv->c_ye);
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next)
|
|
{
|
|
debug2(" - vp: %d %d", vp->v_xs, vp->v_ys);
|
|
debug2(" %d %d\n", vp->v_xe, vp->v_ye);
|
|
/* find leftmost overlapping vp */
|
|
if (y >= vp->v_ys && y <= vp->v_ye && from <= vp->v_xe && to >= vp->v_xs && (lvp == 0 || lvp->v_xs > vp->v_xs))
|
|
{
|
|
lcv = cv;
|
|
lvp = vp;
|
|
}
|
|
}
|
|
}
|
|
if (cv)
|
|
continue; /* we advanced from */
|
|
if (lvp == 0)
|
|
break;
|
|
if (from < lvp->v_xs)
|
|
{
|
|
if (!isblank)
|
|
DisplayLine(&mline_null, &mline_blank, y, from, lvp->v_xs - 1);
|
|
from = lvp->v_xs;
|
|
}
|
|
|
|
/* call LayRedisplayLine on canvas lcv viewport lvp */
|
|
yy = y - lvp->v_yoff;
|
|
xx = to < lvp->v_xe ? to : lvp->v_xe;
|
|
|
|
if (lcv->c_layer && lcv->c_xoff + lcv->c_layer->l_width == from)
|
|
{
|
|
GotoPos(from, y);
|
|
SetRendition(&mchar_blank);
|
|
PUTCHARLP('|');
|
|
from++;
|
|
}
|
|
if (lcv->c_layer && yy == lcv->c_layer->l_height)
|
|
{
|
|
GotoPos(from, y);
|
|
SetRendition(&mchar_blank);
|
|
while (from <= lvp->v_xe && from - lvp->v_xoff < lcv->c_layer->l_width)
|
|
{
|
|
PUTCHARLP('-');
|
|
from++;
|
|
}
|
|
if (from >= lvp->v_xe + 1)
|
|
continue;
|
|
}
|
|
if (lcv->c_layer == 0 || yy >= lcv->c_layer->l_height || from - lvp->v_xoff >= lcv->c_layer->l_width)
|
|
{
|
|
if (!isblank)
|
|
DisplayLine(&mline_null, &mline_blank, y, from, lvp->v_xe);
|
|
from = lvp->v_xe + 1;
|
|
continue;
|
|
}
|
|
|
|
if (xx - lvp->v_xoff >= lcv->c_layer->l_width)
|
|
xx = lcv->c_layer->l_width + lvp->v_xoff - 1;
|
|
oldflayer = flayer;
|
|
flayer = lcv->c_layer;
|
|
cvlist = flayer->l_cvlist;
|
|
cvlnext = lcv->c_lnext;
|
|
flayer->l_cvlist = lcv;
|
|
lcv->c_lnext = 0;
|
|
LayRedisplayLine(yy, from - lvp->v_xoff, xx - lvp->v_xoff, isblank);
|
|
flayer->l_cvlist = cvlist;
|
|
lcv->c_lnext = cvlnext;
|
|
flayer = oldflayer;
|
|
|
|
from = xx + 1;
|
|
}
|
|
if (!isblank && from <= to)
|
|
DisplayLine(&mline_null, &mline_blank, y, from, to);
|
|
}
|
|
|
|
/*********************************************************************/
|
|
|
|
/* clear lp_missing by writing the char on the screen. The
|
|
* position must be safe.
|
|
*/
|
|
static void
|
|
WriteLP(x2, y2)
|
|
int x2, y2;
|
|
{
|
|
struct mchar oldrend;
|
|
|
|
ASSERT(display);
|
|
ASSERT(D_lp_missing);
|
|
oldrend = D_rend;
|
|
debug2("WriteLP(%d,%d)\n", x2, y2);
|
|
#ifdef DW_CHARS
|
|
if (D_lpchar.mbcs)
|
|
{
|
|
if (x2 > 0)
|
|
x2--;
|
|
else
|
|
D_lpchar = mchar_blank;
|
|
}
|
|
#endif
|
|
/* Can't use PutChar */
|
|
GotoPos(x2, y2);
|
|
SetRendition(&D_lpchar);
|
|
PUTCHAR(D_lpchar.image);
|
|
#ifdef DW_CHARS
|
|
if (D_lpchar.mbcs)
|
|
PUTCHAR(D_lpchar.mbcs);
|
|
#endif
|
|
D_lp_missing = 0;
|
|
SetRendition(&oldrend);
|
|
}
|
|
|
|
void
|
|
ClearLine(oml, y, from, to, bce)
|
|
struct mline *oml;
|
|
int from, to, y, bce;
|
|
{
|
|
int x;
|
|
#ifdef COLOR
|
|
struct mchar bcechar;
|
|
#endif
|
|
|
|
debug3("ClearLine %d,%d-%d\n", y, from, to);
|
|
if (D_UT) /* Safe to erase ? */
|
|
SetRendition(&mchar_null);
|
|
#ifdef COLOR
|
|
if (D_BE)
|
|
SetBackColor(bce);
|
|
#endif
|
|
if (from == 0 && D_CB && (to != D_width - 1 || (D_x == to && D_y == y)) && (!bce || D_BE))
|
|
{
|
|
GotoPos(to, y);
|
|
AddCStr(D_CB);
|
|
return;
|
|
}
|
|
if (to == D_width - 1 && D_CE && (!bce || D_BE))
|
|
{
|
|
GotoPos(from, y);
|
|
AddCStr(D_CE);
|
|
return;
|
|
}
|
|
if (oml == 0)
|
|
oml = &mline_null;
|
|
#ifdef COLOR
|
|
if (!bce)
|
|
{
|
|
DisplayLine(oml, &mline_blank, y, from, to);
|
|
return;
|
|
}
|
|
bcechar = mchar_null;
|
|
rend_setbg(&bcechar, bce);
|
|
for (x = from; x <= to; x++)
|
|
copy_mchar2mline(&bcechar, &mline_old, x);
|
|
DisplayLine(oml, &mline_old, y, from, to);
|
|
#else
|
|
DisplayLine(oml, &mline_blank, y, from, to);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
DisplayLine(oml, ml, y, from, to)
|
|
struct mline *oml, *ml;
|
|
int from, to, y;
|
|
{
|
|
register int x;
|
|
int last2flag = 0, delete_lp = 0;
|
|
|
|
ASSERT(display);
|
|
ASSERT(y >= 0 && y < D_height);
|
|
ASSERT(from >= 0 && from < D_width);
|
|
ASSERT(to >= 0 && to < D_width);
|
|
if (!D_CLP && y == D_bot && to == D_width - 1)
|
|
{
|
|
if (D_lp_missing || !cmp_mline(oml, ml, to))
|
|
{
|
|
#ifdef DW_CHARS
|
|
if ((D_IC || D_IM) && from < to && !dw_left(ml, to, D_encoding))
|
|
#else
|
|
if ((D_IC || D_IM) && from < to)
|
|
#endif
|
|
{
|
|
last2flag = 1;
|
|
D_lp_missing = 0;
|
|
to--;
|
|
}
|
|
else
|
|
{
|
|
delete_lp = !cmp_mchar_mline(&mchar_blank, oml, to) && (D_CE || D_DC || D_CDC);
|
|
D_lp_missing = !cmp_mchar_mline(&mchar_blank, ml, to);
|
|
copy_mline2mchar(&D_lpchar, ml, to);
|
|
}
|
|
}
|
|
to--;
|
|
}
|
|
#ifdef DW_CHARS
|
|
if (D_mbcs)
|
|
{
|
|
/* finish dw-char (can happen after a wrap) */
|
|
debug("DisplayLine finishing kanji\n");
|
|
SetRenditionMline(ml, from);
|
|
PUTCHAR(ml->image[from]);
|
|
from++;
|
|
}
|
|
#endif
|
|
for (x = from; x <= to; x++)
|
|
{
|
|
#if 0 /* no longer needed */
|
|
if (x || D_x != D_width || D_y != y - 1)
|
|
#endif
|
|
{
|
|
if (ml != NULL && (x < to || x != D_width - 1 || ml->image[x + 1]))
|
|
if (cmp_mline(oml, ml, x))
|
|
continue;
|
|
GotoPos(x, y);
|
|
}
|
|
#ifdef DW_CHARS
|
|
if (dw_right(ml, x, D_encoding))
|
|
{
|
|
x--;
|
|
debug1("DisplayLine on right side of dw char- x now %d\n", x);
|
|
GotoPos(x, y);
|
|
}
|
|
if (x == to && dw_left(ml, x, D_encoding))
|
|
break; /* don't start new kanji */
|
|
#endif
|
|
SetRenditionMline(ml, x);
|
|
PUTCHAR(ml->image[x]);
|
|
#ifdef DW_CHARS
|
|
if (dw_left(ml, x, D_encoding))
|
|
PUTCHAR(ml->image[++x]);
|
|
#endif
|
|
}
|
|
#if 0 /* not needed any longer */
|
|
/* compare != 0 because ' ' can happen when clipping occures */
|
|
if (to == D_width - 1 && y < D_height - 1 && D_x == D_width && ml->image[to + 1])
|
|
GotoPos(0, y + 1);
|
|
#endif
|
|
if (last2flag)
|
|
{
|
|
GotoPos(x, y);
|
|
SetRenditionMline(ml, x + 1);
|
|
PUTCHAR(ml->image[x + 1]);
|
|
GotoPos(x, y);
|
|
SetRenditionMline(ml, x);
|
|
INSERTCHAR(ml->image[x]);
|
|
}
|
|
else if (delete_lp)
|
|
{
|
|
if (D_UT)
|
|
SetRendition(&mchar_null);
|
|
if (D_DC)
|
|
AddCStr(D_DC);
|
|
else if (D_CDC)
|
|
AddCStr2(D_CDC, 1);
|
|
else if (D_CE)
|
|
AddCStr(D_CE);
|
|
}
|
|
}
|
|
|
|
void
|
|
PutChar(c, x, y)
|
|
struct mchar *c;
|
|
int x, y;
|
|
{
|
|
GotoPos(x, y);
|
|
SetRendition(c);
|
|
PUTCHARLP(c->image);
|
|
#ifdef DW_CHARS
|
|
if (c->mbcs)
|
|
{
|
|
# ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
D_rend.font = 0;
|
|
# endif
|
|
PUTCHARLP(c->mbcs);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
InsChar(c, x, xe, y, oml)
|
|
struct mchar *c;
|
|
int x, xe, y;
|
|
struct mline *oml;
|
|
{
|
|
GotoPos(x, y);
|
|
if (y == D_bot && !D_CLP)
|
|
{
|
|
if (x == D_width - 1)
|
|
{
|
|
D_lp_missing = 1;
|
|
D_lpchar = *c;
|
|
return;
|
|
}
|
|
if (xe == D_width - 1)
|
|
D_lp_missing = 0;
|
|
}
|
|
if (x == xe)
|
|
{
|
|
SetRendition(c);
|
|
PUTCHARLP(c->image);
|
|
return;
|
|
}
|
|
if (!(D_IC || D_CIC || D_IM) || xe != D_width - 1)
|
|
{
|
|
RefreshLine(y, x, xe, 0);
|
|
GotoPos(x + 1, y);
|
|
/* UpdateLine(oml, y, x, xe); */
|
|
return;
|
|
}
|
|
InsertMode(1);
|
|
if (!D_insert)
|
|
{
|
|
#ifdef DW_CHARS
|
|
if (c->mbcs && D_IC)
|
|
AddCStr(D_IC);
|
|
if (D_IC)
|
|
AddCStr(D_IC);
|
|
else
|
|
AddCStr2(D_CIC, c->mbcs ? 2 : 1);
|
|
#else
|
|
if (D_IC)
|
|
AddCStr(D_IC);
|
|
else
|
|
AddCStr2(D_CIC, 1);
|
|
#endif
|
|
}
|
|
SetRendition(c);
|
|
RAW_PUTCHAR(c->image);
|
|
#ifdef DW_CHARS
|
|
if (c->mbcs)
|
|
{
|
|
# ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
D_rend.font = 0;
|
|
# endif
|
|
if (D_x == D_width - 1)
|
|
PUTCHARLP(c->mbcs);
|
|
else
|
|
RAW_PUTCHAR(c->mbcs);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
WrapChar(c, x, y, xs, ys, xe, ye, ins)
|
|
struct mchar *c;
|
|
int x, y;
|
|
int xs, ys, xe, ye;
|
|
int ins;
|
|
{
|
|
int bce;
|
|
|
|
#ifdef COLOR
|
|
bce = rend_getbg(c);
|
|
#else
|
|
bce = 0;
|
|
#endif
|
|
debug("WrapChar:");
|
|
debug2(" x %d y %d", x, y);
|
|
debug2(" Dx %d Dy %d", D_x, D_y);
|
|
debug2(" xs %d ys %d", xs, ys);
|
|
debug3(" xe %d ye %d ins %d\n", xe, ye, ins);
|
|
if (xs != 0 || x != D_width || !D_AM)
|
|
{
|
|
if (y == ye)
|
|
ScrollV(xs, ys, xe, ye, 1, bce);
|
|
else if (y < D_height - 1)
|
|
y++;
|
|
if (ins)
|
|
InsChar(c, xs, xe, y, 0);
|
|
else
|
|
PutChar(c, xs, y);
|
|
return;
|
|
}
|
|
if (y == ye) /* we have to scroll */
|
|
{
|
|
debug("- scrolling\n");
|
|
ChangeScrollRegion(ys, ye);
|
|
if (D_bot != y || D_x != D_width || (!bce && !D_BE))
|
|
{
|
|
debug("- have to call ScrollV\n");
|
|
ScrollV(xs, ys, xe, ye, 1, bce);
|
|
y--;
|
|
}
|
|
}
|
|
else if (y == D_bot) /* remove unusable region? */
|
|
ChangeScrollRegion(0, D_height - 1);
|
|
if (D_x != D_width || D_y != y)
|
|
{
|
|
if (D_CLP && y >= 0) /* don't even try if !LP */
|
|
RefreshLine(y, D_width - 1, D_width - 1, 0);
|
|
debug2("- refresh last char -> x,y now %d,%d\n", D_x, D_y);
|
|
if (D_x != D_width || D_y != y) /* sorry, no bonus */
|
|
{
|
|
if (y == ye)
|
|
ScrollV(xs, ys, xe, ye, 1, bce);
|
|
GotoPos(xs, y == ye || y == D_height - 1 ? y : y + 1);
|
|
}
|
|
}
|
|
debug("- writeing new char");
|
|
if (y != ye && y < D_height - 1)
|
|
y++;
|
|
if (ins != D_insert)
|
|
InsertMode(ins);
|
|
if (ins && !D_insert)
|
|
{
|
|
InsChar(c, 0, xe, y, 0);
|
|
debug2(" -> done with insert (%d,%d)\n", D_x, D_y);
|
|
return;
|
|
}
|
|
D_y = y;
|
|
D_x = 0;
|
|
SetRendition(c);
|
|
RAW_PUTCHAR(c->image);
|
|
#ifdef DW_CHARS
|
|
if (c->mbcs)
|
|
{
|
|
# ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
D_rend.font = 0;
|
|
# endif
|
|
RAW_PUTCHAR(c->mbcs);
|
|
}
|
|
#endif
|
|
debug2(" -> done (%d,%d)\n", D_x, D_y);
|
|
}
|
|
|
|
int
|
|
ResizeDisplay(wi, he)
|
|
int wi, he;
|
|
{
|
|
ASSERT(display);
|
|
debug2("ResizeDisplay: to (%d,%d).\n", wi, he);
|
|
if (D_width == wi && D_height == he)
|
|
{
|
|
debug("ResizeDisplay: No change\n");
|
|
return 0;
|
|
}
|
|
if (D_width != wi && (D_height == he || !D_CWS) && D_CZ0 && (wi == Z0width || wi == Z1width))
|
|
{
|
|
debug("ResizeDisplay: using Z0/Z1\n");
|
|
AddCStr(wi == Z0width ? D_CZ0 : D_CZ1);
|
|
ChangeScreenSize(wi, D_height, 0);
|
|
return (he == D_height) ? 0 : -1;
|
|
}
|
|
if (D_CWS)
|
|
{
|
|
debug("ResizeDisplay: using WS\n");
|
|
AddCStr(tgoto(D_CWS, wi, he));
|
|
ChangeScreenSize(wi, he, 0);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
ChangeScrollRegion(newtop, newbot)
|
|
int newtop, newbot;
|
|
{
|
|
if (display == 0)
|
|
return;
|
|
if (newtop == newbot)
|
|
return; /* xterm etc can't do it */
|
|
if (newtop == -1)
|
|
newtop = 0;
|
|
if (newbot == -1)
|
|
newbot = D_height - 1;
|
|
if (D_CS == 0)
|
|
{
|
|
D_top = 0;
|
|
D_bot = D_height - 1;
|
|
return;
|
|
}
|
|
if (D_top == newtop && D_bot == newbot)
|
|
return;
|
|
debug2("ChangeScrollRegion: (%d - %d)\n", newtop, newbot);
|
|
AddCStr(tgoto(D_CS, newbot, newtop));
|
|
D_top = newtop;
|
|
D_bot = newbot;
|
|
D_y = D_x = -1; /* Just in case... */
|
|
}
|
|
|
|
#ifdef RXVT_OSC
|
|
#define WT_FLAG "2" /* change to "0" to set both title and icon */
|
|
|
|
void
|
|
SetXtermOSC(i, s, t)
|
|
int i;
|
|
char *s;
|
|
char *t;
|
|
{
|
|
static char *oscs[][2] = {
|
|
{ WT_FLAG ";", "screen" }, /* set window title */
|
|
{ "11;", ""}, /* background RGB */
|
|
{ "20;", "" }, /* background */
|
|
{ "39;", "black" }, /* default foreground (black?) */
|
|
{ "49;", "white" } /* default background (white?) */
|
|
};
|
|
|
|
ASSERT(display);
|
|
if (!D_CXT)
|
|
return;
|
|
if (!s)
|
|
s = "";
|
|
if (!D_xtermosc[i] && !*s)
|
|
return;
|
|
if (i == 0 && !D_xtermosc[0])
|
|
AddStr("\033[22;" WT_FLAG "t"); /* stack titles (xterm patch #251) */
|
|
if (!*s)
|
|
s = oscs[i][1];
|
|
D_xtermosc[i] = 1;
|
|
AddStr("\033]");
|
|
AddStr(oscs[i][0]);
|
|
AddStr(s);
|
|
AddStr(t);
|
|
}
|
|
|
|
void
|
|
ClearAllXtermOSC()
|
|
{
|
|
int i;
|
|
for (i = 4; i >= 0; i--)
|
|
SetXtermOSC(i, 0, "\a");
|
|
if (D_xtermosc[0])
|
|
AddStr("\033[23;" WT_FLAG "t"); /* unstack titles (xterm patch #251) */
|
|
}
|
|
#undef WT_FLAG
|
|
#endif
|
|
|
|
/*
|
|
* Output buffering routines
|
|
*/
|
|
|
|
void
|
|
AddStr(str)
|
|
char *str;
|
|
{
|
|
register char c;
|
|
|
|
ASSERT(display);
|
|
|
|
#ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
{
|
|
while ((c = *str++))
|
|
AddUtf8((unsigned char)c);
|
|
return;
|
|
}
|
|
#endif
|
|
while ((c = *str++))
|
|
AddChar(c);
|
|
}
|
|
|
|
void
|
|
AddStrn(str, n)
|
|
char *str;
|
|
int n;
|
|
{
|
|
register char c;
|
|
|
|
ASSERT(display);
|
|
#ifdef UTF8
|
|
if (D_encoding == UTF8)
|
|
{
|
|
while ((c = *str++) && n-- > 0)
|
|
AddUtf8((unsigned char)c);
|
|
}
|
|
else
|
|
#endif
|
|
while ((c = *str++) && n-- > 0)
|
|
AddChar(c);
|
|
while (n-- > 0)
|
|
AddChar(' ');
|
|
}
|
|
|
|
void
|
|
Flush(progress)
|
|
int progress;
|
|
{
|
|
register int l;
|
|
int wr;
|
|
register char *p;
|
|
|
|
ASSERT(display);
|
|
l = D_obufp - D_obuf;
|
|
debug1("Flush(): %d\n", l);
|
|
if (l == 0)
|
|
return;
|
|
ASSERT(l + D_obuffree == D_obuflen);
|
|
if (D_userfd < 0)
|
|
{
|
|
D_obuffree += l;
|
|
D_obufp = D_obuf;
|
|
return;
|
|
}
|
|
p = D_obuf;
|
|
if (!progress)
|
|
{
|
|
if (fcntl(D_userfd, F_SETFL, 0))
|
|
debug1("Warning: BLOCK fcntl failed: %d\n", errno);
|
|
}
|
|
while (l)
|
|
{
|
|
if (progress)
|
|
{
|
|
fd_set w;
|
|
FD_ZERO(&w);
|
|
FD_SET(D_userfd, &w);
|
|
struct timeval t;
|
|
t.tv_sec = progress;
|
|
t.tv_usec = 0;
|
|
wr = select(FD_SETSIZE, (fd_set *)0, &w, (fd_set *)0, &t);
|
|
if (wr == -1)
|
|
{
|
|
if (errno == EINTR)
|
|
continue;
|
|
debug1("Warning: select failed: %d\n", errno);
|
|
break;
|
|
}
|
|
if (wr == 0)
|
|
{
|
|
/* no progress after 3 seconds. sorry. */
|
|
debug1("Warning: no progress after %d seconds\n", progress);
|
|
break;
|
|
}
|
|
}
|
|
wr = write(D_userfd, p, l);
|
|
if (wr <= 0)
|
|
{
|
|
if (errno == EINTR)
|
|
continue;
|
|
debug1("Writing to display: %d\n", errno);
|
|
break;
|
|
}
|
|
D_obuffree += wr;
|
|
p += wr;
|
|
l -= wr;
|
|
}
|
|
if (l)
|
|
debug1("Warning: Flush could not write %d bytes\n", l);
|
|
D_obuffree += l;
|
|
D_obufp = D_obuf;
|
|
if (!progress)
|
|
{
|
|
if (fcntl(D_userfd, F_SETFL, FNBLOCK))
|
|
debug1("Warning: NBLOCK fcntl failed: %d\n", errno);
|
|
}
|
|
if (D_blocked == 1)
|
|
D_blocked = 0;
|
|
D_blocked_fuzz = 0;
|
|
}
|
|
|
|
void
|
|
freetty()
|
|
{
|
|
if (D_userfd >= 0)
|
|
close(D_userfd);
|
|
debug1("did freetty %d\n", D_userfd);
|
|
D_userfd = -1;
|
|
D_obufp = 0;
|
|
D_obuffree = 0;
|
|
if (D_obuf)
|
|
free(D_obuf);
|
|
D_obuf = 0;
|
|
D_obuflen = 0;
|
|
D_obuflenmax = -D_obufmax;
|
|
D_blocked = 0;
|
|
D_blocked_fuzz = 0;
|
|
}
|
|
|
|
/*
|
|
* Asynchronous output routines by
|
|
* Tim MacKenzie (tym@dibbler.cs.monash.edu.au)
|
|
*/
|
|
|
|
void
|
|
Resize_obuf()
|
|
{
|
|
register int ind;
|
|
|
|
ASSERT(display);
|
|
if (D_status_obuffree >= 0)
|
|
{
|
|
ASSERT(D_obuffree == -1);
|
|
RemoveStatusMinWait();
|
|
if (--D_obuffree > 0) /* redo AddChar decrement */
|
|
return;
|
|
}
|
|
if (D_obuflen && D_obuf)
|
|
{
|
|
ind = D_obufp - D_obuf;
|
|
D_obuflen += GRAIN;
|
|
D_obuffree += GRAIN;
|
|
D_obuf = realloc(D_obuf, D_obuflen);
|
|
}
|
|
else
|
|
{
|
|
ind = 0;
|
|
D_obuflen = GRAIN;
|
|
D_obuffree = GRAIN;
|
|
D_obuf = malloc(D_obuflen);
|
|
}
|
|
if (!D_obuf)
|
|
Panic(0, "Out of memory");
|
|
D_obufp = D_obuf + ind;
|
|
D_obuflenmax = D_obuflen - D_obufmax;
|
|
debug1("ResizeObuf: resized to %d\n", D_obuflen);
|
|
}
|
|
|
|
void
|
|
DisplaySleep1000(n, eat)
|
|
int n;
|
|
int eat;
|
|
{
|
|
char buf;
|
|
fd_set r;
|
|
struct timeval t;
|
|
|
|
if (n <= 0)
|
|
return;
|
|
if (!display)
|
|
{
|
|
debug("DisplaySleep has no display sigh\n");
|
|
sleep1000(n);
|
|
return;
|
|
}
|
|
t.tv_usec = (n % 1000) * 1000;
|
|
t.tv_sec = n / 1000;
|
|
FD_ZERO(&r);
|
|
FD_SET(D_userfd, &r);
|
|
if (select(FD_SETSIZE, &r, (fd_set *)0, (fd_set *)0, &t) > 0)
|
|
{
|
|
debug("display activity stopped sleep\n");
|
|
if (eat)
|
|
read(D_userfd, &buf, 1);
|
|
}
|
|
debug2("DisplaySleep(%d) ending, eat was %d\n", n, eat);
|
|
}
|
|
|
|
#ifdef AUTO_NUKE
|
|
void
|
|
NukePending()
|
|
{/* Nuke pending output in current display, clear screen */
|
|
register int len;
|
|
int oldtop = D_top, oldbot = D_bot;
|
|
struct mchar oldrend;
|
|
int oldkeypad = D_keypad, oldcursorkeys = D_cursorkeys;
|
|
int oldcurvis = D_curvis;
|
|
int oldmouse = D_mouse;
|
|
int oldextmouse = D_extmouse;
|
|
|
|
oldrend = D_rend;
|
|
len = D_obufp - D_obuf;
|
|
debug1("NukePending: nuking %d chars\n", len);
|
|
|
|
/* Throw away any output that we can... */
|
|
# ifdef POSIX
|
|
tcflush(D_userfd, TCOFLUSH);
|
|
# else
|
|
# ifdef TCFLSH
|
|
(void) ioctl(D_userfd, TCFLSH, (char *) 1);
|
|
# endif
|
|
# endif
|
|
|
|
D_obufp = D_obuf;
|
|
D_obuffree += len;
|
|
D_top = D_bot = -1;
|
|
AddCStr(D_IS);
|
|
AddCStr(D_TI);
|
|
/* Turn off all attributes. (Tim MacKenzie) */
|
|
if (D_ME)
|
|
AddCStr(D_ME);
|
|
else
|
|
{
|
|
#ifdef COLOR
|
|
if (D_hascolor)
|
|
AddStr("\033[m"); /* why is D_ME not set? */
|
|
#endif
|
|
AddCStr(D_SE);
|
|
AddCStr(D_UE);
|
|
}
|
|
/* Check for toggle */
|
|
if (D_IM && strcmp(D_IM, D_EI))
|
|
AddCStr(D_EI);
|
|
D_insert = 0;
|
|
/* Check for toggle */
|
|
#ifdef MAPKEYS
|
|
if (D_KS && strcmp(D_KS, D_KE))
|
|
AddCStr(D_KS);
|
|
if (D_CCS && strcmp(D_CCS, D_CCE))
|
|
AddCStr(D_CCS);
|
|
#else
|
|
if (D_KS && strcmp(D_KS, D_KE))
|
|
AddCStr(D_KE);
|
|
D_keypad = 0;
|
|
if (D_CCS && strcmp(D_CCS, D_CCE))
|
|
AddCStr(D_CCE);
|
|
D_cursorkeys = 0;
|
|
#endif
|
|
AddCStr(D_CE0);
|
|
D_rend = mchar_null;
|
|
D_atyp = 0;
|
|
AddCStr(D_DS);
|
|
D_hstatus = 0;
|
|
AddCStr(D_VE);
|
|
D_curvis = 0;
|
|
ChangeScrollRegion(oldtop, oldbot);
|
|
SetRendition(&oldrend);
|
|
KeypadMode(oldkeypad);
|
|
CursorkeysMode(oldcursorkeys);
|
|
CursorVisibility(oldcurvis);
|
|
MouseMode(oldmouse);
|
|
ExtMouseMode(oldextmouse);
|
|
if (D_CWS)
|
|
{
|
|
debug("ResizeDisplay: using WS\n");
|
|
AddCStr(tgoto(D_CWS, D_width, D_height));
|
|
}
|
|
else if (D_CZ0 && (D_width == Z0width || D_width == Z1width))
|
|
{
|
|
debug("ResizeDisplay: using Z0/Z1\n");
|
|
AddCStr(D_width == Z0width ? D_CZ0 : D_CZ1);
|
|
}
|
|
}
|
|
#endif /* AUTO_NUKE */
|
|
|
|
#ifdef linux
|
|
/* linux' select can't handle flow control, so wait 100ms if
|
|
* we get EAGAIN
|
|
*/
|
|
static void
|
|
disp_writeev_eagain(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
display = (struct display *)data;
|
|
evdeq(&D_writeev);
|
|
D_writeev.type = EV_WRITE;
|
|
D_writeev.handler = disp_writeev_fn;
|
|
evenq(&D_writeev);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
disp_writeev_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
int len, size = OUTPUT_BLOCK_SIZE;
|
|
|
|
display = (struct display *)data;
|
|
len = D_obufp - D_obuf;
|
|
if (len < size)
|
|
size = len;
|
|
if (D_status_obufpos && size > D_status_obufpos)
|
|
size = D_status_obufpos;
|
|
ASSERT(len >= 0);
|
|
size = write(D_userfd, D_obuf, size);
|
|
if (size >= 0)
|
|
{
|
|
len -= size;
|
|
if (len)
|
|
{
|
|
bcopy(D_obuf + size, D_obuf, len);
|
|
debug2("ASYNC: wrote %d - remaining %d\n", size, len);
|
|
}
|
|
D_obufp -= size;
|
|
D_obuffree += size;
|
|
if (D_status_obufpos)
|
|
{
|
|
D_status_obufpos -= size;
|
|
if (!D_status_obufpos)
|
|
{
|
|
debug("finished writing the status message\n");
|
|
/* we're finished displaying the message! */
|
|
if (D_status == STATUS_ON_WIN)
|
|
{
|
|
/* setup continue trigger */
|
|
D_status_obuflen = D_obuflen;
|
|
D_status_obuffree = D_obuffree;
|
|
/* setting obbuffree to 0 will make AddChar call
|
|
* ResizeObuf */
|
|
D_obuffree = D_obuflen = 0;
|
|
}
|
|
gettimeofday(&D_status_time, NULL);
|
|
SetTimeout(&D_statusev, MsgWait);
|
|
evenq(&D_statusev);
|
|
#ifdef HAVE_BRAILLE
|
|
RefreshBraille(); /* let user see multiple Msg()s */
|
|
#endif
|
|
}
|
|
}
|
|
if (D_blocked_fuzz)
|
|
{
|
|
D_blocked_fuzz -= size;
|
|
if (D_blocked_fuzz < 0)
|
|
D_blocked_fuzz = 0;
|
|
}
|
|
if (D_blockedev.queued)
|
|
{
|
|
if (D_obufp - D_obuf > D_obufmax / 2)
|
|
{
|
|
debug2("%s: resetting timeout to %g secs\n", D_usertty, D_nonblock/1000.);
|
|
SetTimeout(&D_blockedev, D_nonblock);
|
|
}
|
|
else
|
|
{
|
|
debug1("%s: deleting blocked timeout\n", D_usertty);
|
|
evdeq(&D_blockedev);
|
|
}
|
|
}
|
|
if (D_blocked == 1 && D_obuf == D_obufp)
|
|
{
|
|
/* empty again, restart output */
|
|
debug1("%s: buffer empty, unblocking\n", D_usertty);
|
|
D_blocked = 0;
|
|
Activate(D_fore ? D_fore->w_norefresh : 0);
|
|
D_blocked_fuzz = D_obufp - D_obuf;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef linux
|
|
/* linux flow control is badly broken */
|
|
if (errno == EAGAIN)
|
|
{
|
|
evdeq(&D_writeev);
|
|
D_writeev.type = EV_TIMEOUT;
|
|
D_writeev.handler = disp_writeev_eagain;
|
|
SetTimeout(&D_writeev, 100);
|
|
evenq(&D_writeev);
|
|
}
|
|
#endif
|
|
if (errno != EINTR && errno != EAGAIN)
|
|
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
|
|
if (errno != EWOULDBLOCK)
|
|
#endif
|
|
Msg(errno, "Error writing output to display");
|
|
}
|
|
}
|
|
|
|
/* maximum mouse sequence length is SGR: ESC [ < b ; x ; y M */
|
|
#define MAX_MOUSE_SEQUENCE (3+10+1+10+1+10+1)
|
|
|
|
static void
|
|
disp_readev_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
/* We have to intercept mouse sequences in order to translate them
|
|
* properly, and an incoming sequence could be spread over multiple
|
|
* I/O reads. When this occurs, we buffer intermediate state in
|
|
* D_mouse_params, and then use the reservation at the front of
|
|
* bufspace to prepend the completed and translated mouse sequence.
|
|
*/
|
|
int size;
|
|
char bufspace[MAX_MOUSE_SEQUENCE + IOSIZE];
|
|
unsigned char *buf = (unsigned char*)bufspace + MAX_MOUSE_SEQUENCE;
|
|
|
|
struct canvas *cv;
|
|
|
|
display = (struct display *)data;
|
|
|
|
/* Hmmmm... a bit ugly... */
|
|
if (D_forecv)
|
|
for (cv = D_forecv->c_layer->l_cvlist; cv; cv = cv->c_lnext)
|
|
{
|
|
display = cv->c_display;
|
|
if (D_status == STATUS_ON_WIN)
|
|
RemoveStatus();
|
|
}
|
|
|
|
display = (struct display *)data;
|
|
if (D_fore == 0)
|
|
size = IOSIZE;
|
|
else
|
|
{
|
|
#ifdef PSEUDOS
|
|
if (W_UWP(D_fore))
|
|
size = sizeof(D_fore->w_pwin->p_inbuf) - D_fore->w_pwin->p_inlen;
|
|
else
|
|
#endif
|
|
size = sizeof(D_fore->w_inbuf) - D_fore->w_inlen;
|
|
}
|
|
|
|
if (size > IOSIZE)
|
|
size = IOSIZE;
|
|
if (size <= 0)
|
|
size = 1; /* Always allow one char for command keys */
|
|
|
|
size = read(D_userfd, buf, size);
|
|
if (size < 0)
|
|
{
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
return;
|
|
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
|
|
if (errno == EWOULDBLOCK)
|
|
return;
|
|
#endif
|
|
debug1("Read error: %d - hangup!\n", errno);
|
|
Hangup();
|
|
sleep(1);
|
|
return;
|
|
}
|
|
else if (size == 0)
|
|
{
|
|
debug("Found EOF - hangup!\n");
|
|
Hangup();
|
|
sleep(1);
|
|
return;
|
|
}
|
|
if (D_blocked == 4)
|
|
{
|
|
D_blocked = 0;
|
|
#ifdef BLANKER_PRG
|
|
KillBlanker();
|
|
#endif
|
|
Activate(D_fore ? D_fore->w_norefresh : 0);
|
|
ResetIdle();
|
|
return;
|
|
}
|
|
#ifdef ZMODEM
|
|
if (D_blocked > 1) /* 2, 3 */
|
|
{
|
|
char *bufp;
|
|
struct win *p;
|
|
|
|
flayer = 0;
|
|
for (p = windows; p ; p = p->w_next)
|
|
if (p->w_zdisplay == display)
|
|
{
|
|
flayer = &p->w_layer;
|
|
bufp = (char *)buf;
|
|
while (size > 0)
|
|
LayProcess(&bufp, &size);
|
|
return;
|
|
}
|
|
debug("zmodem window gone, deblocking display");
|
|
zmodem_abort(0, display);
|
|
}
|
|
#endif
|
|
if (idletimo > 0)
|
|
ResetIdle();
|
|
if (D_fore)
|
|
D_fore->w_lastdisp = display;
|
|
|
|
if (D_mouse && D_forecv)
|
|
{
|
|
unsigned char *bp = (unsigned char *)buf;
|
|
unsigned char *end = bp + size;
|
|
unsigned char *mark = NULL;
|
|
|
|
/* When mouse mode is enabled, buffer up incoming CSI until we
|
|
* know whether it is a mouse sequence. If not a mouse event,
|
|
* emit the CSI unchanged; if an invalid mouse event, swallow
|
|
* it; otherwise, translate the sequence and emit it.
|
|
*
|
|
* Supported mouse events take two flavors.
|
|
*
|
|
* VT200: CSI M Cb Cx Cy
|
|
* SGR: CSI < Ps ; Ps ; Ps M|m
|
|
*
|
|
* UTF-8 and UXRVT modes are explicitly rejected because they
|
|
* introduce incompatibilities with other control sequences.
|
|
*
|
|
* NOTE: applications wishing to use SGR mode will normally
|
|
* enable VT200 mode first as a fallback in case the terminal
|
|
* does not support SGR mode. Thus, we must expect to receive
|
|
* either mode's sequences when mouse mode is enabled. We will
|
|
* dutifully translate whatever arrives, without attempting to
|
|
* convert between modes on behalf of the application.
|
|
*/
|
|
switch (D_mouse_parse.state)
|
|
{
|
|
case CSI_PY:
|
|
case CSI_PX:
|
|
case CSI_PB:
|
|
/* Partial mouse sequence; do not restore suppressed
|
|
* characters. We will emit a translated version (if valid)
|
|
* or swallow them (otherwise).
|
|
*/
|
|
break;
|
|
|
|
case CSI_BEGIN:
|
|
/* Partial CSI; restore suppressed characters in case it
|
|
* turns out not to be a mouse sequence after all.
|
|
*/
|
|
*(--buf) = '[';
|
|
++size;
|
|
/* fall through */
|
|
|
|
case CSI_ESC_SEEN:
|
|
/* Escape character; restore it in case this turns out not
|
|
* to be the start of a mouse sequence after all.
|
|
*/
|
|
*(--buf) = '\033';
|
|
++size;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
};
|
|
|
|
while (bp != end)
|
|
{
|
|
unsigned char c = *(bp++);
|
|
|
|
switch (D_mouse_parse.state)
|
|
{
|
|
case CSI_INACTIVE:
|
|
if (c == '\033')
|
|
{
|
|
/* potential escape sequence */
|
|
mark = bp-1;
|
|
D_mouse_parse.state = CSI_ESC_SEEN;
|
|
}
|
|
break;
|
|
|
|
case CSI_ESC_SEEN:
|
|
if (c == '[')
|
|
{
|
|
/* continue buffering an escape sequence */
|
|
D_mouse_parse.state = CSI_BEGIN;
|
|
}
|
|
else
|
|
D_mouse_parse.state = CSI_INACTIVE;
|
|
break;
|
|
|
|
case CSI_BEGIN:
|
|
if (c == 'M')
|
|
{
|
|
/* VT200 mouse sequence */
|
|
D_mouse_parse.state = CSI_PB;
|
|
D_mouse_parse.sgrmode = 0;
|
|
}
|
|
else if (c == '<')
|
|
{
|
|
/* SGR mouse sequence */
|
|
D_mouse_parse.state = CSI_PB;
|
|
D_mouse_parse.params[D_mouse_parse.state] = 0;
|
|
D_mouse_parse.sgrmode = 1;
|
|
}
|
|
else
|
|
D_mouse_parse.state = CSI_INACTIVE;
|
|
break;
|
|
|
|
case CSI_PB:
|
|
case CSI_PX:
|
|
case CSI_PY:
|
|
if (D_mouse_parse.sgrmode)
|
|
{
|
|
/* SGR mode: parse decimal numbers */
|
|
if ('0' <= c && c <= '9')
|
|
{
|
|
D_mouse_parse.params[D_mouse_parse.state] *= 10;
|
|
D_mouse_parse.params[D_mouse_parse.state] += c - '0';
|
|
}
|
|
else if (D_mouse_parse.state == CSI_PY)
|
|
{
|
|
if (c == 'M' || c == 'm')
|
|
D_mouse_parse.state = CSI_DONE;
|
|
else
|
|
D_mouse_parse.state = CSI_INVALID;
|
|
}
|
|
else if (c == ';')
|
|
{
|
|
D_mouse_parse.state++;
|
|
D_mouse_parse.params[D_mouse_parse.state] = 0;
|
|
}
|
|
else
|
|
D_mouse_parse.state = CSI_INVALID;
|
|
}
|
|
else
|
|
{
|
|
/* VT200 mode: read raw binary values */
|
|
D_mouse_parse.params[D_mouse_parse.state++] = c;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (D_mouse_parse.state == CSI_INVALID)
|
|
{
|
|
/* swallow invalid sequence, but emit whatever came before */
|
|
if (buf < mark)
|
|
disp_processinput(display, buf, mark-buf);
|
|
|
|
buf = bp;
|
|
size = end - bp;
|
|
D_mouse_parse.state = CSI_INACTIVE;
|
|
}
|
|
else if (D_mouse_parse.state == CSI_DONE)
|
|
{
|
|
/* emit whatever came before this sequence */
|
|
if (buf < mark)
|
|
disp_processinput(display, buf, mark-buf);
|
|
|
|
buf = bp;
|
|
size = end - bp;
|
|
|
|
int x = D_mouse_parse.params[CSI_PX];
|
|
int y = D_mouse_parse.params[CSI_PY];
|
|
int bias = D_mouse_parse.sgrmode? 1 : 33;
|
|
|
|
x -= bias;
|
|
y -= bias;
|
|
|
|
if (x >= D_forecv->c_xs && x <= D_forecv->c_xe && y >= D_forecv->c_ys && y <= D_forecv->c_ye)
|
|
{
|
|
if ((D_fore && D_fore->w_mouse) || (D_mousetrack && D_forecv->c_layer->l_mode == 1))
|
|
{
|
|
/* Send clicks only if the window is expecting clicks */
|
|
x -= D_forecv->c_xoff;
|
|
y -= D_forecv->c_yoff;
|
|
|
|
if (x >= 0 && x < D_forecv->c_layer->l_width && y >= 0 && y < D_forecv->c_layer->l_height)
|
|
{
|
|
char tmp[MAX_MOUSE_SEQUENCE+1];
|
|
int n;
|
|
|
|
x += bias;
|
|
y += bias;
|
|
|
|
if (D_mouse_parse.sgrmode)
|
|
{
|
|
n = snprintf(
|
|
tmp, MAX_MOUSE_SEQUENCE, "\033[<%d;%d;%d%c",
|
|
D_mouse_parse.params[CSI_PB], x, y, c);
|
|
}
|
|
else
|
|
{
|
|
n = snprintf(
|
|
tmp, MAX_MOUSE_SEQUENCE, "\033[M%c%c%c",
|
|
D_mouse_parse.params[CSI_PB], x, y);
|
|
}
|
|
|
|
if (n > MAX_MOUSE_SEQUENCE)
|
|
n = MAX_MOUSE_SEQUENCE;
|
|
|
|
/* emit sequence */
|
|
buf -= n;
|
|
size += n;
|
|
memcpy(buf, tmp, n);
|
|
}
|
|
}
|
|
}
|
|
else if (D_mousetrack)
|
|
{
|
|
/* 'focus' to the clicked region, only on mouse up */
|
|
int focus = 0;
|
|
if (D_mouse_parse.sgrmode)
|
|
focus = (c == 'm');
|
|
else
|
|
focus = (D_mouse_parse.params[CSI_PB] == '#');
|
|
|
|
struct canvas *cv = FindCanvas(x, y);
|
|
if (focus && cv)
|
|
{
|
|
SetForeCanvas(display, cv);
|
|
/* XXX: Do we want to reset the input buffer? */
|
|
}
|
|
}
|
|
|
|
D_mouse_parse.state = CSI_INACTIVE;
|
|
}
|
|
}
|
|
|
|
if (D_mouse_parse.state != CSI_INACTIVE)
|
|
{
|
|
/* suppress partial sequence at end */
|
|
size = mark? mark - buf : 0;
|
|
}
|
|
}
|
|
|
|
if (size > 0)
|
|
disp_processinput(display, buf, size);
|
|
}
|
|
|
|
static void
|
|
disp_processinput(display, buf, size)
|
|
struct display* display;
|
|
unsigned char* buf;
|
|
int size;
|
|
{
|
|
#ifdef ENCODINGS
|
|
if (D_encoding != (D_forecv ? D_forecv->c_layer->l_encoding : 0))
|
|
{
|
|
int i, j, c, enc;
|
|
char buf2[IOSIZE * 2 + 10];
|
|
enc = D_forecv ? D_forecv->c_layer->l_encoding : 0;
|
|
for (i = j = 0; i < size; i++)
|
|
{
|
|
c = ((unsigned char *)buf)[i];
|
|
c = DecodeChar(c, D_encoding, &D_decodestate);
|
|
if (c == -2)
|
|
i--; /* try char again */
|
|
if (c < 0)
|
|
continue;
|
|
if (pastefont)
|
|
{
|
|
int font = 0;
|
|
j += EncodeChar(buf2 + j, c, enc, &font);
|
|
j += EncodeChar(buf2 + j, -1, enc, &font);
|
|
}
|
|
else
|
|
j += EncodeChar(buf2 + j, c, enc, 0);
|
|
if (j > (int)sizeof(buf2) - 10) /* just in case... */
|
|
break;
|
|
}
|
|
(*D_processinput)(buf2, j);
|
|
return;
|
|
}
|
|
#endif
|
|
(*D_processinput)((char *)buf, size);
|
|
}
|
|
|
|
static void
|
|
disp_status_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
display = (struct display *)data;
|
|
debug1("disp_status_fn for display %lx\n", (long)display);
|
|
if (D_status)
|
|
RemoveStatus();
|
|
}
|
|
|
|
static void
|
|
disp_hstatus_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
display = (struct display *)data;
|
|
if (D_status == STATUS_ON_HS)
|
|
{
|
|
SetTimeout(ev, 1);
|
|
evenq(ev);
|
|
return;
|
|
}
|
|
RefreshHStatus();
|
|
}
|
|
|
|
static void
|
|
disp_blocked_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
struct win *p;
|
|
|
|
display = (struct display *)data;
|
|
debug1("blocked timeout %s\n", D_usertty);
|
|
if (D_obufp - D_obuf > D_obufmax + D_blocked_fuzz)
|
|
{
|
|
debug("stopping output to display\n");
|
|
D_blocked = 1;
|
|
/* re-enable all windows */
|
|
for (p = windows; p; p = p->w_next)
|
|
if (p->w_readev.condneg == &D_obuflenmax)
|
|
{
|
|
debug1("freeing window #%d\n", p->w_number);
|
|
p->w_readev.condpos = p->w_readev.condneg = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MAPKEYS
|
|
static void
|
|
disp_map_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
char *p;
|
|
int l, i;
|
|
unsigned char *q;
|
|
display = (struct display *)data;
|
|
debug("Flushing map sequence\n");
|
|
if (!(l = D_seql))
|
|
return;
|
|
p = (char *)D_seqp - l;
|
|
D_seqp = D_kmaps + 3;
|
|
D_seql = 0;
|
|
if ((q = D_seqh) != 0)
|
|
{
|
|
D_seqh = 0;
|
|
i = q[0] << 8 | q[1];
|
|
i &= ~KMAP_NOTIMEOUT;
|
|
debug1("Mapping former hit #%d - ", i);
|
|
debug2("%d(%s) - ", q[2], q + 3);
|
|
if (StuffKey(i))
|
|
ProcessInput2((char *)q + 3, q[2]);
|
|
if (display == 0)
|
|
return;
|
|
l -= q[2];
|
|
p += q[2];
|
|
}
|
|
else
|
|
D_dontmap = 1;
|
|
ProcessInput(p, l);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
disp_idle_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
struct display *olddisplay;
|
|
display = (struct display *)data;
|
|
debug("idle timeout\n");
|
|
if (idletimo <= 0 || idleaction.nr == RC_ILLEGAL)
|
|
return;
|
|
olddisplay = display;
|
|
flayer = D_forecv->c_layer;
|
|
fore = D_fore;
|
|
DoAction(&idleaction, -1);
|
|
if (idleaction.nr == RC_BLANKER)
|
|
return;
|
|
for (display = displays; display; display = display->d_next)
|
|
if (olddisplay == display)
|
|
break;
|
|
if (display)
|
|
ResetIdle();
|
|
}
|
|
|
|
void
|
|
ResetIdle()
|
|
{
|
|
if (idletimo > 0)
|
|
{
|
|
SetTimeout(&D_idleev, idletimo);
|
|
if (!D_idleev.queued)
|
|
evenq(&D_idleev);
|
|
}
|
|
else
|
|
evdeq(&D_idleev);
|
|
}
|
|
|
|
|
|
#ifdef BLANKER_PRG
|
|
|
|
static void
|
|
disp_blanker_fn(ev, data)
|
|
struct event *ev;
|
|
char *data;
|
|
{
|
|
char buf[IOSIZE], *b;
|
|
int size;
|
|
|
|
display = (struct display *)data;
|
|
size = read(D_blankerev.fd, buf, IOSIZE);
|
|
if (size <= 0)
|
|
{
|
|
evdeq(&D_blankerev);
|
|
close(D_blankerev.fd);
|
|
D_blankerev.fd = -1;
|
|
return;
|
|
}
|
|
for (b = buf; size; size--)
|
|
AddChar(*b++);
|
|
}
|
|
|
|
void
|
|
KillBlanker()
|
|
{
|
|
int oldtop = D_top, oldbot = D_bot;
|
|
struct mchar oldrend;
|
|
|
|
if (D_blankerev.fd == -1)
|
|
return;
|
|
if (D_blocked == 4)
|
|
D_blocked = 0;
|
|
evdeq(&D_blankerev);
|
|
close(D_blankerev.fd);
|
|
D_blankerev.fd = -1;
|
|
Kill(D_blankerpid, SIGHUP);
|
|
D_top = D_bot = -1;
|
|
oldrend = D_rend;
|
|
if (D_ME)
|
|
{
|
|
AddCStr(D_ME);
|
|
AddCStr(D_ME);
|
|
}
|
|
else
|
|
{
|
|
#ifdef COLOR
|
|
if (D_hascolor)
|
|
AddStr("\033[m\033[m"); /* why is D_ME not set? */
|
|
#endif
|
|
AddCStr(D_SE);
|
|
AddCStr(D_UE);
|
|
}
|
|
AddCStr(D_VE);
|
|
AddCStr(D_CE0);
|
|
D_rend = mchar_null;
|
|
D_atyp = 0;
|
|
D_curvis = 0;
|
|
D_x = D_y = -1;
|
|
ChangeScrollRegion(oldtop, oldbot);
|
|
SetRendition(&oldrend);
|
|
ClearAll();
|
|
}
|
|
|
|
void
|
|
RunBlanker(cmdv)
|
|
char **cmdv;
|
|
{
|
|
char *m;
|
|
int pid;
|
|
int slave = -1;
|
|
int ptype = 0;
|
|
char termname[MAXTERMLEN + 6];
|
|
#ifndef TIOCSWINSZ
|
|
char libuf[20], cobuf[20];
|
|
#endif
|
|
char **np;
|
|
|
|
strcpy(termname, "TERM=");
|
|
strncpy(termname + 5, D_termname, MAXTERMLEN - 6);
|
|
termname[sizeof(termname) - 1] = 0;
|
|
KillBlanker();
|
|
D_blankerpid = -1;
|
|
if ((D_blankerev.fd = OpenDevice(cmdv, 0, &ptype, &m)) == -1)
|
|
{
|
|
Msg(0, "OpenDevice failed");
|
|
return;
|
|
}
|
|
#ifdef O_NOCTTY
|
|
if (pty_preopen)
|
|
{
|
|
if ((slave = open(m, O_RDWR|O_NOCTTY)) == -1)
|
|
{
|
|
Msg(errno, "%s", m);
|
|
close(D_blankerev.fd);
|
|
D_blankerev.fd = -1;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
switch (pid = (int)fork())
|
|
{
|
|
case -1:
|
|
Msg(errno, "fork");
|
|
close(D_blankerev.fd);
|
|
D_blankerev.fd = -1;
|
|
close(slave);
|
|
return;
|
|
case 0:
|
|
displays = 0;
|
|
ServerSocket = -1;
|
|
#ifdef SIGPIPE
|
|
signal(SIGPIPE, SIG_DFL);
|
|
#endif
|
|
if (setgid(real_gid) || setuid(real_uid))
|
|
Panic(errno, "setuid/setgid");
|
|
eff_uid = real_uid;
|
|
eff_gid = real_gid;
|
|
brktty(D_userfd);
|
|
freetty();
|
|
#ifdef DEBUG
|
|
if (dfp && dfp != stderr)
|
|
fclose(dfp);
|
|
#endif
|
|
if (slave != -1)
|
|
{
|
|
close(0);
|
|
dup(slave);
|
|
close(slave);
|
|
closeallfiles(D_blankerev.fd);
|
|
slave = dup(0);
|
|
}
|
|
else
|
|
closeallfiles(D_blankerev.fd);
|
|
#ifdef DEBUG
|
|
if (dfp)
|
|
{
|
|
char buf[256];
|
|
|
|
sprintf(buf, "%s/screen.blanker", DEBUGDIR);
|
|
if ((dfp = fopen(buf, "a")) == 0)
|
|
dfp = stderr;
|
|
else
|
|
(void) chmod(buf, 0666);
|
|
}
|
|
debug1("=== RunBlanker: pid %d\n", (int)getpid());
|
|
#endif
|
|
close(0);
|
|
close(1);
|
|
close(2);
|
|
if (open(m, O_RDWR))
|
|
Panic(errno, "Cannot open %s", m);
|
|
dup(0);
|
|
dup(0);
|
|
close(D_blankerev.fd);
|
|
if (slave != -1)
|
|
close(slave);
|
|
InitPTY(0);
|
|
fgtty(0);
|
|
SetTTY(0, &D_OldMode);
|
|
np = NewEnv + 3;
|
|
*np++ = NewEnv[0];
|
|
*np++ = termname;
|
|
#ifdef TIOCSWINSZ
|
|
glwz.ws_col = D_width;
|
|
glwz.ws_row = D_height;
|
|
(void)ioctl(0, TIOCSWINSZ, (char *)&glwz);
|
|
#else
|
|
/* Always turn off nonblocking mode */
|
|
(void)fcntl(0, F_SETFL, 0);
|
|
sprintf(libuf, "LINES=%d", D_height);
|
|
sprintf(cobuf, "COLUMNS=%d", D_width);
|
|
*np++ = libuf;
|
|
*np++ = cobuf;
|
|
#endif
|
|
debug1("calling execvpe %s\n", *cmdv);
|
|
execvpe(*cmdv, cmdv, NewEnv + 3);
|
|
debug1("exec error: %d\n", errno);
|
|
Panic(errno, "Cannot exec '%s'", *cmdv);
|
|
default:
|
|
break;
|
|
}
|
|
D_blankerpid = pid;
|
|
evenq(&D_blankerev);
|
|
D_blocked = 4;
|
|
ClearAll();
|
|
close(slave);
|
|
}
|
|
|
|
#endif /* BLANKER_PRG */
|
|
|
|
|