summaryrefslogtreecommitdiffstats
path: root/display.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:28:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:28:28 +0000
commit5a5e2352c9a01f9076994915188c26c6b9036202 (patch)
tree1e1474b8b914d161946c01ba26a56db68b1fd5d3 /display.c
parentInitial commit. (diff)
downloadscreen-upstream/4.9.0.tar.xz
screen-upstream/4.9.0.zip
Adding upstream version 4.9.0.upstream/4.9.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--display.c4082
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 */
+
+