1/* Copyright (c) 1993-2002
2 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Copyright (c) 1987 Oliver Laumann
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program (see the file COPYING); if not, write to the
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
20 *
21 ****************************************************************
22 */
23
24#include <sys/types.h>
25#include <signal.h>
26#include <fcntl.h>
27#ifndef sun
28# include <sys/ioctl.h>
29#endif
30
31#include "config.h"
32#include "screen.h"
33#include "extern.h"
34#include "braille.h"
35
36static int  CountChars __P((int));
37static int  DoAddChar __P((int));
38static int  BlankResize __P((int, int));
39static int  CallRewrite __P((int, int, int, int));
40static void FreeCanvas __P((struct canvas *));
41static void disp_readev_fn __P((struct event *, char *));
42static void disp_writeev_fn __P((struct event *, char *));
43#ifdef linux
44static void disp_writeev_eagain __P((struct event *, char *));
45#endif
46static void disp_status_fn __P((struct event *, char *));
47static void disp_hstatus_fn __P((struct event *, char *));
48static void disp_blocked_fn __P((struct event *, char *));
49static void cv_winid_fn __P((struct event *, char *));
50#ifdef MAPKEYS
51static void disp_map_fn __P((struct event *, char *));
52#endif
53static void disp_idle_fn __P((struct event *, char *));
54#ifdef BLANKER_PRG
55static void disp_blanker_fn __P((struct event *, char *));
56#endif
57static void WriteLP __P((int, int));
58static void INSERTCHAR __P((int));
59static void RAW_PUTCHAR __P((int));
60#ifdef COLOR
61static void SetBackColor __P((int));
62#endif
63
64
65extern struct layer *flayer;
66extern struct win *windows, *fore;
67extern struct LayFuncs WinLf;
68
69extern int  use_hardstatus;
70extern int  MsgWait, MsgMinWait;
71extern int  Z0width, Z1width;
72extern unsigned char *blank, *null;
73extern struct mline mline_blank, mline_null, mline_old;
74extern struct mchar mchar_null, mchar_blank, mchar_so;
75extern struct NewWindow nwin_default;
76extern struct action idleaction;
77
78/* XXX shouldn't be here */
79extern char *hstatusstring;
80extern char *captionstring;
81
82extern int pastefont;
83extern int idletimo;
84
85#ifdef BLANKER_PRG
86extern int pty_preopen;
87#if defined(TIOCSWINSZ) || defined(TIOCGWINSZ)
88extern struct winsize glwz;
89#endif
90extern char **NewEnv;
91extern int real_uid, real_gid;
92#endif
93
94/*
95 * tputs needs this to calculate the padding
96 */
97#ifndef NEED_OSPEED
98extern
99#endif /* NEED_OSPEED */
100short ospeed;
101
102
103struct display *display, *displays;
104#ifdef COLOR
105int  attr2color[8][4];
106int  nattr2color;
107#endif
108
109#ifndef MULTI
110struct display TheDisplay;
111#endif
112
113/*
114 *  The default values
115 */
116int defobuflimit = OBUF_MAX;
117int defnonblock = -1;
118#ifdef AUTO_NUKE
119int defautonuke = 0;
120#endif
121int captionalways;
122int hardstatusemu = HSTATUS_IGNORE;
123
124/*
125 *  Default layer management
126 */
127
128void
129DefProcess(bufp, lenp)
130char **bufp;
131int *lenp;
132{
133  *bufp += *lenp;
134  *lenp = 0;
135}
136
137void
138DefRedisplayLine(y, xs, xe, isblank)
139int y, xs, xe, isblank;
140{
141  if (isblank == 0 && y >= 0)
142    DefClearLine(y, xs, xe, 0);
143}
144
145void
146DefClearLine(y, xs, xe, bce)
147int y, xs, xe, bce;
148{
149  LClearLine(flayer, y, xs, xe, bce, (struct mline *)0);
150}
151
152/*ARGSUSED*/
153int
154DefRewrite(y, xs, xe, rend, doit)
155int y, xs, xe, doit;
156struct mchar *rend;
157{
158  return EXPENSIVE;
159}
160
161/*ARGSUSED*/
162int
163DefResize(wi, he)
164int wi, he;
165{
166  return -1;
167}
168
169void
170DefRestore()
171{
172  LAY_DISPLAYS(flayer, InsertMode(0));
173  /* ChangeScrollRegion(0, D_height - 1); */
174  LKeypadMode(flayer, 0);
175  LCursorkeysMode(flayer, 0);
176  LCursorVisibility(flayer, 0);
177  LMouseMode(flayer, 0);
178  LSetRendition(flayer, &mchar_null);
179  LSetFlow(flayer, nwin_default.flowflag & FLOW_NOW);
180}
181
182/*
183 *  Blank layer management
184 */
185
186struct LayFuncs BlankLf =
187{
188  DefProcess,
189  0,
190  DefRedisplayLine,
191  DefClearLine,
192  DefRewrite,
193  BlankResize,
194  DefRestore
195};
196
197/*ARGSUSED*/
198static int
199BlankResize(wi, he)
200int wi, he;
201{
202  flayer->l_width = wi;
203  flayer->l_height = he;
204  return 0;
205}
206
207
208/*
209 *  Generate new display, start with a blank layer.
210 *  The termcap arrays are not initialised here.
211 *  The new display is placed in the displays list.
212 */
213
214struct display *
215MakeDisplay(uname, utty, term, fd, pid, Mode)
216char *uname, *utty, *term;
217int fd, pid;
218struct mode *Mode;
219{
220  struct acluser **u;
221  struct baud_values *b;
222
223  if (!*(u = FindUserPtr(uname)) && UserAdd(uname, (char *)0, u))
224    return 0;	/* could not find or add user */
225
226#ifdef MULTI
227  if ((display = (struct display *)calloc(1, sizeof(*display))) == 0)
228    return 0;
229#else
230  if (displays)
231    return 0;
232  bzero((char *)&TheDisplay, sizeof(TheDisplay));
233  display = &TheDisplay;
234#endif
235  display->d_next = displays;
236  displays = display;
237  D_flow = 1;
238  D_nonblock = defnonblock;
239  D_userfd = fd;
240  D_readev.fd = D_writeev.fd = fd;
241  D_readev.type  = EV_READ;
242  D_writeev.type = EV_WRITE;
243  D_readev.data = D_writeev.data = (char *)display;
244  D_readev.handler  = disp_readev_fn;
245  D_writeev.handler = disp_writeev_fn;
246  evenq(&D_readev);
247  D_writeev.condpos = &D_obuflen;
248  D_writeev.condneg = &D_obuffree;
249  evenq(&D_writeev);
250  D_statusev.type = EV_TIMEOUT;
251  D_statusev.data = (char *)display;
252  D_statusev.handler = disp_status_fn;
253  D_hstatusev.type = EV_TIMEOUT;
254  D_hstatusev.data = (char *)display;
255  D_hstatusev.handler = disp_hstatus_fn;
256  D_blockedev.type = EV_TIMEOUT;
257  D_blockedev.data = (char *)display;
258  D_blockedev.handler = disp_blocked_fn;
259  D_blockedev.condpos = &D_obuffree;
260  D_blockedev.condneg = &D_obuflenmax;
261  D_hstatusev.handler = disp_hstatus_fn;
262#ifdef MAPKEYS
263  D_mapev.type = EV_TIMEOUT;
264  D_mapev.data = (char *)display;
265  D_mapev.handler = disp_map_fn;
266#endif
267  D_idleev.type = EV_TIMEOUT;
268  D_idleev.data = (char *)display;
269  D_idleev.handler = disp_idle_fn;
270#ifdef BLANKER_PRG
271  D_blankerev.type = EV_READ;
272  D_blankerev.data = (char *)display;
273  D_blankerev.handler = disp_blanker_fn;
274  D_blankerev.fd = -1;
275#endif
276  D_OldMode = *Mode;
277  D_status_obuffree = -1;
278  Resize_obuf();  /* Allocate memory for buffer */
279  D_obufmax = defobuflimit;
280  D_obuflenmax = D_obuflen - D_obufmax;
281#ifdef AUTO_NUKE
282  D_auto_nuke = defautonuke;
283#endif
284  D_obufp = D_obuf;
285  D_printfd = -1;
286  D_userpid = pid;
287
288#ifdef POSIX
289  if ((b = lookup_baud((int)cfgetospeed(&D_OldMode.tio))))
290    D_dospeed = b->idx;
291#else
292# ifdef TERMIO
293  if ((b = lookup_baud(D_OldMode.tio.c_cflag & CBAUD)))
294    D_dospeed = b->idx;
295# else
296  D_dospeed = (short)D_OldMode.m_ttyb.sg_ospeed;
297# endif
298#endif
299  debug1("New displays ospeed = %d\n", D_dospeed);
300
301  strncpy(D_usertty, utty, sizeof(D_usertty) - 1);
302  D_usertty[sizeof(D_usertty) - 1] = 0;
303  strncpy(D_termname, term, sizeof(D_termname) - 1);
304  D_termname[sizeof(D_termname) - 1] = 0;
305  D_user = *u;
306  D_processinput = ProcessInput;
307  return display;
308}
309
310
311void
312FreeDisplay()
313{
314  struct win *p;
315  struct canvas *cv, *cvp;
316#ifdef MULTI
317  struct display *d, **dp;
318#endif
319
320#ifdef FONT
321  FreeTransTable();
322#endif
323#ifdef BLANKER_PRG
324  KillBlanker();
325#endif
326  if (D_userfd >= 0)
327    {
328      Flush();
329      if (!display)
330	return;
331      SetTTY(D_userfd, &D_OldMode);
332      fcntl(D_userfd, F_SETFL, 0);
333    }
334  freetty();
335  if (D_tentry)
336    free(D_tentry);
337  D_tentry = 0;
338  if (D_processinputdata)
339    free(D_processinputdata);
340  D_processinputdata = 0;
341  D_tcinited = 0;
342  evdeq(&D_hstatusev);
343  evdeq(&D_statusev);
344  evdeq(&D_readev);
345  evdeq(&D_writeev);
346  evdeq(&D_blockedev);
347#ifdef MAPKEYS
348  evdeq(&D_mapev);
349  if (D_kmaps)
350    {
351      free(D_kmaps);
352      D_kmaps = 0;
353      D_aseqs = 0;
354      D_nseqs = 0;
355      D_seqp = 0;
356      D_seql = 0;
357      D_seqh = 0;
358    }
359#endif
360  evdeq(&D_idleev);
361#ifdef BLANKER_PRG
362  evdeq(&D_blankerev);
363#endif
364#ifdef HAVE_BRAILLE
365  if (bd.bd_dpy == display)
366    {
367      bd.bd_start_braille = 0;
368      StartBraille();
369    }
370#endif
371
372#ifdef MULTI
373  for (dp = &displays; (d = *dp) ; dp = &d->d_next)
374    if (d == display)
375      break;
376  ASSERT(d);
377  if (D_status_lastmsg)
378    free(D_status_lastmsg);
379  if (D_obuf)
380    free(D_obuf);
381  *dp = display->d_next;
382  cv = display->d_cvlist;
383#else /* MULTI */
384  ASSERT(display == displays);
385  ASSERT(display == &TheDisplay);
386  cv = display->d_cvlist;
387  display->d_cvlist = 0;
388  displays = 0;
389#endif /* MULTI */
390
391  for (p = windows; p; p = p->w_next)
392    {
393      if (p->w_pdisplay == display)
394	p->w_pdisplay = 0;
395      if (p->w_lastdisp == display)
396	p->w_lastdisp = 0;
397      if (p->w_readev.condneg == &D_status || p->w_readev.condneg == &D_obuflenmax)
398	p->w_readev.condpos = p->w_readev.condneg = 0;
399    }
400  for (; cv; cv = cvp)
401    {
402      cvp = cv->c_next;
403      FreeCanvas(cv);
404    }
405#ifdef ZMODEM
406  for (p = windows; p; p = p->w_next)
407    if (p->w_zdisplay == display)
408      zmodem_abort(p, 0);
409#endif
410#ifdef MULTI
411  free((char *)display);
412#endif
413  display = 0;
414}
415
416int
417MakeDefaultCanvas()
418{
419  struct canvas *cv;
420
421  ASSERT(display);
422  if ((cv = (struct canvas *)calloc(1, sizeof *cv)) == 0)
423    return -1;
424  cv->c_xs      = 0;
425  cv->c_xe      = D_width - 1;
426  cv->c_ys      = 0;
427  cv->c_ye      = D_height - 1 - (D_has_hstatus == HSTATUS_LASTLINE) - captionalways;
428  cv->c_xoff    = 0;
429  cv->c_yoff    = 0;
430  cv->c_next = 0;
431  cv->c_display = display;
432  cv->c_vplist = 0;
433  cv->c_captev.type = EV_TIMEOUT;
434  cv->c_captev.data = (char *)cv;
435  cv->c_captev.handler = cv_winid_fn;
436
437  cv->c_blank.l_cvlist = cv;
438  cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1;
439  cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1;
440  cv->c_blank.l_x = cv->c_blank.l_y = 0;
441  cv->c_blank.l_layfn = &BlankLf;
442  cv->c_blank.l_data = 0;
443  cv->c_blank.l_next = 0;
444  cv->c_blank.l_bottom = &cv->c_blank;
445  cv->c_blank.l_blocking = 0;
446  cv->c_layer = &cv->c_blank;
447  cv->c_lnext = 0;
448
449  D_cvlist = cv;
450  RethinkDisplayViewports();
451  D_forecv = cv; 	      /* default input focus */
452  return 0;
453}
454
455static void
456FreeCanvas(cv)
457struct canvas *cv;
458{
459  struct viewport *vp, *nvp;
460  struct win *p;
461
462  p = Layer2Window(cv->c_layer);
463  SetCanvasWindow(cv, 0);
464  if (p)
465    WindowChanged(p, 'u');
466  if (flayer == cv->c_layer)
467    flayer = 0;
468  for (vp = cv->c_vplist; vp; vp = nvp)
469    {
470      vp->v_canvas = 0;
471      nvp = vp->v_next;
472      vp->v_next = 0;
473      free(vp);
474    }
475  evdeq(&cv->c_captev);
476  free(cv);
477}
478
479int
480AddCanvas()
481{
482  int hh, h, i, j;
483  struct canvas *cv, **cvpp;
484
485  for (cv = D_cvlist, j = 0; cv; cv = cv->c_next)
486    j++;
487  j++;	/* new canvas */
488  h = D_height - (D_has_hstatus == HSTATUS_LASTLINE);
489  if (h / j <= 1)
490    return -1;
491
492  for (cv = D_cvlist; cv; cv = cv->c_next)
493    if (cv == D_forecv)
494      break;
495  ASSERT(cv);
496  cvpp = &cv->c_next;
497
498  if ((cv = (struct canvas *)calloc(1, sizeof *cv)) == 0)
499    return -1;
500
501  cv->c_xs      = 0;
502  cv->c_xe      = D_width - 1;
503  cv->c_ys      = 0;
504  cv->c_ye      = D_height - 1;
505  cv->c_xoff    = 0;
506  cv->c_yoff    = 0;
507  cv->c_display = display;
508  cv->c_vplist  = 0;
509  cv->c_captev.type = EV_TIMEOUT;
510  cv->c_captev.data = (char *)cv;
511  cv->c_captev.handler = cv_winid_fn;
512
513  cv->c_blank.l_cvlist = cv;
514  cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1;
515  cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1;
516  cv->c_blank.l_x = cv->c_blank.l_y = 0;
517  cv->c_blank.l_layfn = &BlankLf;
518  cv->c_blank.l_data = 0;
519  cv->c_blank.l_next = 0;
520  cv->c_blank.l_bottom = &cv->c_blank;
521  cv->c_blank.l_blocking = 0;
522  cv->c_layer = &cv->c_blank;
523  cv->c_lnext = 0;
524
525  cv->c_next    = *cvpp;
526  *cvpp = cv;
527
528  i = 0;
529  for (cv = D_cvlist; cv; cv = cv->c_next)
530    {
531      hh = h / j-- - 1;
532      cv->c_ys = i;
533      cv->c_ye = i + hh - 1;
534      cv->c_yoff = i;
535      i += hh + 1;
536      h -= hh + 1;
537    }
538
539  RethinkDisplayViewports();
540  ResizeLayersToCanvases();
541  return 0;
542}
543
544void
545RemCanvas()
546{
547  int hh, h, i, j;
548  struct canvas *cv, **cvpp;
549  int did = 0;
550
551  h = D_height - (D_has_hstatus == HSTATUS_LASTLINE);
552  for (cv = D_cvlist, j = 0; cv; cv = cv->c_next)
553    j++;
554  if (j == 1)
555    return;
556  i = 0;
557  j--;
558  for (cvpp = &D_cvlist; (cv = *cvpp); cvpp = &cv->c_next)
559    {
560      if (cv == D_forecv && !did)
561	{
562	  *cvpp = cv->c_next;
563	  FreeCanvas(cv);
564	  cv = *cvpp;
565	  D_forecv = cv ? cv : D_cvlist;
566	  D_fore = Layer2Window(D_forecv->c_layer);
567	  flayer = D_forecv->c_layer;
568	  if (cv == 0)
569	    break;
570	  did = 1;
571	}
572      hh = h / j-- - 1;
573      if (!captionalways && i == 0 && j == 0)
574	hh++;
575      cv->c_ys = i;
576      cv->c_ye = i + hh - 1;
577      cv->c_yoff = i;
578      i += hh + 1;
579      h -= hh + 1;
580    }
581  RethinkDisplayViewports();
582  ResizeLayersToCanvases();
583}
584
585void
586OneCanvas()
587{
588  struct canvas *mycv = D_forecv;
589  struct canvas *cv, **cvpp;
590
591  for (cvpp = &D_cvlist; (cv = *cvpp);)
592    {
593      if (cv == mycv)
594        {
595	  cv->c_ys = 0;
596	  cv->c_ye = D_height - 1 - (D_has_hstatus == HSTATUS_LASTLINE) - captionalways;
597	  cv->c_yoff = 0;
598	  cvpp = &cv->c_next;
599        }
600      else
601        {
602	  *cvpp = cv->c_next;
603	  FreeCanvas(cv);
604        }
605    }
606  RethinkDisplayViewports();
607  ResizeLayersToCanvases();
608}
609
610int
611RethinkDisplayViewports()
612{
613  struct canvas *cv;
614  struct viewport *vp, *vpn;
615
616  /* free old viewports */
617  for (cv = display->d_cvlist; cv; cv = cv->c_next)
618    {
619      for (vp = cv->c_vplist; vp; vp = vpn)
620	{
621	  vp->v_canvas = 0;
622	  vpn = vp->v_next;
623          bzero((char *)vp, sizeof(*vp));
624          free(vp);
625	}
626      cv->c_vplist = 0;
627    }
628  display->d_vpxmin = -1;
629  display->d_vpxmax = -1;
630
631  for (cv = display->d_cvlist; cv; cv = cv->c_next)
632    {
633      if ((vp = (struct viewport *)malloc(sizeof *vp)) == 0)
634	return -1;
635#ifdef HOLE
636      vp->v_canvas = cv;
637      vp->v_xs = cv->c_xs;
638      vp->v_ys = (cv->c_ys + cv->c_ye) / 2;
639      vp->v_xe = cv->c_xe;
640      vp->v_ye = cv->c_ye;
641      vp->v_xoff = cv->c_xoff;
642      vp->v_yoff = cv->c_yoff;
643      vp->v_next = cv->c_vplist;
644      cv->c_vplist = vp;
645
646      if ((vp = (struct viewport *)malloc(sizeof *vp)) == 0)
647	return -1;
648      vp->v_canvas = cv;
649      vp->v_xs = (cv->c_xs + cv->c_xe) / 2;
650      vp->v_ys = (3 * cv->c_ys + cv->c_ye) / 4;
651      vp->v_xe = cv->c_xe;
652      vp->v_ye = (cv->c_ys + cv->c_ye) / 2 - 1;
653      vp->v_xoff = cv->c_xoff;
654      vp->v_yoff = cv->c_yoff;
655      vp->v_next = cv->c_vplist;
656      cv->c_vplist = vp;
657
658      if ((vp = (struct viewport *)malloc(sizeof *vp)) == 0)
659	return -1;
660      vp->v_canvas = cv;
661      vp->v_xs = cv->c_xs;
662      vp->v_ys = (3 * cv->c_ys + cv->c_ye) / 4;
663      vp->v_xe = (3 * cv->c_xs + cv->c_xe) / 4 - 1;
664      vp->v_ye = (cv->c_ys + cv->c_ye) / 2 - 1;
665      vp->v_xoff = cv->c_xoff;
666      vp->v_yoff = cv->c_yoff;
667      vp->v_next = cv->c_vplist;
668      cv->c_vplist = vp;
669
670      if ((vp = (struct viewport *)malloc(sizeof *vp)) == 0)
671	return -1;
672      vp->v_canvas = cv;
673      vp->v_xs = cv->c_xs;
674      vp->v_ys = cv->c_ys;
675      vp->v_xe = cv->c_xe;
676      vp->v_ye = (3 * cv->c_ys + cv->c_ye) / 4 - 1;
677      vp->v_xoff = cv->c_xoff;
678      vp->v_yoff = cv->c_yoff;
679      vp->v_next = cv->c_vplist;
680      cv->c_vplist = vp;
681#else
682      vp->v_canvas = cv;
683      vp->v_xs = cv->c_xs;
684      vp->v_ys = cv->c_ys;
685      vp->v_xe = cv->c_xe;
686      vp->v_ye = cv->c_ye;
687      vp->v_xoff = cv->c_xoff;
688      vp->v_yoff = cv->c_yoff;
689      vp->v_next = cv->c_vplist;
690      cv->c_vplist = vp;
691#endif
692
693      if (cv->c_xs < display->d_vpxmin || display->d_vpxmin == -1)
694        display->d_vpxmin = cv->c_xs;
695      if (cv->c_xe > display->d_vpxmax || display->d_vpxmax == -1)
696        display->d_vpxmax = cv->c_xe;
697    }
698  return 0;
699}
700
701void
702RethinkViewportOffsets(cv)
703struct canvas *cv;
704{
705  struct viewport *vp;
706
707  for (vp = cv->c_vplist; vp; vp = vp->v_next)
708    {
709      vp->v_xoff = cv->c_xoff;
710      vp->v_yoff = cv->c_yoff;
711    }
712}
713
714/*
715 * if the adaptflag is on, we keep the size of this display, else
716 * we may try to restore our old window sizes.
717 */
718void
719InitTerm(adapt)
720int adapt;
721{
722  ASSERT(display);
723  ASSERT(D_tcinited);
724  D_top = D_bot = -1;
725  AddCStr(D_TI);
726  AddCStr(D_IS);
727  /* Check for toggle */
728  if (D_IM && strcmp(D_IM, D_EI))
729    AddCStr(D_EI);
730  D_insert = 0;
731#ifdef MAPKEYS
732  AddCStr(D_KS);
733  AddCStr(D_CCS);
734#else
735  /* Check for toggle */
736  if (D_KS && strcmp(D_KS, D_KE))
737    AddCStr(D_KE);
738  if (D_CCS && strcmp(D_CCS, D_CCE))
739    AddCStr(D_CCE);
740#endif
741  D_keypad = 0;
742  D_cursorkeys = 0;
743  AddCStr(D_ME);
744  AddCStr(D_EA);
745  AddCStr(D_CE0);
746  D_rend = mchar_null;
747  D_atyp = 0;
748  if (adapt == 0)
749    ResizeDisplay(D_defwidth, D_defheight);
750  ChangeScrollRegion(0, D_height - 1);
751  D_x = D_y = 0;
752  Flush();
753  ClearAll();
754  debug1("we %swant to adapt all our windows to the display\n",
755	 (adapt) ? "" : "don't ");
756  /* In case the size was changed by a init sequence */
757  CheckScreenSize((adapt) ? 2 : 0);
758}
759
760void
761FinitTerm()
762{
763  ASSERT(display);
764#ifdef BLANKER_PRG
765  KillBlanker();
766#endif
767  if (D_tcinited)
768    {
769      ResizeDisplay(D_defwidth, D_defheight);
770      InsertMode(0);
771      ChangeScrollRegion(0, D_height - 1);
772      KeypadMode(0);
773      CursorkeysMode(0);
774      CursorVisibility(0);
775      MouseMode(0);
776      SetRendition(&mchar_null);
777      SetFlow(FLOW_NOW);
778#ifdef MAPKEYS
779      AddCStr(D_KE);
780      AddCStr(D_CCE);
781#endif
782      if (D_hstatus)
783	ShowHStatus((char *)0);
784#ifdef RXVT_OSC
785      ClearAllXtermOSC();
786#endif
787      D_x = D_y = -1;
788      GotoPos(0, D_height - 1);
789      AddChar('\r');
790      AddChar('\n');
791      AddCStr(D_TE);
792    }
793  Flush();
794}
795
796
797static void
798INSERTCHAR(c)
799int c;
800{
801  ASSERT(display);
802  if (!D_insert && D_x < D_width - 1)
803    {
804      if (D_IC || D_CIC)
805	{
806	  if (D_IC)
807	    AddCStr(D_IC);
808	  else
809	    AddCStr2(D_CIC, 1);
810	  RAW_PUTCHAR(c);
811	  return;
812	}
813      InsertMode(1);
814      if (!D_insert)
815	{
816          RefreshLine(D_y, D_x, D_width-1, 0);
817	  return;
818	}
819    }
820  RAW_PUTCHAR(c);
821}
822
823void
824PUTCHAR(c)
825int c;
826{
827  ASSERT(display);
828  if (D_insert && D_x < D_width - 1)
829    InsertMode(0);
830  RAW_PUTCHAR(c);
831}
832
833void
834PUTCHARLP(c)
835int c;
836{
837  if (D_x < D_width - 1)
838    {
839      if (D_insert)
840	InsertMode(0);
841      RAW_PUTCHAR(c);
842      return;
843    }
844  if (D_CLP || D_y != D_bot)
845    {
846      int y = D_y;
847      RAW_PUTCHAR(c);
848      if (D_AM && !D_CLP)
849	GotoPos(D_width - 1, y);
850      return;
851    }
852  debug("PUTCHARLP: lp_missing!\n");
853  D_lp_missing = 1;
854  D_rend.image = c;
855  D_lpchar = D_rend;
856#ifdef DW_CHARS
857  /* XXX -> PutChar ? */
858  if (D_mbcs)
859    {
860      D_lpchar.mbcs = c;
861      D_lpchar.image = D_mbcs;
862      D_mbcs = 0;
863      D_x--;
864    }
865#endif
866}
867
868/*
869 * RAW_PUTCHAR() is for all text that will be displayed.
870 * NOTE: charset Nr. 0 has a conversion table, but c1, c2, ... don't.
871 */
872
873STATIC void
874RAW_PUTCHAR(c)
875int c;
876{
877  ASSERT(display);
878
879#ifdef FONT
880# ifdef UTF8
881  if (D_encoding == UTF8)
882    {
883      c = (c & 255) | (unsigned char)D_rend.font << 8;
884#  ifdef DW_CHARS
885      if (D_mbcs)
886	{
887	  c = D_mbcs;
888	  if (D_x == D_width)
889	    D_x += D_AM ? 1 : -1;
890	  D_mbcs = 0;
891	}
892      else if (utf8_isdouble(c))
893	{
894	  D_mbcs = c;
895	  D_x++;
896	  return;
897	}
898#  endif
899      if (c < 32)
900	{
901	  AddCStr2(D_CS0, '0');
902	  AddChar(c + 0x5f);
903	  AddCStr(D_CE0);
904	  goto addedutf8;
905	}
906      AddUtf8(c);
907      goto addedutf8;
908    }
909# endif
910# ifdef DW_CHARS
911  if (is_dw_font(D_rend.font))
912    {
913      int t = c;
914      if (D_mbcs == 0)
915	{
916	  D_mbcs = c;
917	  D_x++;
918	  return;
919	}
920      D_x--;
921      if (D_x == D_width - 1)
922	D_x += D_AM ? 1 : -1;
923      c = D_mbcs;
924      D_mbcs = t;
925    }
926# endif
927# if defined(ENCODINGS) && defined(DW_CHARS)
928  if (D_encoding)
929    c = PrepareEncodedChar(c);
930# endif
931# ifdef DW_CHARS
932  kanjiloop:
933# endif
934  if (D_xtable && D_xtable[(int)(unsigned char)D_rend.font] && D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c])
935    AddStr(D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c]);
936  else
937    AddChar(D_rend.font != '0' ? c : D_c0_tab[(int)(unsigned char)c]);
938#else /* FONT */
939    AddChar(c);
940#endif /* FONT */
941
942#ifdef UTF8
943addedutf8:
944#endif
945  if (++D_x >= D_width)
946    {
947      if (D_AM == 0)
948        D_x = D_width - 1;
949      else if (!D_CLP || D_x > D_width)
950	{
951	  D_x -= D_width;
952	  if (D_y < D_height-1 && D_y != D_bot)
953	    D_y++;
954	}
955    }
956#ifdef DW_CHARS
957  if (D_mbcs)
958    {
959      c = D_mbcs;
960      D_mbcs = 0;
961      goto kanjiloop;
962    }
963#endif
964}
965
966static int
967DoAddChar(c)
968int c;
969{
970  /* this is for ESC-sequences only (AddChar is a macro) */
971  AddChar(c);
972  return c;
973}
974
975void
976AddCStr(s)
977char *s;
978{
979  if (display && s && *s)
980    {
981      ospeed = D_dospeed;
982      tputs(s, 1, DoAddChar);
983    }
984}
985
986void
987AddCStr2(s, c)
988char *s;
989int c;
990{
991  if (display && s && *s)
992    {
993      ospeed = D_dospeed;
994      tputs(tgoto(s, 0, c), 1, DoAddChar);
995    }
996}
997
998
999/* Insert mode is a toggle on some terminals, so we need this hack:
1000 */
1001void
1002InsertMode(on)
1003int on;
1004{
1005  if (display && on != D_insert && D_IM)
1006    {
1007      D_insert = on;
1008      if (on)
1009	AddCStr(D_IM);
1010      else
1011	AddCStr(D_EI);
1012    }
1013}
1014
1015/* ...and maybe keypad application mode is a toggle, too:
1016 */
1017void
1018KeypadMode(on)
1019int on;
1020{
1021#ifdef MAPKEYS
1022  if (display)
1023    D_keypad = on;
1024#else
1025  if (display && D_keypad != on && D_KS)
1026    {
1027      D_keypad = on;
1028      if (on)
1029	AddCStr(D_KS);
1030      else
1031	AddCStr(D_KE);
1032    }
1033#endif
1034}
1035
1036void
1037CursorkeysMode(on)
1038int on;
1039{
1040#ifdef MAPKEYS
1041  if (display)
1042    D_cursorkeys = on;
1043#else
1044  if (display && D_cursorkeys != on && D_CCS)
1045    {
1046      D_cursorkeys = on;
1047      if (on)
1048	AddCStr(D_CCS);
1049      else
1050	AddCStr(D_CCE);
1051    }
1052#endif
1053}
1054
1055void
1056ReverseVideo(on)
1057int on;
1058{
1059  if (display && D_revvid != on && D_CVR)
1060    {
1061      D_revvid = on;
1062      if (D_revvid)
1063	AddCStr(D_CVR);
1064      else
1065	AddCStr(D_CVN);
1066    }
1067}
1068
1069void
1070CursorVisibility(v)
1071int v;
1072{
1073  if (display && D_curvis != v)
1074    {
1075      if (D_curvis)
1076	AddCStr(D_VE);		/* do this always, just to be safe */
1077      D_curvis = 0;
1078      if (v == -1 && D_VI)
1079	AddCStr(D_VI);
1080      else if (v == 1 && D_VS)
1081	AddCStr(D_VS);
1082      else
1083	return;
1084      D_curvis = v;
1085    }
1086}
1087
1088void
1089MouseMode(mode)
1090int mode;
1091{
1092  if (display && D_mouse != mode)
1093    {
1094      char mousebuf[20];
1095      if (!D_CXT)
1096	return;
1097      if (D_mouse)
1098        {
1099          sprintf(mousebuf, "\033[?%dl", D_mouse);
1100          AddStr(mousebuf);
1101	}
1102      if (mode)
1103        {
1104          sprintf(mousebuf, "\033[?%dh", mode);
1105          AddStr(mousebuf);
1106	}
1107      D_mouse = mode;
1108    }
1109}
1110
1111static int StrCost;
1112
1113/* ARGSUSED */
1114static int
1115CountChars(c)
1116int c;
1117{
1118  StrCost++;
1119  return c;
1120}
1121
1122int
1123CalcCost(s)
1124register char *s;
1125{
1126  ASSERT(display);
1127  if (s)
1128    {
1129      StrCost = 0;
1130      ospeed = D_dospeed;
1131      tputs(s, 1, CountChars);
1132      return StrCost;
1133    }
1134  else
1135    return EXPENSIVE;
1136}
1137
1138static int
1139CallRewrite(y, xs, xe, doit)
1140int y, xs, xe, doit;
1141{
1142  struct canvas *cv, *cvlist, *cvlnext;
1143  struct viewport *vp;
1144  struct layer *oldflayer;
1145  int cost;
1146
1147  debug3("CallRewrite %d %d %d\n", y, xs, xe);
1148  ASSERT(display);
1149  ASSERT(xe >= xs);
1150
1151  vp = 0;
1152  for (cv = D_cvlist; cv; cv = cv->c_next)
1153    {
1154      if (y < cv->c_ys || y > cv->c_ye || xe < cv->c_xs || xs > cv->c_xe)
1155	continue;
1156      for (vp = cv->c_vplist; vp; vp = vp->v_next)
1157	if (y >= vp->v_ys && y <= vp->v_ye && xe >= vp->v_xs && xs <= vp->v_xe)
1158	  break;
1159      if (vp)
1160	break;
1161    }
1162  if (doit)
1163    {
1164      oldflayer = flayer;
1165      flayer = cv->c_layer;
1166      cvlist = flayer->l_cvlist;
1167      cvlnext = cv->c_lnext;
1168      flayer->l_cvlist = cv;
1169      cv->c_lnext = 0;
1170      LayRewrite(y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff, &D_rend, 1);
1171      flayer->l_cvlist = cvlist;
1172      cv->c_lnext = cvlnext;
1173      flayer = oldflayer;
1174      return 0;
1175    }
1176  if (cv == 0 || cv->c_layer == 0)
1177    return EXPENSIVE;	/* not found or nothing on it */
1178  if (xs < vp->v_xs || xe > vp->v_xe)
1179    return EXPENSIVE;	/* crosses viewport boundaries */
1180  if (y - vp->v_yoff < 0 || y - vp->v_yoff >= cv->c_layer->l_height)
1181    return EXPENSIVE;	/* line not on layer */
1182  if (xs - vp->v_xoff < 0 || xe - vp->v_xoff >= cv->c_layer->l_width)
1183    return EXPENSIVE;	/* line not on layer */
1184#ifdef UTF8
1185  if (D_encoding == UTF8)
1186    D_rend.font = 0;
1187#endif
1188  oldflayer = flayer;
1189  flayer = cv->c_layer;
1190  debug3("Calling Rewrite %d %d %d\n", y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff);
1191  cost = LayRewrite(y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff, &D_rend, 0);
1192  flayer = oldflayer;
1193  if (D_insert)
1194    cost += D_EIcost + D_IMcost;
1195  return cost;
1196}
1197
1198
1199void
1200GotoPos(x2, y2)
1201int x2, y2;
1202{
1203  register int dy, dx, x1, y1;
1204  register int costx, costy;
1205  register int m;
1206  register char *s;
1207  int CMcost;
1208  enum move_t xm = M_NONE, ym = M_NONE;
1209
1210  if (!display)
1211    return;
1212
1213  x1 = D_x;
1214  y1 = D_y;
1215
1216  if (x1 == D_width)
1217    {
1218      if (D_CLP && D_AM)
1219        x1 = -1;	/* don't know how the terminal treats this */
1220      else
1221        x1--;
1222    }
1223  if (x2 == D_width)
1224    x2--;
1225  dx = x2 - x1;
1226  dy = y2 - y1;
1227  if (dy == 0 && dx == 0)
1228    return;
1229  debug2("GotoPos (%d,%d)", x1, y1);
1230  debug2(" -> (%d,%d)\n", x2, y2);
1231  if (!D_MS)		/* Safe to move ? */
1232    SetRendition(&mchar_null);
1233  if (y1 < 0			/* don't know the y position */
1234      || (y2 > D_bot && y1 <= D_bot)	/* have to cross border */
1235      || (y2 < D_top && y1 >= D_top))	/* of scrollregion ?    */
1236    {
1237    DoCM:
1238      if (D_HO && !x2 && !y2)
1239        AddCStr(D_HO);
1240      else
1241        AddCStr(tgoto(D_CM, x2, y2));
1242      D_x = x2;
1243      D_y = y2;
1244      return;
1245    }
1246
1247  /* some scrollregion implementations don't allow movements
1248   * away from the region. sigh.
1249   */
1250  if ((y1 > D_bot && y2 > y1) || (y1 < D_top && y2 < y1))
1251    goto DoCM;
1252
1253  /* Calculate CMcost */
1254  if (D_HO && !x2 && !y2)
1255    s = D_HO;
1256  else
1257    s = tgoto(D_CM, x2, y2);
1258  CMcost = CalcCost(s);
1259
1260  /* Calculate the cost to move the cursor to the right x position */
1261  costx = EXPENSIVE;
1262  if (x1 >= 0)	/* relativ x positioning only if we know where we are */
1263    {
1264      if (dx > 0)
1265	{
1266	  if (D_CRI && (dx > 1 || !D_ND))
1267	    {
1268	      costx = CalcCost(tgoto(D_CRI, 0, dx));
1269	      xm = M_CRI;
1270	    }
1271	  if ((m = D_NDcost * dx) < costx)
1272	    {
1273	      costx = m;
1274	      xm = M_RI;
1275	    }
1276	  /* Speedup: dx <= LayRewrite() */
1277	  if (dx < costx && (m = CallRewrite(y1, x1, x2 - 1, 0)) < costx)
1278	    {
1279	      costx = m;
1280	      xm = M_RW;
1281	    }
1282	}
1283      else if (dx < 0)
1284	{
1285	  if (D_CLE && (dx < -1 || !D_BC))
1286	    {
1287	      costx = CalcCost(tgoto(D_CLE, 0, -dx));
1288	      xm = M_CLE;
1289	    }
1290	  if ((m = -dx * D_LEcost) < costx)
1291	    {
1292	      costx = m;
1293	      xm = M_LE;
1294	    }
1295	}
1296      else
1297	costx = 0;
1298    }
1299  /* Speedup: LayRewrite() >= x2 */
1300  if (x2 + D_CRcost < costx && (m = (x2 ? CallRewrite(y1, 0, x2 - 1, 0) : 0) + D_CRcost) < costx)
1301    {
1302      costx = m;
1303      xm = M_CR;
1304    }
1305
1306  /* Check if it is already cheaper to do CM */
1307  if (costx >= CMcost)
1308    goto DoCM;
1309
1310  /* Calculate the cost to move the cursor to the right y position */
1311  costy = EXPENSIVE;
1312  if (dy > 0)
1313    {
1314      if (D_CDO && dy > 1)	/* DO & NL are always != 0 */
1315	{
1316	  costy = CalcCost(tgoto(D_CDO, 0, dy));
1317	  ym = M_CDO;
1318	}
1319      if ((m = dy * ((x2 == 0) ? D_NLcost : D_DOcost)) < costy)
1320	{
1321	  costy = m;
1322	  ym = M_DO;
1323	}
1324    }
1325  else if (dy < 0)
1326    {
1327      if (D_CUP && (dy < -1 || !D_UP))
1328	{
1329	  costy = CalcCost(tgoto(D_CUP, 0, -dy));
1330	  ym = M_CUP;
1331	}
1332      if ((m = -dy * D_UPcost) < costy)
1333	{
1334	  costy = m;
1335	  ym = M_UP;
1336	}
1337    }
1338  else
1339    costy = 0;
1340
1341  /* Finally check if it is cheaper to do CM */
1342  if (costx + costy >= CMcost)
1343    goto DoCM;
1344
1345  switch (xm)
1346    {
1347    case M_LE:
1348      while (dx++ < 0)
1349	AddCStr(D_BC);
1350      break;
1351    case M_CLE:
1352      AddCStr2(D_CLE, -dx);
1353      break;
1354    case M_RI:
1355      while (dx-- > 0)
1356	AddCStr(D_ND);
1357      break;
1358    case M_CRI:
1359      AddCStr2(D_CRI, dx);
1360      break;
1361    case M_CR:
1362      AddCStr(D_CR);
1363      D_x = 0;
1364      x1 = 0;
1365      /* FALLTHROUGH */
1366    case M_RW:
1367      if (x1 < x2)
1368	(void) CallRewrite(y1, x1, x2 - 1, 1);
1369      break;
1370    default:
1371      break;
1372    }
1373
1374  switch (ym)
1375    {
1376    case M_UP:
1377      while (dy++ < 0)
1378	AddCStr(D_UP);
1379      break;
1380    case M_CUP:
1381      AddCStr2(D_CUP, -dy);
1382      break;
1383    case M_DO:
1384      s =  (x2 == 0) ? D_NL : D_DO;
1385      while (dy-- > 0)
1386	AddCStr(s);
1387      break;
1388    case M_CDO:
1389      AddCStr2(D_CDO, dy);
1390      break;
1391    default:
1392      break;
1393    }
1394  D_x = x2;
1395  D_y = y2;
1396}
1397
1398void
1399ClearAll()
1400{
1401  ASSERT(display);
1402  ClearArea(0, 0, 0, D_width - 1, D_width - 1, D_height - 1, 0, 0);
1403}
1404
1405void
1406ClearArea(x1, y1, xs, xe, x2, y2, bce, uselayfn)
1407int x1, y1, xs, xe, x2, y2, bce, uselayfn;
1408{
1409  int y, xxe;
1410  struct canvas *cv;
1411  struct viewport *vp;
1412
1413  debug2("Clear %d,%d", x1, y1);
1414  debug2(" %d-%d", xs, xe);
1415  debug2(" %d,%d", x2, y2);
1416  debug2(" uselayfn=%d bce=%d\n", uselayfn, bce);
1417  ASSERT(display);
1418  if (x1 == D_width)
1419    x1--;
1420  if (x2 == D_width)
1421    x2--;
1422  if (xs == -1)
1423    xs = x1;
1424  if (xe == -1)
1425    xe = x2;
1426  if (D_UT)	/* Safe to erase ? */
1427    SetRendition(&mchar_null);
1428#ifdef COLOR
1429  if (D_BE)
1430    SetBackColor(bce);
1431#endif
1432  if (D_lp_missing && y1 <= D_bot && xe >= D_width - 1)
1433    {
1434      if (y2 > D_bot || (y2 == D_bot && x2 >= D_width - 1))
1435	D_lp_missing = 0;
1436    }
1437  if (x2 == D_width - 1 && (xs == 0 || y1 == y2) && xe == D_width - 1 && y2 == D_height - 1 && (!bce || D_BE))
1438    {
1439#ifdef AUTO_NUKE
1440      if (x1 == 0 && y1 == 0 && D_auto_nuke)
1441	NukePending();
1442#endif
1443      if (x1 == 0 && y1 == 0 && D_CL)
1444	{
1445	  AddCStr(D_CL);
1446	  D_y = D_x = 0;
1447	  return;
1448	}
1449      /*
1450       * Workaround a hp700/22 terminal bug. Do not use CD where CE
1451       * is also appropriate.
1452       */
1453      if (D_CD && (y1 < y2 || !D_CE))
1454	{
1455	  GotoPos(x1, y1);
1456	  AddCStr(D_CD);
1457	  return;
1458	}
1459    }
1460  if (x1 == 0 && xs == 0 && (xe == D_width - 1 || y1 == y2) && y1 == 0 && D_CCD && (!bce || D_BE))
1461    {
1462      GotoPos(x1, y1);
1463      AddCStr(D_CCD);
1464      return;
1465    }
1466  xxe = xe;
1467  for (y = y1; y <= y2; y++, x1 = xs)
1468    {
1469      if (y == y2)
1470	xxe = x2;
1471      if (x1 == 0 && D_CB && (xxe != D_width - 1 || (D_x == xxe && D_y == y)) && (!bce || D_BE))
1472	{
1473	  GotoPos(xxe, y);
1474	  AddCStr(D_CB);
1475	  continue;
1476	}
1477      if (xxe == D_width - 1 && D_CE && (!bce || D_BE))
1478	{
1479	  GotoPos(x1, y);
1480	  AddCStr(D_CE);
1481	  continue;
1482	}
1483      if (uselayfn)
1484	{
1485	  vp = 0;
1486	  for (cv = D_cvlist; cv; cv = cv->c_next)
1487	    {
1488	      if (y < cv->c_ys || y > cv->c_ye || xxe < cv->c_xs || x1 > cv->c_xe)
1489		continue;
1490	      for (vp = cv->c_vplist; vp; vp = vp->v_next)
1491	        if (y >= vp->v_ys && y <= vp->v_ye && xxe >= vp->v_xs && x1 <= vp->v_xe)
1492		  break;
1493	      if (vp)
1494		break;
1495	    }
1496	  if (cv && cv->c_layer && x1 >= vp->v_xs && xxe <= vp->v_xe &&
1497              y - vp->v_yoff >= 0 && y - vp->v_yoff < cv->c_layer->l_height &&
1498              xxe - vp->v_xoff >= 0 && x1 - vp->v_xoff < cv->c_layer->l_width)
1499	    {
1500	      struct layer *oldflayer = flayer;
1501	      struct canvas *cvlist, *cvlnext;
1502	      flayer = cv->c_layer;
1503	      cvlist = flayer->l_cvlist;
1504	      cvlnext = cv->c_lnext;
1505	      flayer->l_cvlist = cv;
1506	      cv->c_lnext = 0;
1507              LayClearLine(y - vp->v_yoff, x1 - vp->v_xoff, xxe - vp->v_xoff, bce);
1508	      flayer->l_cvlist = cvlist;
1509	      cv->c_lnext = cvlnext;
1510	      flayer = oldflayer;
1511	      continue;
1512	    }
1513	}
1514      ClearLine((struct mline *)0, y, x1, xxe, bce);
1515    }
1516}
1517
1518
1519/*
1520 * if cur_only > 0, we only redisplay current line, as a full refresh is
1521 * too expensive over a low baud line.
1522 */
1523void
1524Redisplay(cur_only)
1525int cur_only;
1526{
1527  ASSERT(display);
1528
1529  /* XXX do em all? */
1530  InsertMode(0);
1531  ChangeScrollRegion(0, D_height - 1);
1532  KeypadMode(0);
1533  CursorkeysMode(0);
1534  CursorVisibility(0);
1535  MouseMode(0);
1536  SetRendition(&mchar_null);
1537  SetFlow(FLOW_NOW);
1538
1539  ClearAll();
1540#ifdef RXVT_OSC
1541  RefreshXtermOSC();
1542#endif
1543  if (cur_only > 0 && D_fore)
1544    RefreshArea(0, D_fore->w_y, D_width - 1, D_fore->w_y, 1);
1545  else
1546    RefreshAll(1);
1547  RefreshHStatus();
1548  CV_CALL(D_forecv, LayRestore();LaySetCursor());
1549}
1550
1551void
1552RedisplayDisplays(cur_only)
1553int cur_only;
1554{
1555  struct display *olddisplay = display;
1556  for (display = displays; display; display = display->d_next)
1557    Redisplay(cur_only);
1558  display = olddisplay;
1559}
1560
1561
1562/* XXX: use oml! */
1563void
1564ScrollH(y, xs, xe, n, bce, oml)
1565int y, xs, xe, n, bce;
1566struct mline *oml;
1567{
1568  int i;
1569
1570  if (n == 0)
1571    return;
1572  if (xe != D_width - 1)
1573    {
1574      RefreshLine(y, xs, xe, 0);
1575      /* UpdateLine(oml, y, xs, xe); */
1576      return;
1577    }
1578  GotoPos(xs, y);
1579  if (D_UT)
1580    SetRendition(&mchar_null);
1581#ifdef COLOR
1582  if (D_BE)
1583    SetBackColor(bce);
1584#endif
1585  if (n > 0)
1586    {
1587      if (n >= xe - xs + 1)
1588	n = xe - xs + 1;
1589      if (D_CDC && !(n == 1 && D_DC))
1590	AddCStr2(D_CDC, n);
1591      else if (D_DC)
1592	{
1593	  for (i = n; i--; )
1594	    AddCStr(D_DC);
1595	}
1596      else
1597	{
1598	  RefreshLine(y, xs, xe, 0);
1599	  /* UpdateLine(oml, y, xs, xe); */
1600	  return;
1601	}
1602    }
1603  else
1604    {
1605      if (-n >= xe - xs + 1)
1606	n = -(xe - xs + 1);
1607      if (!D_insert)
1608	{
1609	  if (D_CIC && !(n == -1 && D_IC))
1610	    AddCStr2(D_CIC, -n);
1611	  else if (D_IC)
1612	    {
1613	      for (i = -n; i--; )
1614		AddCStr(D_IC);
1615	    }
1616	  else if (D_IM)
1617	    {
1618	      InsertMode(1);
1619	      SetRendition(&mchar_null);
1620#ifdef COLOR
1621	      SetBackColor(bce);
1622#endif
1623	      for (i = -n; i--; )
1624		INSERTCHAR(' ');
1625	      bce = 0;	/* all done */
1626	    }
1627	  else
1628	    {
1629	      /* UpdateLine(oml, y, xs, xe); */
1630	      RefreshLine(y, xs, xe, 0);
1631	      return;
1632	    }
1633	}
1634      else
1635	{
1636	  SetRendition(&mchar_null);
1637#ifdef COLOR
1638	  SetBackColor(bce);
1639#endif
1640	  for (i = -n; i--; )
1641	    INSERTCHAR(' ');
1642	  bce = 0;	/* all done */
1643	}
1644    }
1645  if (bce && !D_BE)
1646    {
1647      if (n > 0)
1648        ClearLine((struct mline *)0, y, xe - n + 1, xe, bce);
1649      else
1650        ClearLine((struct mline *)0, y, xs, xs - n - 1, bce);
1651    }
1652  if (D_lp_missing && y == D_bot)
1653    {
1654      if (n > 0)
1655        WriteLP(D_width - 1 - n, y);
1656      D_lp_missing = 0;
1657    }
1658}
1659
1660void
1661ScrollV(xs, ys, xe, ye, n, bce)
1662int xs, ys, xe, ye, n, bce;
1663{
1664  int i;
1665  int up;
1666  int oldtop, oldbot;
1667  int alok, dlok, aldlfaster;
1668  int missy = 0;
1669
1670  ASSERT(display);
1671  if (n == 0)
1672    return;
1673  if (n >= ye - ys + 1 || -n >= ye - ys + 1)
1674    {
1675      ClearArea(xs, ys, xs, xe, xe, ye, bce, 0);
1676      return;
1677    }
1678  if (xs > D_vpxmin || xe < D_vpxmax)
1679    {
1680      RefreshArea(xs, ys, xe, ye, 0);
1681      return;
1682    }
1683
1684  if (D_lp_missing)
1685    {
1686      if (D_bot > ye || D_bot < ys)
1687	missy = D_bot;
1688      else
1689	{
1690	  missy = D_bot - n;
1691          if (missy > ye || missy < ys)
1692	    D_lp_missing = 0;
1693	}
1694    }
1695
1696  up = 1;
1697  if (n < 0)
1698    {
1699      up = 0;
1700      n = -n;
1701    }
1702  if (n >= ye - ys + 1)
1703    n = ye - ys + 1;
1704
1705  oldtop = D_top;
1706  oldbot = D_bot;
1707  if (ys < D_top || D_bot != ye)
1708    ChangeScrollRegion(ys, ye);
1709  alok = (D_AL || D_CAL || (ys >= D_top && ye == D_bot &&  up));
1710  dlok = (D_DL || D_CDL || (ys >= D_top && ye == D_bot && !up));
1711  if (D_top != ys && !(alok && dlok))
1712    ChangeScrollRegion(ys, ye);
1713
1714  if (D_lp_missing &&
1715      (oldbot != D_bot ||
1716       (oldbot == D_bot && up && D_top == ys && D_bot == ye)))
1717    {
1718      WriteLP(D_width - 1, oldbot);
1719      if (oldbot == D_bot)		/* have scrolled */
1720	{
1721	  if (--n == 0)
1722	    {
1723/* XXX
1724	      ChangeScrollRegion(oldtop, oldbot);
1725*/
1726	      if (bce && !D_BE)
1727		ClearLine((struct mline *)0, ye, xs, xe, bce);
1728	      return;
1729	    }
1730	}
1731    }
1732
1733  if (D_UT)
1734    SetRendition(&mchar_null);
1735#ifdef COLOR
1736  if (D_BE)
1737    SetBackColor(bce);
1738#endif
1739
1740  aldlfaster = (n > 1 && ys >= D_top && ye == D_bot && ((up && D_CDL) || (!up && D_CAL)));
1741
1742  if ((up || D_SR) && D_top == ys && D_bot == ye && !aldlfaster)
1743    {
1744      if (up)
1745	{
1746	  GotoPos(0, ye);
1747	  for(i = n; i-- > 0; )
1748	    AddCStr(D_NL);		/* was SF, I think NL is faster */
1749	}
1750      else
1751	{
1752	  GotoPos(0, ys);
1753	  for(i = n; i-- > 0; )
1754	    AddCStr(D_SR);
1755	}
1756    }
1757  else if (alok && dlok)
1758    {
1759      if (up || ye != D_bot)
1760	{
1761          GotoPos(0, up ? ys : ye+1-n);
1762          if (D_CDL && !(n == 1 && D_DL))
1763	    AddCStr2(D_CDL, n);
1764	  else
1765	    for(i = n; i--; )
1766	      AddCStr(D_DL);
1767	}
1768      if (!up || ye != D_bot)
1769	{
1770          GotoPos(0, up ? ye+1-n : ys);
1771          if (D_CAL && !(n == 1 && D_AL))
1772	    AddCStr2(D_CAL, n);
1773	  else
1774	    for(i = n; i--; )
1775	      AddCStr(D_AL);
1776	}
1777    }
1778  else
1779    {
1780      RefreshArea(xs, ys, xe, ye, 0);
1781      return;
1782    }
1783  if (bce && !D_BE)
1784    {
1785      if (up)
1786        ClearArea(xs, ye - n + 1, xs, xe, xe, ye, bce, 0);
1787      else
1788        ClearArea(xs, ys, xs, xe, xe, ys + n - 1, bce, 0);
1789    }
1790  if (D_lp_missing && missy != D_bot)
1791    WriteLP(D_width - 1, missy);
1792/* XXX
1793  ChangeScrollRegion(oldtop, oldbot);
1794  if (D_lp_missing && missy != D_bot)
1795    WriteLP(D_width - 1, missy);
1796*/
1797}
1798
1799void
1800SetAttr(new)
1801register int new;
1802{
1803  register int i, j, old, typ;
1804
1805  if (!display || (old = D_rend.attr) == new)
1806    return;
1807#ifdef COLORS16
1808  D_col16change = (old ^ new) & (A_BFG | A_BBG);
1809  new ^= D_col16change;
1810  if (old == new)
1811    return;
1812#endif
1813#if defined(TERMINFO) && defined(USE_SGR)
1814  if (D_SA)
1815    {
1816      char *tparm();
1817      SetFont(ASCII);
1818      ospeed = D_dospeed;
1819      tputs(tparm(D_SA, new & A_SO, new & A_US, new & A_RV, new & A_BL,
1820			new & A_DI, new & A_BD, 0         , 0         ,
1821			0), 1, DoAddChar);
1822      D_rend.attr = new;
1823      D_atyp = 0;
1824# ifdef COLOR
1825      if (D_hascolor)
1826	rend_setdefault(&D_rend);
1827# endif
1828      return;
1829    }
1830#endif
1831  D_rend.attr = new;
1832  typ = D_atyp;
1833  if ((new & old) != old)
1834    {
1835      if ((typ & ATYP_U))
1836        AddCStr(D_UE);
1837      if ((typ & ATYP_S))
1838        AddCStr(D_SE);
1839      if ((typ & ATYP_M))
1840	{
1841          AddCStr(D_ME);
1842#ifdef COLOR
1843	  /* ansi attrib handling: \E[m resets color, too */
1844	  if (D_hascolor)
1845	    rend_setdefault(&D_rend);
1846#endif
1847#ifdef FONT
1848	  if (!D_CG0)
1849	    {
1850	      /* D_ME may also reset the alternate charset */
1851	      D_rend.font = 0;
1852# ifdef ENCODINGS
1853	      D_realfont = 0;
1854# endif
1855	    }
1856#endif
1857	}
1858      old = 0;
1859      typ = 0;
1860    }
1861  old ^= new;
1862  for (i = 0, j = 1; old && i < NATTR; i++, j <<= 1)
1863    {
1864      if ((old & j) == 0)
1865	continue;
1866      old ^= j;
1867      if (D_attrtab[i])
1868	{
1869	  AddCStr(D_attrtab[i]);
1870	  typ |= D_attrtyp[i];
1871	}
1872    }
1873  D_atyp = typ;
1874}
1875
1876#ifdef FONT
1877void
1878SetFont(new)
1879int new;
1880{
1881  int old = D_rend.font;
1882  if (!display || old == new)
1883    return;
1884  D_rend.font = new;
1885#ifdef ENCODINGS
1886  if (D_encoding && CanEncodeFont(D_encoding, new))
1887    return;
1888  if (new == D_realfont)
1889    return;
1890  D_realfont = new;
1891#endif
1892  if (D_xtable && D_xtable[(int)(unsigned char)new] &&
1893      D_xtable[(int)(unsigned char)new][256])
1894    {
1895      AddCStr(D_xtable[(int)(unsigned char)new][256]);
1896      return;
1897    }
1898
1899  if (!D_CG0 && new != '0')
1900    {
1901      new = ASCII;
1902      if (old == new)
1903	return;
1904    }
1905
1906  if (new == ASCII)
1907    AddCStr(D_CE0);
1908#ifdef DW_CHARS
1909  else if (new < ' ')
1910    {
1911      AddStr("\033$");
1912      if (new > 2)
1913        AddChar('(');
1914      AddChar(new + '@');
1915    }
1916#endif
1917  else
1918    AddCStr2(D_CS0, new);
1919}
1920#endif
1921
1922#ifdef COLOR
1923
1924int
1925color256to16(jj)
1926int jj;
1927{
1928  int min, max;
1929  int r, g, b;
1930
1931  if (jj >= 232)
1932    {
1933      jj = (jj - 232) / 6;
1934      jj = (jj & 1) << 3 | (jj & 2 ? 7 : 0);
1935    }
1936  else if (jj >= 16)
1937    {
1938      jj -= 16;
1939      r = jj / 36;
1940      g = (jj / 6) % 6;
1941      b = jj % 6;
1942      min = r < g ? (r < b ? r : b) : (g < b ? g : b);
1943      max = r > g ? (r > b ? r : b) : (g > b ? g : b);
1944      if (min == max)
1945        jj = ((max + 1) & 2) << 2 | ((max + 1) & 4 ? 7 : 0);
1946      else
1947        jj = (b - min) / (max - min) << 2 | (g - min) / (max - min) << 1 | (r -
1948min) / (max - min) | (max > 3 ? 8 : 0);
1949    }
1950  return jj;
1951}
1952
1953#ifdef COLORS256
1954int
1955color256to88(jj)
1956int jj;
1957{
1958  int r, g, b;
1959
1960  if (jj >= 232)
1961    return (jj - 232) / 3 + 80;
1962  if (jj >= 16)
1963    {
1964      jj -= 16;
1965      r = jj / 36;
1966      g = (jj / 6) % 6;
1967      b = jj % 6;
1968      return ((r + 1) / 2) * 16 + ((g + 1) / 2) * 4 + ((b + 1) / 2) + 16;
1969    }
1970  return jj;
1971}
1972#endif
1973
1974void
1975SetColor(f, b)
1976int f, b;
1977{
1978  int of, ob;
1979  static unsigned char sftrans[8] = {0,4,2,6,1,5,3,7};
1980
1981  if (!display)
1982    return;
1983
1984  of = rend_getfg(&D_rend);
1985  ob = rend_getbg(&D_rend);
1986
1987#ifdef COLORS16
1988  /* intense default not invented yet */
1989  if (f == 0x100)
1990    f = 0;
1991  if (b == 0x100)
1992    b = 0;
1993#endif
1994  debug2("SetColor %d %d", coli2e(of), coli2e(ob));
1995  debug2(" -> %d %d\n", coli2e(f), coli2e(b));
1996  debug2("(%d %d", of, ob);
1997  debug2(" -> %d %d)\n", f, b);
1998
1999  if (!D_CAX && D_hascolor && ((f == 0 && f != of) || (b == 0 && b != ob)))
2000    {
2001      if (D_OP)
2002        AddCStr(D_OP);
2003      else
2004	{
2005	  int oattr;
2006	  oattr = D_rend.attr;
2007	  AddCStr(D_ME ? D_ME : "\033[m");
2008#ifdef FONT
2009	  if (D_ME && !D_CG0)
2010	    {
2011	      /* D_ME may also reset the alternate charset */
2012	      D_rend.font = 0;
2013# ifdef ENCODINGS
2014	      D_realfont = 0;
2015# endif
2016	    }
2017#endif
2018	  D_atyp = 0;
2019	  D_rend.attr = 0;
2020	  SetAttr(oattr);
2021	}
2022      of = ob = 0;
2023    }
2024  rend_setfg(&D_rend, f);
2025  rend_setbg(&D_rend, b);
2026#ifdef COLORS16
2027  D_col16change = 0;
2028#endif
2029  if (!D_hascolor)
2030    return;
2031  f = f ? coli2e(f) : -1;
2032  b = b ? coli2e(b) : -1;
2033  of = of ? coli2e(of) : -1;
2034  ob = ob ? coli2e(ob) : -1;
2035#ifdef COLORS256
2036  if (f != of && f > 15 && D_CCO != 256)
2037    f = D_CCO == 88 && D_CAF ? color256to88(f) : color256to16(f);
2038  if (f != of && f > 15 && D_CAF)
2039    {
2040      AddCStr2(D_CAF, f);
2041      of = f;
2042    }
2043  if (b != ob && b > 15 && D_CCO != 256)
2044    b = D_CCO == 88 && D_CAB ? color256to88(b) : color256to16(b);
2045  if (b != ob && b > 15 && D_CAB)
2046    {
2047      AddCStr2(D_CAB, b);
2048      ob = b;
2049    }
2050#endif
2051  if (f != of && f != (of | 8))
2052    {
2053      if (f == -1)
2054	AddCStr("\033[39m");	/* works because AX is set */
2055      else if (D_CAF)
2056	AddCStr2(D_CAF, f & 7);
2057      else if (D_CSF)
2058	AddCStr2(D_CSF, sftrans[f & 7]);
2059    }
2060  if (b != ob && b != (ob | 8))
2061    {
2062      if (b == -1)
2063	AddCStr("\033[49m");	/* works because AX is set */
2064      else if (D_CAB)
2065	AddCStr2(D_CAB, b & 7);
2066      else if (D_CSB)
2067	AddCStr2(D_CSB, sftrans[b & 7]);
2068    }
2069#ifdef COLORS16
2070  if (f != of && D_CXT && (f & 8) != 0 && f != -1)
2071    {
2072# ifdef TERMINFO
2073      AddCStr2("\033[9%p1%dm", f & 7);
2074# else
2075      AddCStr2("\033[9%dm", f & 7);
2076# endif
2077    }
2078  if (b != ob && D_CXT && (b & 8) != 0 && b != -1)
2079    {
2080# ifdef TERMINFO
2081      AddCStr2("\033[10%p1%dm", b & 7);
2082# else
2083      AddCStr2("\033[10%dm", b & 7);
2084# endif
2085    }
2086#endif
2087}
2088
2089static void
2090SetBackColor(new)
2091int new;
2092{
2093  if (!display)
2094    return;
2095  SetColor(rend_getfg(&D_rend), new);
2096}
2097#endif /* COLOR */
2098
2099void
2100SetRendition(mc)
2101struct mchar *mc;
2102{
2103  if (!display)
2104    return;
2105  if (nattr2color && D_hascolor && (mc->attr & nattr2color) != 0)
2106    {
2107      static struct mchar mmc;
2108      int i;
2109      mmc = *mc;
2110      for (i = 0; i < 8; i++)
2111	if (attr2color[i] && (mc->attr & (1 << i)) != 0)
2112	  {
2113	    if (mc->color == 0 && attr2color[i][3])
2114              ApplyAttrColor(attr2color[i][3], &mmc);
2115	    else if ((mc->color & 0x0f) == 0 && attr2color[i][2])
2116              ApplyAttrColor(attr2color[i][2], &mmc);
2117	    else if ((mc->color & 0xf0) == 0 && attr2color[i][1])
2118              ApplyAttrColor(attr2color[i][1], &mmc);
2119	    else
2120              ApplyAttrColor(attr2color[i][0], &mmc);
2121	  }
2122      mc = &mmc;
2123      debug2("SetRendition: mapped to %02x %02x\n", (unsigned char)mc->attr, 0x99 - (unsigned char)mc->color);
2124    }
2125  if (D_hascolor && D_CC8 && (mc->attr & (A_BFG|A_BBG)))
2126    {
2127      int a = mc->attr;
2128      if ((mc->attr & A_BFG) && D_MD)
2129	a |= A_BD;
2130      if ((mc->attr & A_BBG) && D_MB)
2131	a |= A_BL;
2132      if (D_rend.attr != a)
2133        SetAttr(a);
2134    }
2135  else if (D_rend.attr != mc->attr)
2136    SetAttr(mc->attr);
2137#ifdef COLOR
2138  if (D_rend.color != mc->color
2139# ifdef COLORS256
2140      || D_rend.colorx != mc->colorx
2141# endif
2142# ifdef COLORS16
2143      || D_col16change
2144# endif
2145    )
2146    SetColor(rend_getfg(mc), rend_getbg(mc));
2147#endif
2148#ifdef FONT
2149  if (D_rend.font != mc->font)
2150    SetFont(mc->font);
2151#endif
2152}
2153
2154void
2155SetRenditionMline(ml, x)
2156struct mline *ml;
2157int x;
2158{
2159  if (!display)
2160    return;
2161  if (nattr2color && D_hascolor && (ml->attr[x] & nattr2color) != 0)
2162    {
2163      struct mchar mc;
2164      copy_mline2mchar(&mc, ml, x);
2165      SetRendition(&mc);
2166      return;
2167    }
2168  if (D_hascolor && D_CC8 && (ml->attr[x] & (A_BFG|A_BBG)))
2169    {
2170      int a = ml->attr[x];
2171      if ((ml->attr[x] & A_BFG) && D_MD)
2172	a |= A_BD;
2173      if ((ml->attr[x] & A_BBG) && D_MB)
2174	a |= A_BL;
2175      if (D_rend.attr != a)
2176        SetAttr(a);
2177    }
2178  else if (D_rend.attr != ml->attr[x])
2179    SetAttr(ml->attr[x]);
2180#ifdef COLOR
2181  if (D_rend.color != ml->color[x]
2182# ifdef COLORS256
2183      || D_rend.colorx != ml->colorx[x]
2184# endif
2185# ifdef COLORS16
2186      || D_col16change
2187# endif
2188    )
2189    {
2190      struct mchar mc;
2191      copy_mline2mchar(&mc, ml, x);
2192      SetColor(rend_getfg(&mc), rend_getbg(&mc));
2193    }
2194#endif
2195#ifdef FONT
2196  if (D_rend.font != ml->font[x])
2197    SetFont(ml->font[x]);
2198#endif
2199}
2200
2201void
2202MakeStatus(msg)
2203char *msg;
2204{
2205  register char *s, *t;
2206  register int max;
2207
2208  if (!display)
2209    return;
2210
2211  if (D_blocked)
2212    return;
2213  if (!D_tcinited)
2214    {
2215      debug("tc not inited, just writing msg\n");
2216      if (D_processinputdata)
2217	return;		/* XXX: better */
2218      AddStr(msg);
2219      AddStr("\r\n");
2220      Flush();
2221      return;
2222    }
2223  if (!use_hardstatus || !D_HS)
2224    {
2225      max = D_width;
2226      if (D_CLP == 0)
2227	max--;
2228    }
2229  else
2230    max = D_WS > 0 ? D_WS : (D_width - !D_CLP);
2231  if (D_status)
2232    {
2233      /* same message? */
2234      if (strcmp(msg, D_status_lastmsg) == 0)
2235	{
2236	  debug("same message - increase timeout");
2237	  SetTimeout(&D_statusev, MsgWait);
2238	  return;
2239	}
2240      if (!D_status_bell)
2241	{
2242	  struct timeval now;
2243	  int ti;
2244	  gettimeofday(&now, NULL);
2245	  ti = (now.tv_sec - D_status_time.tv_sec) * 1000 + (now.tv_usec - D_status_time.tv_usec) / 1000;
2246	  if (ti < MsgMinWait)
2247	    DisplaySleep1000(MsgMinWait - ti, 0);
2248	}
2249      RemoveStatus();
2250    }
2251  for (s = t = msg; *s && t - msg < max; ++s)
2252    if (*s == BELL)
2253      AddCStr(D_BL);
2254    else if ((unsigned char)*s >= ' ' && *s != 0177)
2255      *t++ = *s;
2256  *t = '\0';
2257  if (t == msg)
2258    return;
2259  if (t - msg >= D_status_buflen)
2260    {
2261      char *buf;
2262      if (D_status_lastmsg)
2263	buf = realloc(D_status_lastmsg, t - msg + 1);
2264      else
2265	buf = malloc(t - msg + 1);
2266      if (buf)
2267	{
2268	  D_status_lastmsg = buf;
2269	  D_status_buflen = t - msg + 1;
2270	}
2271    }
2272  if (t - msg < D_status_buflen)
2273    strcpy(D_status_lastmsg, msg);
2274  D_status_len = t - msg;
2275  D_status_lastx = D_x;
2276  D_status_lasty = D_y;
2277  if (!use_hardstatus || D_has_hstatus == HSTATUS_IGNORE || D_has_hstatus == HSTATUS_MESSAGE)
2278    {
2279      D_status = STATUS_ON_WIN;
2280      debug1("using STATLINE %d\n", STATLINE);
2281      GotoPos(0, STATLINE);
2282      SetRendition(&mchar_so);
2283      InsertMode(0);
2284      AddStr(msg);
2285      if (D_status_len < max)
2286	{
2287	  /* Wayne Davison: add extra space for readability */
2288	  D_status_len++;
2289	  SetRendition(&mchar_null);
2290	  AddChar(' ');
2291	  if (D_status_len < max)
2292	    {
2293	      D_status_len++;
2294	      AddChar(' ');
2295	      AddChar('\b');
2296	    }
2297	  AddChar('\b');
2298	}
2299      D_x = -1;
2300    }
2301  else
2302    {
2303      D_status = STATUS_ON_HS;
2304      ShowHStatus(msg);
2305    }
2306  Flush();
2307  if (!display)
2308    return;
2309  if (D_status == STATUS_ON_WIN)
2310    {
2311      struct display *olddisplay = display;
2312      struct layer *oldflayer = flayer;
2313
2314      ASSERT(D_obuffree == D_obuflen);
2315      /* this is copied over from RemoveStatus() */
2316      D_status = 0;
2317      GotoPos(0, STATLINE);
2318      RefreshLine(STATLINE, 0, D_status_len - 1, 0);
2319      GotoPos(D_status_lastx, D_status_lasty);
2320      flayer = D_forecv ? D_forecv->c_layer : 0;
2321      if (flayer)
2322	LaySetCursor();
2323      display = olddisplay;
2324      flayer = oldflayer;
2325      D_status_obuflen = D_obuflen;
2326      D_status_obuffree = D_obuffree;
2327      D_obuffree = D_obuflen = 0;
2328      D_status = STATUS_ON_WIN;
2329    }
2330  gettimeofday(&D_status_time, NULL);
2331  SetTimeout(&D_statusev, MsgWait);
2332  evenq(&D_statusev);
2333#ifdef HAVE_BRAILLE
2334  RefreshBraille();	/* let user see multiple Msg()s */
2335#endif
2336}
2337
2338void
2339RemoveStatus()
2340{
2341  struct display *olddisplay;
2342  struct layer *oldflayer;
2343  int where;
2344
2345  if (!display)
2346    return;
2347  if (!(where = D_status))
2348    return;
2349
2350  debug("RemoveStatus\n");
2351  if (D_status_obuffree >= 0)
2352    {
2353      D_obuflen = D_status_obuflen;
2354      D_obuffree = D_status_obuffree;
2355      D_status_obuffree = -1;
2356      D_status = 0;
2357      D_status_bell = 0;
2358      evdeq(&D_statusev);
2359      return;
2360    }
2361  D_status = 0;
2362  D_status_bell = 0;
2363  evdeq(&D_statusev);
2364  olddisplay = display;
2365  oldflayer = flayer;
2366  if (where == STATUS_ON_WIN)
2367    {
2368      GotoPos(0, STATLINE);
2369      RefreshLine(STATLINE, 0, D_status_len - 1, 0);
2370      GotoPos(D_status_lastx, D_status_lasty);
2371    }
2372  else
2373    RefreshHStatus();
2374  flayer = D_forecv ? D_forecv->c_layer : 0;
2375  if (flayer)
2376    LaySetCursor();
2377  display = olddisplay;
2378  flayer = oldflayer;
2379}
2380
2381/* refresh the display's hstatus line */
2382void
2383ShowHStatus(str)
2384char *str;
2385{
2386  int l, i, ox, oy, max;
2387
2388  if (D_status == STATUS_ON_WIN && D_has_hstatus == HSTATUS_LASTLINE && STATLINE == D_height-1)
2389    return;	/* sorry, in use */
2390  if (D_blocked)
2391    return;
2392
2393  if (D_HS && D_has_hstatus == HSTATUS_HS)
2394    {
2395      if (!D_hstatus && (str == 0 || *str == 0))
2396	return;
2397      debug("ShowHStatus: using HS\n");
2398      SetRendition(&mchar_null);
2399      InsertMode(0);
2400      if (D_hstatus)
2401	AddCStr(D_DS);
2402      D_hstatus = 0;
2403      if (str == 0 || *str == 0)
2404	return;
2405      AddCStr2(D_TS, 0);
2406      max = D_WS > 0 ? D_WS : (D_width - !D_CLP);
2407      if ((int)strlen(str) > max)
2408	AddStrn(str, max);
2409      else
2410	AddStr(str);
2411      AddCStr(D_FS);
2412      D_hstatus = 1;
2413    }
2414  else if (D_has_hstatus == HSTATUS_LASTLINE)
2415    {
2416      debug("ShowHStatus: using last line\n");
2417      ox = D_x;
2418      oy = D_y;
2419      str = str ? str : "";
2420      l = strlen(str);
2421      if (l > D_width)
2422	l = D_width;
2423      GotoPos(0, D_height - 1);
2424      SetRendition(captionalways || D_cvlist == 0 || D_cvlist->c_next ? &mchar_null: &mchar_so);
2425      if (!PutWinMsg(str, 0, l))
2426        for (i = 0; i < l; i++)
2427	  PUTCHARLP(str[i]);
2428      if (!captionalways && D_cvlist && !D_cvlist->c_next)
2429        while (l++ < D_width)
2430	  PUTCHARLP(' ');
2431      if (l < D_width)
2432        ClearArea(l, D_height - 1, l, D_width - 1, D_width - 1, D_height - 1, 0, 0);
2433      if (ox != -1 && oy != -1)
2434	GotoPos(ox, oy);
2435      D_hstatus = *str ? 1 : 0;
2436      SetRendition(&mchar_null);
2437    }
2438  else if (str && *str && D_has_hstatus == HSTATUS_MESSAGE)
2439    {
2440      debug("ShowHStatus: using message\n");
2441      Msg(0, "%s", str);
2442    }
2443}
2444
2445
2446/*
2447 *  Refreshes the harstatus of the fore window. Shouldn't be here...
2448 */
2449void
2450RefreshHStatus()
2451{
2452  char *buf;
2453
2454  evdeq(&D_hstatusev);
2455  if (D_status == STATUS_ON_HS)
2456    return;
2457  buf = MakeWinMsgEv(hstatusstring, D_fore, '%', (D_HS && D_has_hstatus == HSTATUS_HS && D_WS > 0) ? D_WS : D_width - !D_CLP, &D_hstatusev, 0);
2458  if (buf && *buf)
2459    {
2460      ShowHStatus(buf);
2461      if (D_has_hstatus != HSTATUS_IGNORE && D_hstatusev.timeout.tv_sec)
2462        evenq(&D_hstatusev);
2463    }
2464  else
2465    ShowHStatus((char *)0);
2466}
2467
2468/*********************************************************************/
2469/*
2470 *  Here come the routines that refresh an arbitrary part of the screen.
2471 */
2472
2473void
2474RefreshAll(isblank)
2475int isblank;
2476{
2477  struct canvas *cv;
2478
2479  ASSERT(display);
2480  debug("Signalling full refresh!\n");
2481  for (cv = D_cvlist; cv; cv = cv->c_next)
2482    {
2483      CV_CALL(cv, LayRedisplayLine(-1, -1, -1, isblank));
2484      display = cv->c_display;	/* just in case! */
2485    }
2486  RefreshArea(0, 0, D_width - 1, D_height - 1, isblank);
2487}
2488
2489void
2490RefreshArea(xs, ys, xe, ye, isblank)
2491int xs, ys, xe, ye, isblank;
2492{
2493  int y;
2494  ASSERT(display);
2495  debug2("Refresh Area: %d,%d", xs, ys);
2496  debug3(" - %d,%d (isblank=%d)\n", xe, ye, isblank);
2497  if (!isblank && xs == 0 && xe == D_width - 1 && ye == D_height - 1 && (ys == 0 || D_CD))
2498    {
2499      ClearArea(xs, ys, xs, xe, xe, ye, 0, 0);
2500      isblank = 1;
2501    }
2502  for (y = ys; y <= ye; y++)
2503    RefreshLine(y, xs, xe, isblank);
2504}
2505
2506void
2507RefreshLine(y, from, to, isblank)
2508int y, from, to, isblank;
2509{
2510  struct viewport *vp, *lvp;
2511  struct canvas *cv, *lcv, *cvlist, *cvlnext;
2512  struct layer *oldflayer;
2513  int xx, yy;
2514  char *buf;
2515  struct win *p;
2516
2517  ASSERT(display);
2518
2519  debug2("RefreshLine %d %d", y, from);
2520  debug2(" %d %d\n", to, isblank);
2521
2522  if (D_status == STATUS_ON_WIN && y == STATLINE)
2523    return;	/* can't refresh status */
2524
2525  if (isblank == 0 && D_CE && to == D_width - 1 && from < to)
2526    {
2527      GotoPos(from, y);
2528      if (D_UT || D_BE)
2529	SetRendition(&mchar_null);
2530      AddCStr(D_CE);
2531      isblank = 1;
2532    }
2533  while (from <= to)
2534    {
2535      lcv = 0;
2536      lvp = 0;
2537      for (cv = display->d_cvlist; cv; cv = cv->c_next)
2538	{
2539	  if (y < cv->c_ys || y > cv->c_ye || to < cv->c_xs || from > cv->c_xe)
2540	    continue;
2541	  debug2("- canvas hit: %d %d", cv->c_xs, cv->c_ys);
2542	  debug2("  %d %d\n", cv->c_xe, cv->c_ye);
2543	  for (vp = cv->c_vplist; vp; vp = vp->v_next)
2544	    {
2545	      debug2("  - vp: %d %d", vp->v_xs, vp->v_ys);
2546	      debug2("  %d %d\n", vp->v_xe, vp->v_ye);
2547	      /* find leftmost overlapping vp */
2548	      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))
2549		{
2550		  lcv = cv;
2551		  lvp = vp;
2552		}
2553	    }
2554	}
2555      if (lvp == 0)
2556	break;
2557      if (from < lvp->v_xs)
2558	{
2559	  if (!isblank)
2560	    DisplayLine(&mline_null, &mline_blank, y, from, lvp->v_xs - 1);
2561	  from = lvp->v_xs;
2562	}
2563
2564      /* call LayRedisplayLine on canvas lcv viewport lvp */
2565      yy = y - lvp->v_yoff;
2566      xx = to < lvp->v_xe ? to : lvp->v_xe;
2567
2568      if (lcv->c_layer && yy == lcv->c_layer->l_height)
2569	{
2570	  GotoPos(from, y);
2571	  SetRendition(&mchar_blank);
2572	  while (from <= lvp->v_xe && from - lvp->v_xoff < lcv->c_layer->l_width)
2573	    {
2574	      PUTCHARLP('-');
2575	      from++;
2576	    }
2577	  if (from >= lvp->v_xe + 1)
2578	    continue;
2579	}
2580      if (lcv->c_layer == 0 || yy >= lcv->c_layer->l_height || from - lvp->v_xoff >= lcv->c_layer->l_width)
2581	{
2582	  if (!isblank)
2583	    DisplayLine(&mline_null, &mline_blank, y, from, lvp->v_xe);
2584	  from = lvp->v_xe + 1;
2585	  continue;
2586	}
2587
2588      if (xx - lvp->v_xoff >= lcv->c_layer->l_width)
2589	xx = lcv->c_layer->l_width + lvp->v_xoff - 1;
2590      oldflayer = flayer;
2591      flayer = lcv->c_layer;
2592      cvlist = flayer->l_cvlist;
2593      cvlnext = lcv->c_lnext;
2594      flayer->l_cvlist = lcv;
2595      lcv->c_lnext = 0;
2596      LayRedisplayLine(yy, from - lvp->v_xoff, xx - lvp->v_xoff, isblank);
2597      flayer->l_cvlist = cvlist;
2598      lcv->c_lnext = cvlnext;
2599      flayer = oldflayer;
2600
2601      from = xx + 1;
2602    }
2603  if (from > to)
2604    return;		/* all done */
2605
2606  if (y == D_height - 1 && D_has_hstatus == HSTATUS_LASTLINE)
2607    {
2608      RefreshHStatus();
2609      return;
2610    }
2611
2612  for (cv = display->d_cvlist; cv; cv = cv->c_next)
2613    if (y == cv->c_ye + 1)
2614      break;
2615  if (cv == 0)
2616    {
2617      if (!isblank)
2618	DisplayLine(&mline_null, &mline_blank, y, from, to);
2619      return;
2620    }
2621
2622  p = Layer2Window(cv->c_layer);
2623  buf = MakeWinMsgEv(captionstring, p, '%', D_width - !D_CLP, &cv->c_captev, 0);
2624  if (cv->c_captev.timeout.tv_sec)
2625    evenq(&cv->c_captev);
2626  xx = strlen(buf);
2627  GotoPos(from, y);
2628  SetRendition(&mchar_so);
2629  if (PutWinMsg(buf, from, to + 1))
2630    from = xx > to + 1 ? to + 1 : xx;
2631  else
2632    {
2633      while (from <= to && from < xx)
2634	{
2635	  PUTCHARLP(buf[from]);
2636	  from++;
2637	}
2638    }
2639  while (from++ <= to)
2640    PUTCHARLP(' ');
2641}
2642
2643/*********************************************************************/
2644
2645/* clear lp_missing by writing the char on the screen. The
2646 * position must be safe.
2647 */
2648static void
2649WriteLP(x2, y2)
2650int x2, y2;
2651{
2652  struct mchar oldrend;
2653
2654  ASSERT(display);
2655  ASSERT(D_lp_missing);
2656  oldrend = D_rend;
2657  debug2("WriteLP(%d,%d)\n", x2, y2);
2658#ifdef DW_CHARS
2659  if (D_lpchar.mbcs)
2660    {
2661      if (x2 > 0)
2662	x2--;
2663      else
2664	D_lpchar = mchar_blank;
2665    }
2666#endif
2667  /* Can't use PutChar */
2668  GotoPos(x2, y2);
2669  SetRendition(&D_lpchar);
2670  PUTCHAR(D_lpchar.image);
2671#ifdef DW_CHARS
2672  if (D_lpchar.mbcs)
2673    PUTCHAR(D_lpchar.mbcs);
2674#endif
2675  D_lp_missing = 0;
2676  SetRendition(&oldrend);
2677}
2678
2679void
2680ClearLine(oml, y, from, to, bce)
2681struct mline *oml;
2682int from, to, y, bce;
2683{
2684  int x;
2685#ifdef COLOR
2686  struct mchar bcechar;
2687#endif
2688
2689  debug3("ClearLine %d,%d-%d\n", y, from, to);
2690  if (D_UT)	/* Safe to erase ? */
2691    SetRendition(&mchar_null);
2692#ifdef COLOR
2693  if (D_BE)
2694    SetBackColor(bce);
2695#endif
2696  if (from == 0 && D_CB && (to != D_width - 1 || (D_x == to && D_y == y)) && (!bce || D_BE))
2697    {
2698      GotoPos(to, y);
2699      AddCStr(D_CB);
2700      return;
2701    }
2702  if (to == D_width - 1 && D_CE && (!bce || D_BE))
2703    {
2704      GotoPos(from, y);
2705      AddCStr(D_CE);
2706      return;
2707    }
2708  if (oml == 0)
2709    oml = &mline_null;
2710#ifdef COLOR
2711  if (!bce)
2712    {
2713      DisplayLine(oml, &mline_blank, y, from, to);
2714      return;
2715    }
2716  bcechar = mchar_blank;
2717  rend_setbg(&bcechar, bce);
2718  for (x = from; x <= to; x++)
2719    copy_mchar2mline(&bcechar, &mline_old, x);
2720  DisplayLine(oml, &mline_old, y, from, to);
2721#else
2722  DisplayLine(oml, &mline_blank, y, from, to);
2723#endif
2724}
2725
2726void
2727DisplayLine(oml, ml, y, from, to)
2728struct mline *oml, *ml;
2729int from, to, y;
2730{
2731  register int x;
2732  int last2flag = 0, delete_lp = 0;
2733
2734  ASSERT(display);
2735  ASSERT(y >= 0 && y < D_height);
2736  ASSERT(from >= 0 && from < D_width);
2737  ASSERT(to >= 0 && to < D_width);
2738  if (!D_CLP && y == D_bot && to == D_width - 1)
2739    {
2740      if (D_lp_missing || !cmp_mline(oml, ml, to))
2741	{
2742#ifdef DW_CHARS
2743	  if ((D_IC || D_IM) && from < to && !dw_left(ml, to, D_encoding))
2744#else
2745	  if ((D_IC || D_IM) && from < to)
2746#endif
2747	    {
2748	      last2flag = 1;
2749	      D_lp_missing = 0;
2750	      to--;
2751	    }
2752	  else
2753	    {
2754	      delete_lp = !cmp_mchar_mline(&mchar_blank, oml, to) && (D_CE || D_DC || D_CDC);
2755	      D_lp_missing = !cmp_mchar_mline(&mchar_blank, ml, to);
2756	      copy_mline2mchar(&D_lpchar, ml, to);
2757	    }
2758	}
2759      to--;
2760    }
2761#ifdef DW_CHARS
2762  if (D_mbcs)
2763    {
2764      /* finish dw-char (can happen after a wrap) */
2765      debug("DisplayLine finishing kanji\n");
2766      SetRenditionMline(ml, from);
2767      PUTCHAR(ml->image[from]);
2768      from++;
2769    }
2770#endif
2771  for (x = from; x <= to; x++)
2772    {
2773#if 0	/* no longer needed */
2774      if (x || D_x != D_width || D_y != y - 1)
2775#endif
2776        {
2777	  if (x < to || x != D_width - 1 || ml->image[x + 1])
2778	    if (cmp_mline(oml, ml, x))
2779	      continue;
2780	  GotoPos(x, y);
2781        }
2782#ifdef DW_CHARS
2783      if (dw_right(ml, x, D_encoding))
2784	{
2785	  x--;
2786	  debug1("DisplayLine on right side of dw char- x now %d\n", x);
2787	  GotoPos(x, y);
2788	}
2789      if (x == to && dw_left(ml, x, D_encoding))
2790	break;	/* don't start new kanji */
2791#endif
2792      SetRenditionMline(ml, x);
2793      PUTCHAR(ml->image[x]);
2794#ifdef DW_CHARS
2795      if (dw_left(ml, x, D_encoding))
2796        PUTCHAR(ml->image[++x]);
2797#endif
2798    }
2799#if 0	/* not needed any longer */
2800  /* compare != 0 because ' ' can happen when clipping occures */
2801  if (to == D_width - 1 && y < D_height - 1 && D_x == D_width && ml->image[to + 1])
2802    GotoPos(0, y + 1);
2803#endif
2804  if (last2flag)
2805    {
2806      GotoPos(x, y);
2807      SetRenditionMline(ml, x + 1);
2808      PUTCHAR(ml->image[x + 1]);
2809      GotoPos(x, y);
2810      SetRenditionMline(ml, x);
2811      INSERTCHAR(ml->image[x]);
2812    }
2813  else if (delete_lp)
2814    {
2815      if (D_UT)
2816	SetRendition(&mchar_null);
2817      if (D_DC)
2818	AddCStr(D_DC);
2819      else if (D_CDC)
2820	AddCStr2(D_CDC, 1);
2821      else if (D_CE)
2822	AddCStr(D_CE);
2823    }
2824}
2825
2826void
2827PutChar(c, x, y)
2828struct mchar *c;
2829int x, y;
2830{
2831  GotoPos(x, y);
2832  SetRendition(c);
2833  PUTCHARLP(c->image);
2834#ifdef DW_CHARS
2835  if (c->mbcs)
2836    {
2837# ifdef UTF8
2838      if (D_encoding == UTF8)
2839        D_rend.font = 0;
2840# endif
2841      PUTCHARLP(c->mbcs);
2842    }
2843#endif
2844}
2845
2846void
2847InsChar(c, x, xe, y, oml)
2848struct mchar *c;
2849int x, xe, y;
2850struct mline *oml;
2851{
2852  GotoPos(x, y);
2853  if (y == D_bot && !D_CLP)
2854    {
2855      if (x == D_width - 1)
2856        {
2857          D_lp_missing = 1;
2858          D_lpchar = *c;
2859          return;
2860        }
2861      if (xe == D_width - 1)
2862        D_lp_missing = 0;
2863    }
2864  if (x == xe)
2865    {
2866      SetRendition(c);
2867      PUTCHARLP(c->image);
2868      return;
2869    }
2870  if (!(D_IC || D_CIC || D_IM) || xe != D_width - 1)
2871    {
2872      RefreshLine(y, x, xe, 0);
2873      GotoPos(x + 1, y);
2874      /* UpdateLine(oml, y, x, xe); */
2875      return;
2876    }
2877  InsertMode(1);
2878  if (!D_insert)
2879    {
2880#ifdef DW_CHARS
2881      if (c->mbcs && D_IC)
2882        AddCStr(D_IC);
2883      if (D_IC)
2884        AddCStr(D_IC);
2885      else
2886        AddCStr2(D_CIC, c->mbcs ? 2 : 1);
2887#else
2888      if (D_IC)
2889        AddCStr(D_IC);
2890      else
2891        AddCStr2(D_CIC, 1);
2892#endif
2893    }
2894  SetRendition(c);
2895  RAW_PUTCHAR(c->image);
2896#ifdef DW_CHARS
2897  if (c->mbcs)
2898    {
2899# ifdef UTF8
2900      if (D_encoding == UTF8)
2901	D_rend.font = 0;
2902# endif
2903      if (D_x == D_width - 1)
2904	PUTCHARLP(c->mbcs);
2905      else
2906	RAW_PUTCHAR(c->mbcs);
2907    }
2908#endif
2909}
2910
2911void
2912WrapChar(c, x, y, xs, ys, xe, ye, ins)
2913struct mchar *c;
2914int x, y;
2915int xs, ys, xe, ye;
2916int ins;
2917{
2918  int bce;
2919
2920#ifdef COLOR
2921  bce = rend_getbg(c);
2922#else
2923  bce = 0;
2924#endif
2925  debug("WrapChar:");
2926  debug2("  x %d  y %d", x, y);
2927  debug2("  Dx %d  Dy %d", D_x, D_y);
2928  debug2("  xs %d  ys %d", xs, ys);
2929  debug3("  xe %d  ye %d ins %d\n", xe, ye, ins);
2930  if (xs != 0 || x != D_width || !D_AM)
2931    {
2932      if (y == ye)
2933	ScrollV(xs, ys, xe, ye, 1, bce);
2934      else if (y < D_height - 1)
2935	y++;
2936      if (ins)
2937	InsChar(c, xs, xe, y, 0);
2938      else
2939        PutChar(c, xs, y);
2940      return;
2941    }
2942  if (y == ye)		/* we have to scroll */
2943    {
2944      debug("- scrolling\n");
2945      ChangeScrollRegion(ys, ye);
2946      if (D_bot != y || D_x != D_width || (!bce && !D_BE))
2947	{
2948	  debug("- have to call ScrollV\n");
2949	  ScrollV(xs, ys, xe, ye, 1, bce);
2950	  y--;
2951	}
2952    }
2953  else if (y == D_bot)	/* remove unusable region? */
2954    ChangeScrollRegion(0, D_height - 1);
2955  if (D_x != D_width || D_y != y)
2956    {
2957      if (D_CLP && y >= 0)		/* don't even try if !LP */
2958        RefreshLine(y, D_width - 1, D_width - 1, 0);
2959      debug2("- refresh last char -> x,y now %d,%d\n", D_x, D_y);
2960      if (D_x != D_width || D_y != y)	/* sorry, no bonus */
2961	{
2962	  if (y == ye)
2963	    ScrollV(xs, ys, xe, ye, 1, bce);
2964	  GotoPos(xs, y == ye || y == D_height - 1 ? y : y + 1);
2965	}
2966    }
2967  debug("- writeing new char");
2968  if (y != ye && y < D_height - 1)
2969    y++;
2970  if (ins != D_insert)
2971    InsertMode(ins);
2972  if (ins && !D_insert)
2973    {
2974      InsChar(c, 0, xe, y, 0);
2975      debug2(" -> done with insert (%d,%d)\n", D_x, D_y);
2976      return;
2977    }
2978  D_y = y;
2979  D_x = 0;
2980  SetRendition(c);
2981  RAW_PUTCHAR(c->image);
2982#ifdef DW_CHARS
2983  if (c->mbcs)
2984    {
2985# ifdef UTF8
2986      if (D_encoding == UTF8)
2987	D_rend.font = 0;
2988# endif
2989      RAW_PUTCHAR(c->mbcs);
2990    }
2991#endif
2992  debug2(" -> done (%d,%d)\n", D_x, D_y);
2993}
2994
2995int
2996ResizeDisplay(wi, he)
2997int wi, he;
2998{
2999  ASSERT(display);
3000  debug2("ResizeDisplay: to (%d,%d).\n", wi, he);
3001  if (D_width == wi && D_height == he)
3002    {
3003      debug("ResizeDisplay: No change\n");
3004      return 0;
3005    }
3006  if (D_width != wi && (D_height == he || !D_CWS) && D_CZ0 && (wi == Z0width || wi == Z1width))
3007    {
3008      debug("ResizeDisplay: using Z0/Z1\n");
3009      AddCStr(wi == Z0width ? D_CZ0 : D_CZ1);
3010      ChangeScreenSize(wi, D_height, 0);
3011      return (he == D_height) ? 0 : -1;
3012    }
3013  if (D_CWS)
3014    {
3015      debug("ResizeDisplay: using WS\n");
3016      AddCStr(tgoto(D_CWS, wi, he));
3017      ChangeScreenSize(wi, he, 0);
3018      return 0;
3019    }
3020  return -1;
3021}
3022
3023void
3024ChangeScrollRegion(newtop, newbot)
3025int newtop, newbot;
3026{
3027  if (display == 0)
3028    return;
3029  if (newtop == newbot)
3030    return;			/* xterm etc can't do it */
3031  if (newtop == -1)
3032    newtop = 0;
3033  if (newbot == -1)
3034    newbot = D_height - 1;
3035  if (D_CS == 0)
3036    {
3037      D_top = 0;
3038      D_bot = D_height - 1;
3039      return;
3040    }
3041  if (D_top == newtop && D_bot == newbot)
3042    return;
3043  debug2("ChangeScrollRegion: (%d - %d)\n", newtop, newbot);
3044  AddCStr(tgoto(D_CS, newbot, newtop));
3045  D_top = newtop;
3046  D_bot = newbot;
3047  D_y = D_x = -1;		/* Just in case... */
3048}
3049
3050#ifdef RXVT_OSC
3051void
3052SetXtermOSC(i, s)
3053int i;
3054char *s;
3055{
3056  static char oscs[] = "1;\000\00020;\00039;\00049;\000";
3057
3058  ASSERT(display);
3059  if (!D_CXT)
3060    return;
3061  if (!s)
3062    s = "";
3063  if (!D_xtermosc[i] && !*s)
3064    return;
3065  if (i == 0 && !*s)
3066    s = "screen";		/* always set icon name */
3067  if (i == 1 && !*s)
3068    s = "";			/* no background */
3069  if (i == 2 && !*s)
3070    s = "black";		/* black text */
3071  if (i == 3 && !*s)
3072    s = "white";		/* on white background */
3073  D_xtermosc[i] = 1;
3074  AddStr("\033]");
3075  AddStr(oscs + i * 4);
3076  AddStr(s);
3077  AddChar(7);
3078}
3079
3080void
3081ClearAllXtermOSC()
3082{
3083  int i;
3084  for (i = 3; i >= 0; i--)
3085    SetXtermOSC(i, 0);
3086}
3087#endif
3088
3089/*
3090 *  Output buffering routines
3091 */
3092
3093void
3094AddStr(str)
3095char *str;
3096{
3097  register char c;
3098
3099  ASSERT(display);
3100
3101#ifdef UTF8
3102  if (D_encoding == UTF8)
3103    {
3104      while ((c = *str++))
3105        AddUtf8((unsigned char)c);
3106      return;
3107    }
3108#endif
3109  while ((c = *str++))
3110    AddChar(c);
3111}
3112
3113void
3114AddStrn(str, n)
3115char *str;
3116int n;
3117{
3118  register char c;
3119
3120  ASSERT(display);
3121#ifdef UTF8
3122  if (D_encoding == UTF8)
3123    {
3124      while ((c = *str++) && n-- > 0)
3125        AddUtf8((unsigned char)c);
3126    }
3127  else
3128#endif
3129  while ((c = *str++) && n-- > 0)
3130    AddChar(c);
3131  while (n-- > 0)
3132    AddChar(' ');
3133}
3134
3135void
3136Flush()
3137{
3138  register int l;
3139  register char *p;
3140
3141  ASSERT(display);
3142  l = D_obufp - D_obuf;
3143  debug1("Flush(): %d\n", l);
3144  if (l == 0)
3145    return;
3146  ASSERT(l + D_obuffree == D_obuflen);
3147  if (D_userfd < 0)
3148    {
3149      D_obuffree += l;
3150      D_obufp = D_obuf;
3151      return;
3152    }
3153  p = D_obuf;
3154  if (fcntl(D_userfd, F_SETFL, 0))
3155    debug1("Warning: BLOCK fcntl failed: %d\n", errno);
3156  while (l)
3157    {
3158      register int wr;
3159      wr = write(D_userfd, p, l);
3160      if (wr <= 0)
3161	{
3162	  if (errno == EINTR)
3163	    continue;
3164	  debug1("Writing to display: %d\n", errno);
3165	  wr = l;
3166	}
3167      if (!display)
3168	return;
3169      D_obuffree += wr;
3170      p += wr;
3171      l -= wr;
3172    }
3173  D_obuffree += l;
3174  D_obufp = D_obuf;
3175  if (fcntl(D_userfd, F_SETFL, FNBLOCK))
3176    debug1("Warning: NBLOCK fcntl failed: %d\n", errno);
3177  if (D_blocked == 1)
3178    D_blocked = 0;
3179  D_blocked_fuzz = 0;
3180}
3181
3182void
3183freetty()
3184{
3185  if (D_userfd >= 0)
3186    close(D_userfd);
3187  debug1("did freetty %d\n", D_userfd);
3188  D_userfd = -1;
3189  D_obufp = 0;
3190  D_obuffree = 0;
3191  if (D_obuf)
3192    free(D_obuf);
3193  D_obuf = 0;
3194  D_obuflen = 0;
3195  D_obuflenmax = -D_obufmax;
3196  D_blocked = 0;
3197  D_blocked_fuzz = 0;
3198}
3199
3200/*
3201 *  Asynchronous output routines by
3202 *  Tim MacKenzie (tym@dibbler.cs.monash.edu.au)
3203 */
3204
3205void
3206Resize_obuf()
3207{
3208  register int ind;
3209
3210  ASSERT(display);
3211  if (D_status_obuffree >= 0)
3212    {
3213      ASSERT(D_obuffree == -1);
3214      if (!D_status_bell)
3215	{
3216	  struct timeval now;
3217	  int ti;
3218	  gettimeofday(&now, NULL);
3219	  ti = (now.tv_sec - D_status_time.tv_sec) * 1000 + (now.tv_usec - D_status_time.tv_usec) / 1000;
3220	  if (ti < MsgMinWait)
3221	    DisplaySleep1000(MsgMinWait - ti, 0);
3222	}
3223      RemoveStatus();
3224      if (--D_obuffree > 0)	/* redo AddChar decrement */
3225	return;
3226    }
3227  if (D_obuflen && D_obuf)
3228    {
3229      ind  = D_obufp - D_obuf;
3230      D_obuflen += GRAIN;
3231      D_obuffree += GRAIN;
3232      D_obuf = realloc(D_obuf, D_obuflen);
3233    }
3234  else
3235    {
3236      ind  = 0;
3237      D_obuflen = GRAIN;
3238      D_obuffree = GRAIN;
3239      D_obuf = malloc(D_obuflen);
3240    }
3241  if (!D_obuf)
3242    Panic(0, "Out of memory");
3243  D_obufp = D_obuf + ind;
3244  D_obuflenmax = D_obuflen - D_obufmax;
3245  debug1("ResizeObuf: resized to %d\n", D_obuflen);
3246}
3247
3248void
3249DisplaySleep1000(n, eat)
3250int n;
3251int eat;
3252{
3253  char buf;
3254  fd_set r;
3255  struct timeval t;
3256
3257  if (n <= 0)
3258    return;
3259  if (!display)
3260    {
3261      debug("DisplaySleep has no display sigh\n");
3262      sleep1000(n);
3263      return;
3264    }
3265  t.tv_usec = (n % 1000) * 1000;
3266  t.tv_sec = n / 1000;
3267  FD_ZERO(&r);
3268  FD_SET(D_userfd, &r);
3269  if (select(FD_SETSIZE, &r, (fd_set *)0, (fd_set *)0, &t) > 0)
3270    {
3271      debug("display activity stopped sleep\n");
3272      if (eat)
3273        read(D_userfd, &buf, 1);
3274    }
3275  debug2("DisplaySleep(%d) ending, eat was %d\n", n, eat);
3276}
3277
3278#ifdef AUTO_NUKE
3279void
3280NukePending()
3281{/* Nuke pending output in current display, clear screen */
3282  register int len;
3283  int oldtop = D_top, oldbot = D_bot;
3284  struct mchar oldrend;
3285  int oldkeypad = D_keypad, oldcursorkeys = D_cursorkeys;
3286  int oldcurvis = D_curvis;
3287  int oldmouse = D_mouse;
3288
3289  oldrend = D_rend;
3290  len = D_obufp - D_obuf;
3291  debug1("NukePending: nuking %d chars\n", len);
3292
3293  /* Throw away any output that we can... */
3294# ifdef POSIX
3295  tcflush(D_userfd, TCOFLUSH);
3296# else
3297#  ifdef TCFLSH
3298  (void) ioctl(D_userfd, TCFLSH, (char *) 1);
3299#  endif
3300# endif
3301
3302  D_obufp = D_obuf;
3303  D_obuffree += len;
3304  D_top = D_bot = -1;
3305  AddCStr(D_TI);
3306  AddCStr(D_IS);
3307  /* Turn off all attributes. (Tim MacKenzie) */
3308  if (D_ME)
3309    AddCStr(D_ME);
3310  else
3311    {
3312#ifdef COLOR
3313      if (D_hascolor)
3314	AddStr("\033[m");	/* why is D_ME not set? */
3315#endif
3316      AddCStr(D_SE);
3317      AddCStr(D_UE);
3318    }
3319  /* Check for toggle */
3320  if (D_IM && strcmp(D_IM, D_EI))
3321    AddCStr(D_EI);
3322  D_insert = 0;
3323  /* Check for toggle */
3324#ifdef MAPKEYS
3325  if (D_KS && strcmp(D_KS, D_KE))
3326    AddCStr(D_KS);
3327  if (D_CCS && strcmp(D_CCS, D_CCE))
3328    AddCStr(D_CCS);
3329#else
3330  if (D_KS && strcmp(D_KS, D_KE))
3331    AddCStr(D_KE);
3332  D_keypad = 0;
3333  if (D_CCS && strcmp(D_CCS, D_CCE))
3334    AddCStr(D_CCE);
3335  D_cursorkeys = 0;
3336#endif
3337  AddCStr(D_CE0);
3338  D_rend = mchar_null;
3339  D_atyp = 0;
3340  AddCStr(D_DS);
3341  D_hstatus = 0;
3342  AddCStr(D_VE);
3343  D_curvis = 0;
3344  ChangeScrollRegion(oldtop, oldbot);
3345  SetRendition(&oldrend);
3346  KeypadMode(oldkeypad);
3347  CursorkeysMode(oldcursorkeys);
3348  CursorVisibility(oldcurvis);
3349  MouseMode(oldmouse);
3350  if (D_CWS)
3351    {
3352      debug("ResizeDisplay: using WS\n");
3353      AddCStr(tgoto(D_CWS, D_width, D_height));
3354    }
3355  else if (D_CZ0 && (D_width == Z0width || D_width == Z1width))
3356    {
3357      debug("ResizeDisplay: using Z0/Z1\n");
3358      AddCStr(D_width == Z0width ? D_CZ0 : D_CZ1);
3359    }
3360}
3361#endif /* AUTO_NUKE */
3362
3363#ifdef linux
3364/* linux' select can't handle flow control, so wait 100ms if
3365 * we get EAGAIN
3366 */
3367static void
3368disp_writeev_eagain(ev, data)
3369struct event *ev;
3370char *data;
3371{
3372  display = (struct display *)data;
3373  evdeq(&D_writeev);
3374  D_writeev.type = EV_WRITE;
3375  D_writeev.handler = disp_writeev_fn;
3376  evenq(&D_writeev);
3377}
3378#endif
3379
3380static void
3381disp_writeev_fn(ev, data)
3382struct event *ev;
3383char *data;
3384{
3385  int len, size = OUTPUT_BLOCK_SIZE;
3386
3387  display = (struct display *)data;
3388  len = D_obufp - D_obuf;
3389  if (len < size)
3390    size = len;
3391  ASSERT(len >= 0);
3392  size = write(D_userfd, D_obuf, size);
3393  if (size >= 0)
3394    {
3395      len -= size;
3396      if (len)
3397	{
3398	  bcopy(D_obuf + size, D_obuf, len);
3399	  debug2("ASYNC: wrote %d - remaining %d\n", size, len);
3400	}
3401      D_obufp -= size;
3402      D_obuffree += size;
3403      if (D_blocked_fuzz)
3404	{
3405	  D_blocked_fuzz -= size;
3406	  if (D_blocked_fuzz < 0)
3407	    D_blocked_fuzz = 0;
3408	}
3409      if (D_blockedev.queued)
3410	{
3411	  if (D_obufp - D_obuf > D_obufmax / 2)
3412	    {
3413	      debug2("%s: resetting timeout to %g secs\n", D_usertty, D_nonblock/1000.);
3414	      SetTimeout(&D_blockedev, D_nonblock);
3415	    }
3416	  else
3417	    {
3418	      debug1("%s: deleting blocked timeout\n", D_usertty);
3419	      evdeq(&D_blockedev);
3420	    }
3421	}
3422      if (D_blocked == 1 && D_obuf == D_obufp)
3423	{
3424	  /* empty again, restart output */
3425          debug1("%s: buffer empty, unblocking\n", D_usertty);
3426	  D_blocked = 0;
3427	  Activate(D_fore ? D_fore->w_norefresh : 0);
3428	  D_blocked_fuzz = D_obufp - D_obuf;
3429	}
3430    }
3431  else
3432    {
3433#ifdef linux
3434      /* linux flow control is badly broken */
3435      if (errno == EAGAIN)
3436	{
3437	  evdeq(&D_writeev);
3438	  D_writeev.type = EV_TIMEOUT;
3439	  D_writeev.handler = disp_writeev_eagain;
3440	  SetTimeout(&D_writeev, 100);
3441	  evenq(&D_writeev);
3442	}
3443#endif
3444      if (errno != EINTR && errno != EAGAIN)
3445#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
3446	if (errno != EWOULDBLOCK)
3447#endif
3448	  Msg(errno, "Error writing output to display");
3449    }
3450}
3451
3452static void
3453disp_readev_fn(ev, data)
3454struct event *ev;
3455char *data;
3456{
3457  int size;
3458  char buf[IOSIZE];
3459  struct canvas *cv;
3460
3461  display = (struct display *)data;
3462
3463  /* Hmmmm... a bit ugly... */
3464  if (D_forecv)
3465    for (cv = D_forecv->c_layer->l_cvlist; cv; cv = cv->c_lnext)
3466      {
3467        display = cv->c_display;
3468        if (D_status == STATUS_ON_WIN)
3469          RemoveStatus();
3470      }
3471
3472  display = (struct display *)data;
3473  if (D_fore == 0)
3474    size = IOSIZE;
3475  else
3476    {
3477#ifdef PSEUDOS
3478      if (W_UWP(D_fore))
3479	size = sizeof(D_fore->w_pwin->p_inbuf) - D_fore->w_pwin->p_inlen;
3480      else
3481#endif
3482	size = sizeof(D_fore->w_inbuf) - D_fore->w_inlen;
3483    }
3484
3485  if (size > IOSIZE)
3486    size = IOSIZE;
3487  if (size <= 0)
3488    size = 1;     /* Always allow one char for command keys */
3489
3490  size = read(D_userfd, buf, size);
3491  if (size < 0)
3492    {
3493      if (errno == EINTR || errno == EAGAIN)
3494	return;
3495#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
3496      if (errno == EWOULDBLOCK)
3497	return;
3498#endif
3499      debug1("Read error: %d - hangup!\n", errno);
3500      Hangup();
3501      sleep(1);
3502      return;
3503    }
3504  else if (size == 0)
3505    {
3506      debug("Found EOF - hangup!\n");
3507      Hangup();
3508      sleep(1);
3509      return;
3510    }
3511  if (D_blocked == 4)
3512    {
3513      D_blocked = 0;
3514#ifdef BLANKER_PRG
3515      KillBlanker();
3516#endif
3517      Activate(D_fore ? D_fore->w_norefresh : 0);
3518      ResetIdle();
3519      return;
3520    }
3521#ifdef ZMODEM
3522  if (D_blocked > 1)	/* 2, 3 */
3523    {
3524      char *bufp;
3525      struct win *p;
3526
3527      flayer = 0;
3528      for (p = windows; p ; p = p->w_next)
3529	if (p->w_zdisplay == display)
3530	  {
3531	    flayer = &p->w_layer;
3532	    bufp = buf;
3533	    while (size > 0)
3534	      LayProcess(&bufp, &size);
3535	    return;
3536	  }
3537      debug("zmodem window gone, deblocking display");
3538      zmodem_abort(0, display);
3539    }
3540#endif
3541  if (idletimo > 0)
3542    ResetIdle();
3543  if (D_fore)
3544    D_fore->w_lastdisp = display;
3545  if (D_mouse && D_forecv)
3546    {
3547      unsigned char *bp = (unsigned char *)buf;
3548      int x, y, i = size;
3549
3550      /* XXX this assumes that the string is read in as a whole... */
3551      for (i = size; i > 0; i--, bp++)
3552	{
3553	  if (i > 5 && bp[0] == 033 && bp[1] == '[' && bp[2] == 'M')
3554	    {
3555	      bp++;
3556	      i--;
3557	    }
3558	  else if (i < 5 || bp[0] != 0233 || bp[1] != 'M')
3559	    continue;
3560	  x = bp[3] - 33;
3561	  y = bp[4] - 33;
3562	  if (x >= D_forecv->c_xs && x <= D_forecv->c_xe && y >= D_forecv->c_ys && y <= D_forecv->c_ye)
3563	    {
3564	      x -= D_forecv->c_xoff;
3565	      y -= D_forecv->c_yoff;
3566	      if (x >= 0 && x < D_forecv->c_layer->l_width && y >= 0 && y < D_forecv->c_layer->l_height)
3567		{
3568		  bp[3] = x + 33;
3569		  bp[4] = y + 33;
3570		  i -= 4;
3571		  bp += 4;
3572		  continue;
3573		}
3574	    }
3575	  if (bp[0] == '[')
3576	    {
3577	      bcopy((char *)bp + 1, (char *)bp, i);
3578	      bp--;
3579	      size--;
3580	    }
3581	  if (i > 5)
3582	    bcopy((char *)bp + 5, (char *)bp, i - 5);
3583	  bp--;
3584	  i -= 4;
3585	  size -= 5;
3586	}
3587    }
3588#ifdef ENCODINGS
3589  if (D_encoding != (D_forecv ? D_forecv->c_layer->l_encoding : 0))
3590    {
3591      int i, j, c, enc;
3592      char buf2[IOSIZE * 2 + 10];
3593      enc = D_forecv ? D_forecv->c_layer->l_encoding : 0;
3594      for (i = j = 0; i < size; i++)
3595	{
3596	  c = ((unsigned char *)buf)[i];
3597	  c = DecodeChar(c, D_encoding, &D_decodestate);
3598	  if (c == -2)
3599	    i--;	/* try char again */
3600	  if (c < 0)
3601	    continue;
3602	  if (pastefont)
3603	    {
3604	      int font = 0;
3605	      j += EncodeChar(buf2 + j, c, enc, &font);
3606	      j += EncodeChar(buf2 + j, -1, enc, &font);
3607	    }
3608	  else
3609	    j += EncodeChar(buf2 + j, c, enc, 0);
3610	  if (j > (int)sizeof(buf2) - 10)	/* just in case... */
3611	    break;
3612	}
3613      (*D_processinput)(buf2, j);
3614      return;
3615    }
3616#endif
3617  (*D_processinput)(buf, size);
3618}
3619
3620static void
3621disp_status_fn(ev, data)
3622struct event *ev;
3623char *data;
3624{
3625  display = (struct display *)data;
3626  debug1("disp_status_fn for display %x\n", (int)display);
3627  if (D_status)
3628    RemoveStatus();
3629}
3630
3631static void
3632disp_hstatus_fn(ev, data)
3633struct event *ev;
3634char *data;
3635{
3636  display = (struct display *)data;
3637  if (D_status == STATUS_ON_HS)
3638    {
3639      SetTimeout(ev, 1);
3640      evenq(ev);
3641      return;
3642    }
3643  RefreshHStatus();
3644}
3645
3646static void
3647disp_blocked_fn(ev, data)
3648struct event *ev;
3649char *data;
3650{
3651  struct win *p;
3652
3653  display = (struct display *)data;
3654  debug1("blocked timeout %s\n", D_usertty);
3655  if (D_obufp - D_obuf > D_obufmax + D_blocked_fuzz)
3656    {
3657      debug("stopping output to display\n");
3658      D_blocked = 1;
3659      /* re-enable all windows */
3660      for (p = windows; p; p = p->w_next)
3661	if (p->w_readev.condneg == &D_obuflenmax)
3662	  {
3663	    debug1("freeing window #%d\n", p->w_number);
3664	    p->w_readev.condpos = p->w_readev.condneg = 0;
3665	  }
3666    }
3667}
3668
3669static void
3670cv_winid_fn(ev, data)
3671struct event *ev;
3672char *data;
3673{
3674  int ox, oy;
3675  struct canvas *cv = (struct canvas *)data;
3676
3677  display = cv->c_display;
3678  if (D_status == STATUS_ON_WIN)
3679    {
3680      SetTimeout(ev, 1);
3681      evenq(ev);
3682      return;
3683    }
3684  ox = D_x;
3685  oy = D_y;
3686  if (cv->c_ye + 1 < D_height)
3687    RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0);
3688  if (ox != -1 && oy != -1)
3689    GotoPos(ox, oy);
3690}
3691
3692#ifdef MAPKEYS
3693static void
3694disp_map_fn(ev, data)
3695struct event *ev;
3696char *data;
3697{
3698  char *p;
3699  int l, i;
3700  unsigned char *q;
3701  display = (struct display *)data;
3702  debug("Flushing map sequence\n");
3703  if (!(l = D_seql))
3704    return;
3705  p = (char *)D_seqp - l;
3706  D_seqp = D_kmaps + 3;
3707  D_seql = 0;
3708  if ((q = D_seqh) != 0)
3709    {
3710      D_seqh = 0;
3711      i = q[0] << 8 | q[1];
3712      i &= ~KMAP_NOTIMEOUT;
3713      debug1("Mapping former hit #%d - ", i);
3714      debug2("%d(%s) - ", q[2], q + 3);
3715      if (StuffKey(i))
3716	ProcessInput2((char *)q + 3, q[2]);
3717      if (display == 0)
3718	return;
3719      l -= q[2];
3720      p += q[2];
3721    }
3722  else
3723    D_dontmap = 1;
3724  ProcessInput(p, l);
3725}
3726#endif
3727
3728static void
3729disp_idle_fn(ev, data)
3730struct event *ev;
3731char *data;
3732{
3733  struct display *olddisplay;
3734  display = (struct display *)data;
3735  debug("idle timeout\n");
3736  if (idletimo <= 0 || idleaction.nr == RC_ILLEGAL)
3737    return;
3738  olddisplay = display;
3739  flayer = D_forecv->c_layer;
3740  fore = D_fore;
3741  DoAction(&idleaction, -1);
3742  if (idleaction.nr == RC_BLANKER)
3743    return;
3744  for (display = displays; display; display = display->d_next)
3745    if (olddisplay == display)
3746      break;
3747  if (display)
3748    ResetIdle();
3749}
3750
3751void
3752ResetIdle()
3753{
3754  if (idletimo > 0)
3755    {
3756      SetTimeout(&D_idleev, idletimo);
3757      if (!D_idleev.queued)
3758	evenq(&D_idleev);
3759    }
3760  else
3761    evdeq(&D_idleev);
3762}
3763
3764
3765#ifdef BLANKER_PRG
3766
3767static void
3768disp_blanker_fn(ev, data)
3769struct event *ev;
3770char *data;
3771{
3772  char buf[IOSIZE], *b;
3773  int size;
3774
3775  display = (struct display *)data;
3776  size = read(D_blankerev.fd, buf, IOSIZE);
3777  if (size <= 0)
3778    {
3779      evdeq(&D_blankerev);
3780      close(D_blankerev.fd);
3781      D_blankerev.fd = -1;
3782      return;
3783    }
3784  for (b = buf; size; size--)
3785    AddChar(*b++);
3786}
3787
3788void
3789KillBlanker()
3790{
3791  int oldtop = D_top, oldbot = D_bot;
3792  struct mchar oldrend;
3793
3794  if (D_blankerev.fd == -1)
3795    return;
3796  if (D_blocked == 4)
3797    D_blocked = 0;
3798  evdeq(&D_blankerev);
3799  close(D_blankerev.fd);
3800  D_blankerev.fd = -1;
3801  Kill(D_blankerpid, SIGHUP);
3802  D_top = D_bot = -1;
3803  oldrend = D_rend;
3804  if (D_ME)
3805    {
3806      AddCStr(D_ME);
3807      AddCStr(D_ME);
3808    }
3809  else
3810    {
3811#ifdef COLOR
3812      if (D_hascolor)
3813	AddStr("\033[m\033[m");	/* why is D_ME not set? */
3814#endif
3815      AddCStr(D_SE);
3816      AddCStr(D_UE);
3817    }
3818  AddCStr(D_VE);
3819  AddCStr(D_CE0);
3820  D_rend = mchar_null;
3821  D_atyp = 0;
3822  D_curvis = 0;
3823  D_x = D_y = -1;
3824  ChangeScrollRegion(oldtop, oldbot);
3825  SetRendition(&oldrend);
3826  ClearAll();
3827}
3828
3829void
3830RunBlanker(cmdv)
3831char **cmdv;
3832{
3833  char *m;
3834  int pid;
3835  int slave = -1;
3836  char termname[30];
3837#ifndef TIOCSWINSZ
3838  char libuf[20], cobuf[20];
3839#endif
3840  char **np;
3841
3842  strcpy(termname, "TERM=");
3843  strncpy(termname + 5, D_termname, sizeof(termname) - 6);
3844  termname[sizeof(termname) - 1] = 0;
3845  KillBlanker();
3846  D_blankerpid = -1;
3847  if ((D_blankerev.fd = OpenPTY(&m)) == -1)
3848    {
3849      Msg(0, "OpenPty failed");
3850      return;
3851    }
3852#ifdef O_NOCTTY
3853  if (pty_preopen)
3854    {
3855      if ((slave = open(m, O_RDWR|O_NOCTTY)) == -1)
3856	{
3857	  Msg(errno, "%s", m);
3858	  close(D_blankerev.fd);
3859	  D_blankerev.fd = -1;
3860	  return;
3861	}
3862    }
3863#endif
3864  switch (pid = (int)fork())
3865    {
3866    case -1:
3867      Msg(errno, "fork");
3868      close(D_blankerev.fd);
3869      D_blankerev.fd = -1;
3870      return;
3871    case 0:
3872      displays = 0;
3873#ifdef DEBUG
3874      if (dfp && dfp != stderr)
3875        fclose(dfp);
3876#endif
3877      if (setgid(real_gid) || setuid(real_uid))
3878        Panic(errno, "setuid/setgid");
3879      brktty(D_userfd);
3880      freetty();
3881      close(0);
3882      close(1);
3883      close(2);
3884      closeallfiles(slave);
3885      if (open(m, O_RDWR))
3886	Panic(errno, "Cannot open %s", m);
3887      dup(0);
3888      dup(0);
3889      if (slave != -1)
3890	close(slave);
3891      InitPTY(0);
3892      fgtty(0);
3893      SetTTY(0, &D_OldMode);
3894      np = NewEnv + 3;
3895      *np++ = NewEnv[0];
3896      *np++ = termname;
3897#ifdef TIOCSWINSZ
3898      glwz.ws_col = D_width;
3899      glwz.ws_row = D_height;
3900      (void)ioctl(0, TIOCSWINSZ, (char *)&glwz);
3901#else
3902      sprintf(libuf, "LINES=%d", D_height);
3903      sprintf(libuf, "COLUMNS=%d", D_width);
3904      *np++ = libuf;
3905      *np++ = cobuf;
3906#endif
3907#ifdef SIGPIPE
3908      signal(SIGPIPE, SIG_DFL);
3909#endif
3910      display = 0;
3911      execvpe(*cmdv, cmdv, NewEnv + 3);
3912      Panic(errno, *cmdv);
3913    default:
3914      break;
3915    }
3916  D_blankerpid = pid;
3917  evenq(&D_blankerev);
3918  D_blocked = 4;
3919  ClearAll();
3920}
3921
3922#endif
3923