diff options
Diffstat (limited to '')
-rw-r--r-- | display.c | 4082 |
1 files changed, 4082 insertions, 0 deletions
diff --git a/display.c b/display.c new file mode 100644 index 0000000..978b0a8 --- /dev/null +++ b/display.c @@ -0,0 +1,4082 @@ +/* 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 <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) /* relativ 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 harstatus 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 %x\n", (int)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 */ + + |