1 /* Copyright (c) 2008, 2009
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Micah Cowan (micah@cowan.name)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Copyright (c) 1987 Oliver Laumann
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3, or (at your option)
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program (see the file COPYING); if not, see
23 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 ****************************************************************
29 #include <sys/types.h>
33 # include <sys/ioctl.h>
42 static int CountChars
__P((int));
43 static int DoAddChar
__P((int));
44 static int BlankResize
__P((int, int));
45 static int CallRewrite
__P((int, int, int, int));
46 static void disp_readev_fn
__P((struct event
*, char *));
47 static void disp_writeev_fn
__P((struct event
*, char *));
49 static void disp_writeev_eagain
__P((struct event
*, char *));
51 static void disp_status_fn
__P((struct event
*, char *));
52 static void disp_hstatus_fn
__P((struct event
*, char *));
53 static void disp_blocked_fn
__P((struct event
*, char *));
54 static void cv_winid_fn
__P((struct event
*, char *));
56 static void disp_map_fn
__P((struct event
*, char *));
58 static void disp_idle_fn
__P((struct event
*, char *));
60 static void disp_blanker_fn
__P((struct event
*, char *));
62 static void WriteLP
__P((int, int));
63 static void INSERTCHAR
__P((int));
64 static void RAW_PUTCHAR
__P((int));
66 static void SetBackColor
__P((int));
68 static void RemoveStatusMinWait
__P((void));
71 extern struct layer
*flayer
;
72 extern struct win
*windows
, *fore
;
73 extern struct LayFuncs WinLf
;
75 extern int use_hardstatus
;
76 extern int MsgWait
, MsgMinWait
;
77 extern int Z0width
, Z1width
;
78 extern unsigned char *blank
, *null
;
79 extern struct mline mline_blank
, mline_null
, mline_old
;
80 extern struct mchar mchar_null
, mchar_blank
, mchar_so
;
81 extern struct NewWindow nwin_default
;
82 extern struct action idleaction
;
84 /* XXX shouldn't be here */
85 extern char *hstatusstring
;
86 extern char *captionstring
;
92 extern int pty_preopen
;
93 #if defined(TIOCSWINSZ) || defined(TIOCGWINSZ)
94 extern struct winsize glwz
;
97 extern int real_uid
, real_gid
;
101 * tputs needs this to calculate the padding
105 #endif /* NEED_OSPEED */
109 struct display
*display
, *displays
;
111 int attr2color
[8][4];
116 struct display TheDisplay
;
122 int defobuflimit
= OBUF_MAX
;
123 int defnonblock
= -1;
124 int defmousetrack
= 0;
129 int hardstatusemu
= HSTATUS_IGNORE
;
131 int focusminwidth
, focusminheight
;
134 * Default layer management
138 DefProcess(bufp
, lenp
)
147 DefRedisplayLine(y
, xs
, xe
, isblank
)
148 int y
, xs
, xe
, isblank
;
150 if (isblank
== 0 && y
>= 0)
151 DefClearLine(y
, xs
, xe
, 0);
155 DefClearLine(y
, xs
, xe
, bce
)
158 LClearLine(flayer
, y
, xs
, xe
, bce
, (struct mline
*)0);
163 DefRewrite(y
, xs
, xe
, rend
, doit
)
181 LAY_DISPLAYS(flayer
, InsertMode(0));
182 /* ChangeScrollRegion(0, D_height - 1); */
183 LKeypadMode(flayer
, 0);
184 LCursorkeysMode(flayer
, 0);
185 LCursorVisibility(flayer
, 0);
186 LMouseMode(flayer
, 0);
187 LSetRendition(flayer
, &mchar_null
);
188 LSetFlow(flayer
, nwin_default
.flowflag
& FLOW_NOW
);
192 * Blank layer management
195 struct LayFuncs BlankLf
=
211 flayer
->l_width
= wi
;
212 flayer
->l_height
= he
;
218 * Generate new display, start with a blank layer.
219 * The termcap arrays are not initialised here.
220 * The new display is placed in the displays list.
224 MakeDisplay(uname
, utty
, term
, fd
, pid
, Mode
)
225 char *uname
, *utty
, *term
;
230 struct baud_values
*b
;
232 if (!*(u
= FindUserPtr(uname
)) && UserAdd(uname
, (char *)0, u
))
233 return 0; /* could not find or add user */
236 if ((display
= (struct display
*)calloc(1, sizeof(*display
))) == 0)
241 bzero((char *)&TheDisplay
, sizeof(TheDisplay
));
242 display
= &TheDisplay
;
244 display
->d_next
= displays
;
247 D_nonblock
= defnonblock
;
249 D_readev
.fd
= D_writeev
.fd
= fd
;
250 D_readev
.type
= EV_READ
;
251 D_writeev
.type
= EV_WRITE
;
252 D_readev
.data
= D_writeev
.data
= (char *)display
;
253 D_readev
.handler
= disp_readev_fn
;
254 D_writeev
.handler
= disp_writeev_fn
;
256 D_writeev
.condpos
= &D_obuflen
;
257 D_writeev
.condneg
= &D_obuffree
;
259 D_statusev
.type
= EV_TIMEOUT
;
260 D_statusev
.data
= (char *)display
;
261 D_statusev
.handler
= disp_status_fn
;
262 D_hstatusev
.type
= EV_TIMEOUT
;
263 D_hstatusev
.data
= (char *)display
;
264 D_hstatusev
.handler
= disp_hstatus_fn
;
265 D_blockedev
.type
= EV_TIMEOUT
;
266 D_blockedev
.data
= (char *)display
;
267 D_blockedev
.handler
= disp_blocked_fn
;
268 D_blockedev
.condpos
= &D_obuffree
;
269 D_blockedev
.condneg
= &D_obuflenmax
;
270 D_hstatusev
.handler
= disp_hstatus_fn
;
272 D_mapev
.type
= EV_TIMEOUT
;
273 D_mapev
.data
= (char *)display
;
274 D_mapev
.handler
= disp_map_fn
;
276 D_idleev
.type
= EV_TIMEOUT
;
277 D_idleev
.data
= (char *)display
;
278 D_idleev
.handler
= disp_idle_fn
;
280 D_blankerev
.type
= EV_READ
;
281 D_blankerev
.data
= (char *)display
;
282 D_blankerev
.handler
= disp_blanker_fn
;
286 D_status_obuffree
= -1;
287 Resize_obuf(); /* Allocate memory for buffer */
288 D_obufmax
= defobuflimit
;
289 D_obuflenmax
= D_obuflen
- D_obufmax
;
291 D_auto_nuke
= defautonuke
;
298 if ((b
= lookup_baud((int)cfgetospeed(&D_OldMode
.tio
))))
302 if ((b
= lookup_baud(D_OldMode
.tio
.c_cflag
& CBAUD
)))
305 D_dospeed
= (short)D_OldMode
.m_ttyb
.sg_ospeed
;
308 debug1("New displays ospeed = %d\n", D_dospeed
);
310 strncpy(D_usertty
, utty
, sizeof(D_usertty
) - 1);
311 D_usertty
[sizeof(D_usertty
) - 1] = 0;
312 strncpy(D_termname
, term
, sizeof(D_termname
) - 1);
313 D_termname
[sizeof(D_termname
) - 1] = 0;
315 D_processinput
= ProcessInput
;
316 D_mousetrack
= defmousetrack
;
326 struct display
*d
, **dp
;
340 SetTTY(D_userfd
, &D_OldMode
);
341 fcntl(D_userfd
, F_SETFL
, 0);
347 if (D_processinputdata
)
348 free(D_processinputdata
);
349 D_processinputdata
= 0;
374 if (bd
.bd_dpy
== display
)
376 bd
.bd_start_braille
= 0;
382 for (dp
= &displays
; (d
= *dp
) ; dp
= &d
->d_next
)
386 if (D_status_lastmsg
)
387 free(D_status_lastmsg
);
390 *dp
= display
->d_next
;
392 ASSERT(display
== displays
);
393 ASSERT(display
== &TheDisplay
);
397 while (D_canvas
.c_slperp
)
398 FreeCanvas(D_canvas
.c_slperp
);
401 for (p
= windows
; p
; p
= p
->w_next
)
403 if (p
->w_pdisplay
== display
)
405 if (p
->w_lastdisp
== display
)
407 if (p
->w_readev
.condneg
== &D_status
|| p
->w_readev
.condneg
== &D_obuflenmax
)
408 p
->w_readev
.condpos
= p
->w_readev
.condneg
= 0;
411 for (p
= windows
; p
; p
= p
->w_next
)
412 if (p
->w_zdisplay
== display
)
421 free((char *)display
);
427 * if the adaptflag is on, we keep the size of this display, else
428 * we may try to restore our old window sizes.
439 /* Check for toggle */
440 if (D_IM
&& strcmp(D_IM
, D_EI
))
447 /* Check for toggle */
448 if (D_KS
&& strcmp(D_KS
, D_KE
))
450 if (D_CCS
&& strcmp(D_CCS
, D_CCE
))
461 ResizeDisplay(D_defwidth
, D_defheight
);
462 ChangeScrollRegion(0, D_height
- 1);
466 debug1("we %swant to adapt all our windows to the display\n",
467 (adapt
) ? "" : "don't ");
468 /* In case the size was changed by a init sequence */
469 CheckScreenSize((adapt
) ? 2 : 0);
481 ResizeDisplay(D_defwidth
, D_defheight
);
483 ChangeScrollRegion(0, D_height
- 1);
490 SetRendition(&mchar_null
);
497 ShowHStatus((char *)0);
502 GotoPos(0, D_height
- 1);
516 if (!D_insert
&& D_x
< D_width
- 1)
530 RefreshLine(D_y
, D_x
, D_width
-1, 0);
542 if (D_insert
&& D_x
< D_width
- 1)
551 if (D_x
< D_width
- 1)
558 if (D_CLP
|| D_y
!= D_bot
)
563 GotoPos(D_width
- 1, y
);
566 debug("PUTCHARLP: lp_missing!\n");
571 /* XXX -> PutChar ? */
575 D_lpchar
.image
= D_mbcs
;
583 * RAW_PUTCHAR() is for all text that will be displayed.
584 * NOTE: charset Nr. 0 has a conversion table, but c1, c2, ... don't.
595 if (D_encoding
== UTF8
)
597 c
= (c
& 255) | (unsigned char)D_rend
.font
<< 8;
603 D_x
+= D_AM
? 1 : -1;
606 else if (utf8_isdouble(c
))
615 AddCStr2(D_CS0
, '0');
625 if (is_dw_font(D_rend
.font
))
635 if (D_x
== D_width
- 1)
636 D_x
+= D_AM
? 1 : -1;
641 # if defined(ENCODINGS) && defined(DW_CHARS)
643 c
= PrepareEncodedChar(c
);
648 if (D_xtable
&& D_xtable
[(int)(unsigned char)D_rend
.font
] && D_xtable
[(int)(unsigned char)D_rend
.font
][(int)(unsigned char)c
])
649 AddStr(D_xtable
[(int)(unsigned char)D_rend
.font
][(int)(unsigned char)c
]);
651 AddChar(D_rend
.font
!= '0' ? c
: D_c0_tab
[(int)(unsigned char)c
]);
659 if (++D_x
>= D_width
)
663 else if (!D_CLP
|| D_x
> D_width
)
666 if (D_y
< D_height
-1 && D_y
!= D_bot
)
684 /* this is for ESC-sequences only (AddChar is a macro) */
693 if (display
&& s
&& *s
)
696 tputs(s
, 1, DoAddChar
);
705 if (display
&& s
&& *s
)
708 tputs(tgoto(s
, 0, c
), 1, DoAddChar
);
713 /* Insert mode is a toggle on some terminals, so we need this hack:
719 if (display
&& on
!= D_insert
&& D_IM
)
729 /* ...and maybe keypad application mode is a toggle, too:
739 if (display
&& D_keypad
!= on
&& D_KS
)
758 if (display
&& D_cursorkeys
!= on
&& D_CCS
)
773 if (display
&& D_revvid
!= on
&& D_CVR
)
787 if (display
&& D_curvis
!= v
)
790 AddCStr(D_VE
); /* do this always, just to be safe */
794 else if (v
== 1 && D_VS
)
809 if (mode
< D_mousetrack
)
819 sprintf(mousebuf
, "\033[?%dl", D_mouse
);
824 sprintf(mousebuf
, "\033[?%dh", mode
);
851 tputs(s
, 1, CountChars
);
859 CallRewrite(y
, xs
, xe
, doit
)
862 struct canvas
*cv
, *cvlist
, *cvlnext
;
864 struct layer
*oldflayer
;
867 debug3("CallRewrite %d %d %d\n", y
, xs
, xe
);
872 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
874 if (y
< cv
->c_ys
|| y
> cv
->c_ye
|| xe
< cv
->c_xs
|| xs
> cv
->c_xe
)
876 for (vp
= cv
->c_vplist
; vp
; vp
= vp
->v_next
)
877 if (y
>= vp
->v_ys
&& y
<= vp
->v_ye
&& xe
>= vp
->v_xs
&& xs
<= vp
->v_xe
)
885 flayer
= cv
->c_layer
;
886 cvlist
= flayer
->l_cvlist
;
887 cvlnext
= cv
->c_lnext
;
888 flayer
->l_cvlist
= cv
;
890 LayRewrite(y
- vp
->v_yoff
, xs
- vp
->v_xoff
, xe
- vp
->v_xoff
, &D_rend
, 1);
891 flayer
->l_cvlist
= cvlist
;
892 cv
->c_lnext
= cvlnext
;
896 if (cv
== 0 || cv
->c_layer
== 0)
897 return EXPENSIVE
; /* not found or nothing on it */
898 if (xs
< vp
->v_xs
|| xe
> vp
->v_xe
)
899 return EXPENSIVE
; /* crosses viewport boundaries */
900 if (y
- vp
->v_yoff
< 0 || y
- vp
->v_yoff
>= cv
->c_layer
->l_height
)
901 return EXPENSIVE
; /* line not on layer */
902 if (xs
- vp
->v_xoff
< 0 || xe
- vp
->v_xoff
>= cv
->c_layer
->l_width
)
903 return EXPENSIVE
; /* line not on layer */
905 if (D_encoding
== UTF8
)
909 flayer
= cv
->c_layer
;
910 debug3("Calling Rewrite %d %d %d\n", y
- vp
->v_yoff
, xs
- vp
->v_xoff
, xe
- vp
->v_xoff
);
911 cost
= LayRewrite(y
- vp
->v_yoff
, xs
- vp
->v_xoff
, xe
- vp
->v_xoff
, &D_rend
, 0);
914 cost
+= D_EIcost
+ D_IMcost
;
923 register int dy
, dx
, x1
, y1
;
924 register int costx
, costy
;
928 enum move_t xm
= M_NONE
, ym
= M_NONE
;
939 x1
= -1; /* don't know how the terminal treats this */
947 if (dy
== 0 && dx
== 0)
949 debug2("GotoPos (%d,%d)", x1
, y1
);
950 debug2(" -> (%d,%d)\n", x2
, y2
);
951 if (!D_MS
) /* Safe to move ? */
952 SetRendition(&mchar_null
);
953 if (y1
< 0 /* don't know the y position */
954 || (y2
> D_bot
&& y1
<= D_bot
) /* have to cross border */
955 || (y2
< D_top
&& y1
>= D_top
)) /* of scrollregion ? */
958 if (D_HO
&& !x2
&& !y2
)
961 AddCStr(tgoto(D_CM
, x2
, y2
));
967 /* some scrollregion implementations don't allow movements
968 * away from the region. sigh.
970 if ((y1
> D_bot
&& y2
> y1
) || (y1
< D_top
&& y2
< y1
))
973 /* Calculate CMcost */
974 if (D_HO
&& !x2
&& !y2
)
977 s
= tgoto(D_CM
, x2
, y2
);
978 CMcost
= CalcCost(s
);
980 /* Calculate the cost to move the cursor to the right x position */
982 if (x1
>= 0) /* relativ x positioning only if we know where we are */
986 if (D_CRI
&& (dx
> 1 || !D_ND
))
988 costx
= CalcCost(tgoto(D_CRI
, 0, dx
));
991 if ((m
= D_NDcost
* dx
) < costx
)
996 /* Speedup: dx <= LayRewrite() */
997 if (dx
< costx
&& (m
= CallRewrite(y1
, x1
, x2
- 1, 0)) < costx
)
1005 if (D_CLE
&& (dx
< -1 || !D_BC
))
1007 costx
= CalcCost(tgoto(D_CLE
, 0, -dx
));
1010 if ((m
= -dx
* D_LEcost
) < costx
)
1019 /* Speedup: LayRewrite() >= x2 */
1020 if (x2
+ D_CRcost
< costx
&& (m
= (x2
? CallRewrite(y1
, 0, x2
- 1, 0) : 0) + D_CRcost
) < costx
)
1026 /* Check if it is already cheaper to do CM */
1027 if (costx
>= CMcost
)
1030 /* Calculate the cost to move the cursor to the right y position */
1034 if (D_CDO
&& dy
> 1) /* DO & NL are always != 0 */
1036 costy
= CalcCost(tgoto(D_CDO
, 0, dy
));
1039 if ((m
= dy
* ((x2
== 0) ? D_NLcost
: D_DOcost
)) < costy
)
1047 if (D_CUP
&& (dy
< -1 || !D_UP
))
1049 costy
= CalcCost(tgoto(D_CUP
, 0, -dy
));
1052 if ((m
= -dy
* D_UPcost
) < costy
)
1061 /* Finally check if it is cheaper to do CM */
1062 if (costx
+ costy
>= CMcost
)
1072 AddCStr2(D_CLE
, -dx
);
1079 AddCStr2(D_CRI
, dx
);
1088 (void) CallRewrite(y1
, x1
, x2
- 1, 1);
1101 AddCStr2(D_CUP
, -dy
);
1104 s
= (x2
== 0) ? D_NL
: D_DO
;
1109 AddCStr2(D_CDO
, dy
);
1122 ClearArea(0, 0, 0, D_width
- 1, D_width
- 1, D_height
- 1, 0, 0);
1126 ClearArea(x1
, y1
, xs
, xe
, x2
, y2
, bce
, uselayfn
)
1127 int x1
, y1
, xs
, xe
, x2
, y2
, bce
, uselayfn
;
1131 struct viewport
*vp
;
1133 debug2("Clear %d,%d", x1
, y1
);
1134 debug2(" %d-%d", xs
, xe
);
1135 debug2(" %d,%d", x2
, y2
);
1136 debug2(" uselayfn=%d bce=%d\n", uselayfn
, bce
);
1146 if (D_UT
) /* Safe to erase ? */
1147 SetRendition(&mchar_null
);
1152 if (D_lp_missing
&& y1
<= D_bot
&& xe
>= D_width
- 1)
1154 if (y2
> D_bot
|| (y2
== D_bot
&& x2
>= D_width
- 1))
1157 if (x2
== D_width
- 1 && (xs
== 0 || y1
== y2
) && xe
== D_width
- 1 && y2
== D_height
- 1 && (!bce
|| D_BE
))
1160 if (x1
== 0 && y1
== 0 && D_auto_nuke
)
1163 if (x1
== 0 && y1
== 0 && D_CL
)
1170 * Workaround a hp700/22 terminal bug. Do not use CD where CE
1171 * is also appropriate.
1173 if (D_CD
&& (y1
< y2
|| !D_CE
))
1180 if (x1
== 0 && xs
== 0 && (xe
== D_width
- 1 || y1
== y2
) && y1
== 0 && D_CCD
&& (!bce
|| D_BE
))
1187 for (y
= y1
; y
<= y2
; y
++, x1
= xs
)
1191 if (x1
== 0 && D_CB
&& (xxe
!= D_width
- 1 || (D_x
== xxe
&& D_y
== y
)) && (!bce
|| D_BE
))
1197 if (xxe
== D_width
- 1 && D_CE
&& (!bce
|| D_BE
))
1206 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
1208 if (y
< cv
->c_ys
|| y
> cv
->c_ye
|| xxe
< cv
->c_xs
|| x1
> cv
->c_xe
)
1210 for (vp
= cv
->c_vplist
; vp
; vp
= vp
->v_next
)
1211 if (y
>= vp
->v_ys
&& y
<= vp
->v_ye
&& xxe
>= vp
->v_xs
&& x1
<= vp
->v_xe
)
1216 if (cv
&& cv
->c_layer
&& x1
>= vp
->v_xs
&& xxe
<= vp
->v_xe
&&
1217 y
- vp
->v_yoff
>= 0 && y
- vp
->v_yoff
< cv
->c_layer
->l_height
&&
1218 xxe
- vp
->v_xoff
>= 0 && x1
- vp
->v_xoff
< cv
->c_layer
->l_width
)
1220 struct layer
*oldflayer
= flayer
;
1221 struct canvas
*cvlist
, *cvlnext
;
1222 flayer
= cv
->c_layer
;
1223 cvlist
= flayer
->l_cvlist
;
1224 cvlnext
= cv
->c_lnext
;
1225 flayer
->l_cvlist
= cv
;
1227 LayClearLine(y
- vp
->v_yoff
, x1
- vp
->v_xoff
, xxe
- vp
->v_xoff
, bce
);
1228 flayer
->l_cvlist
= cvlist
;
1229 cv
->c_lnext
= cvlnext
;
1234 ClearLine((struct mline
*)0, y
, x1
, xxe
, bce
);
1240 * if cur_only > 0, we only redisplay current line, as a full refresh is
1241 * too expensive over a low baud line.
1249 /* XXX do em all? */
1251 ChangeScrollRegion(0, D_height
- 1);
1254 CursorVisibility(0);
1256 SetRendition(&mchar_null
);
1263 if (cur_only
> 0 && D_fore
)
1264 RefreshArea(0, D_fore
->w_y
, D_width
- 1, D_fore
->w_y
, 1);
1268 CV_CALL(D_forecv
, LayRestore();LaySetCursor());
1272 RedisplayDisplays(cur_only
)
1275 struct display
*olddisplay
= display
;
1276 for (display
= displays
; display
; display
= display
->d_next
)
1277 Redisplay(cur_only
);
1278 display
= olddisplay
;
1284 ScrollH(y
, xs
, xe
, n
, bce
, oml
)
1285 int y
, xs
, xe
, n
, bce
;
1292 if (xe
!= D_width
- 1)
1294 RefreshLine(y
, xs
, xe
, 0);
1295 /* UpdateLine(oml, y, xs, xe); */
1300 SetRendition(&mchar_null
);
1307 if (n
>= xe
- xs
+ 1)
1309 if (D_CDC
&& !(n
== 1 && D_DC
))
1318 RefreshLine(y
, xs
, xe
, 0);
1319 /* UpdateLine(oml, y, xs, xe); */
1325 if (-n
>= xe
- xs
+ 1)
1329 if (D_CIC
&& !(n
== -1 && D_IC
))
1330 AddCStr2(D_CIC
, -n
);
1339 SetRendition(&mchar_null
);
1345 bce
= 0; /* all done */
1349 /* UpdateLine(oml, y, xs, xe); */
1350 RefreshLine(y
, xs
, xe
, 0);
1356 SetRendition(&mchar_null
);
1362 bce
= 0; /* all done */
1368 ClearLine((struct mline
*)0, y
, xe
- n
+ 1, xe
, bce
);
1370 ClearLine((struct mline
*)0, y
, xs
, xs
- n
- 1, bce
);
1372 if (D_lp_missing
&& y
== D_bot
)
1375 WriteLP(D_width
- 1 - n
, y
);
1381 ScrollV(xs
, ys
, xe
, ye
, n
, bce
)
1382 int xs
, ys
, xe
, ye
, n
, bce
;
1387 int alok
, dlok
, aldlfaster
;
1393 if (n
>= ye
- ys
+ 1 || -n
>= ye
- ys
+ 1)
1395 ClearArea(xs
, ys
, xs
, xe
, xe
, ye
, bce
, 0);
1398 if (xs
> D_vpxmin
|| xe
< D_vpxmax
)
1400 RefreshArea(xs
, ys
, xe
, ye
, 0);
1406 if (D_bot
> ye
|| D_bot
< ys
)
1411 if (missy
> ye
|| missy
< ys
)
1422 if (n
>= ye
- ys
+ 1)
1427 if (ys
< D_top
|| D_bot
!= ye
)
1428 ChangeScrollRegion(ys
, ye
);
1429 alok
= (D_AL
|| D_CAL
|| (ys
>= D_top
&& ye
== D_bot
&& up
));
1430 dlok
= (D_DL
|| D_CDL
|| (ys
>= D_top
&& ye
== D_bot
&& !up
));
1431 if (D_top
!= ys
&& !(alok
&& dlok
))
1432 ChangeScrollRegion(ys
, ye
);
1436 (oldbot
== D_bot
&& up
&& D_top
== ys
&& D_bot
== ye
)))
1438 WriteLP(D_width
- 1, oldbot
);
1439 if (oldbot
== D_bot
) /* have scrolled */
1444 ChangeScrollRegion(oldtop, oldbot);
1447 ClearLine((struct mline
*)0, ye
, xs
, xe
, bce
);
1454 SetRendition(&mchar_null
);
1460 aldlfaster
= (n
> 1 && ys
>= D_top
&& ye
== D_bot
&& ((up
&& D_CDL
) || (!up
&& D_CAL
)));
1462 if ((up
|| D_SR
) && D_top
== ys
&& D_bot
== ye
&& !aldlfaster
)
1467 for(i
= n
; i
-- > 0; )
1468 AddCStr(D_NL
); /* was SF, I think NL is faster */
1473 for(i
= n
; i
-- > 0; )
1477 else if (alok
&& dlok
)
1479 if (up
|| ye
!= D_bot
)
1481 GotoPos(0, up
? ys
: ye
+1-n
);
1482 if (D_CDL
&& !(n
== 1 && D_DL
))
1488 if (!up
|| ye
!= D_bot
)
1490 GotoPos(0, up
? ye
+1-n
: ys
);
1491 if (D_CAL
&& !(n
== 1 && D_AL
))
1500 RefreshArea(xs
, ys
, xe
, ye
, 0);
1506 ClearArea(xs
, ye
- n
+ 1, xs
, xe
, xe
, ye
, bce
, 0);
1508 ClearArea(xs
, ys
, xs
, xe
, xe
, ys
+ n
- 1, bce
, 0);
1510 if (D_lp_missing
&& missy
!= D_bot
)
1511 WriteLP(D_width
- 1, missy
);
1513 ChangeScrollRegion(oldtop, oldbot);
1514 if (D_lp_missing && missy != D_bot)
1515 WriteLP(D_width - 1, missy);
1523 register int i
, j
, old
, typ
;
1525 if (!display
|| (old
= D_rend
.attr
) == new)
1528 D_col16change
= (old
^ new) & (A_BFG
| A_BBG
);
1529 new ^= D_col16change
;
1533 #if defined(TERMINFO) && defined(USE_SGR)
1539 tputs(tparm(D_SA
, new & A_SO
, new & A_US
, new & A_RV
, new & A_BL
,
1540 new & A_DI
, new & A_BD
, 0 , 0 ,
1546 rend_setdefault(&D_rend
);
1553 if ((new & old
) != old
)
1563 /* ansi attrib handling: \E[m resets color, too */
1565 rend_setdefault(&D_rend
);
1570 /* D_ME may also reset the alternate charset */
1582 for (i
= 0, j
= 1; old
&& i
< NATTR
; i
++, j
<<= 1)
1589 AddCStr(D_attrtab
[i
]);
1590 typ
|= D_attrtyp
[i
];
1601 int old
= D_rend
.font
;
1602 if (!display
|| old
== new)
1606 if (D_encoding
&& CanEncodeFont(D_encoding
, new))
1608 if (new == D_realfont
)
1612 if (D_xtable
&& D_xtable
[(int)(unsigned char)new] &&
1613 D_xtable
[(int)(unsigned char)new][256])
1615 AddCStr(D_xtable
[(int)(unsigned char)new][256]);
1619 if (!D_CG0
&& new != '0')
1638 AddCStr2(D_CS0
, new);
1653 jj
= (jj
- 232) / 6;
1654 jj
= (jj
& 1) << 3 | (jj
& 2 ? 7 : 0);
1662 min
= r
< g
? (r
< b
? r
: b
) : (g
< b
? g
: b
);
1663 max
= r
> g
? (r
> b
? r
: b
) : (g
> b
? g
: b
);
1665 jj
= ((max
+ 1) & 2) << 2 | ((max
+ 1) & 4 ? 7 : 0);
1667 jj
= (b
- min
) / (max
- min
) << 2 | (g
- min
) / (max
- min
) << 1 | (r
-
1668 min
) / (max
- min
) | (max
> 3 ? 8 : 0);
1681 return (jj
- 232) / 3 + 80;
1688 return ((r
+ 1) / 2) * 16 + ((g
+ 1) / 2) * 4 + ((b
+ 1) / 2) + 16;
1699 static unsigned char sftrans
[8] = {0,4,2,6,1,5,3,7};
1704 of
= rend_getfg(&D_rend
);
1705 ob
= rend_getbg(&D_rend
);
1708 /* intense default not invented yet */
1714 debug2("SetColor %d %d", coli2e(of
), coli2e(ob
));
1715 debug2(" -> %d %d\n", coli2e(f
), coli2e(b
));
1716 debug2("(%d %d", of
, ob
);
1717 debug2(" -> %d %d)\n", f
, b
);
1719 if (!D_CAX
&& D_hascolor
&& ((f
== 0 && f
!= of
) || (b
== 0 && b
!= ob
)))
1726 oattr
= D_rend
.attr
;
1727 AddCStr(D_ME
? D_ME
: "\033[m");
1731 /* D_ME may also reset the alternate charset */
1744 rend_setfg(&D_rend
, f
);
1745 rend_setbg(&D_rend
, b
);
1751 f
= f
? coli2e(f
) : -1;
1752 b
= b
? coli2e(b
) : -1;
1753 of
= of
? coli2e(of
) : -1;
1754 ob
= ob
? coli2e(ob
) : -1;
1756 if (f
!= of
&& f
> 15 && D_CCO
!= 256)
1757 f
= D_CCO
== 88 && D_CAF
? color256to88(f
) : color256to16(f
);
1758 if (f
!= of
&& f
> 15 && D_CAF
)
1763 if (b
!= ob
&& b
> 15 && D_CCO
!= 256)
1764 b
= D_CCO
== 88 && D_CAB
? color256to88(b
) : color256to16(b
);
1765 if (b
!= ob
&& b
> 15 && D_CAB
)
1771 if (f
!= of
&& f
!= (of
| 8))
1774 AddCStr("\033[39m"); /* works because AX is set */
1776 AddCStr2(D_CAF
, f
& 7);
1778 AddCStr2(D_CSF
, sftrans
[f
& 7]);
1780 if (b
!= ob
&& b
!= (ob
| 8))
1783 AddCStr("\033[49m"); /* works because AX is set */
1785 AddCStr2(D_CAB
, b
& 7);
1787 AddCStr2(D_CSB
, sftrans
[b
& 7]);
1790 if (f
!= of
&& D_CXT
&& (f
& 8) != 0 && f
!= -1)
1793 AddCStr2("\033[9%p1%dm", f
& 7);
1795 AddCStr2("\033[9%dm", f
& 7);
1798 if (b
!= ob
&& D_CXT
&& (b
& 8) != 0 && b
!= -1)
1801 AddCStr2("\033[10%p1%dm", b
& 7);
1803 AddCStr2("\033[10%dm", b
& 7);
1815 SetColor(rend_getfg(&D_rend
), new);
1826 if (nattr2color
&& D_hascolor
&& (mc
->attr
& nattr2color
) != 0)
1828 static struct mchar mmc
;
1831 for (i
= 0; i
< 8; i
++)
1832 if (attr2color
[i
] && (mc
->attr
& (1 << i
)) != 0)
1834 if (mc
->color
== 0 && attr2color
[i
][3])
1835 ApplyAttrColor(attr2color
[i
][3], &mmc
);
1836 else if ((mc
->color
& 0x0f) == 0 && attr2color
[i
][2])
1837 ApplyAttrColor(attr2color
[i
][2], &mmc
);
1838 else if ((mc
->color
& 0xf0) == 0 && attr2color
[i
][1])
1839 ApplyAttrColor(attr2color
[i
][1], &mmc
);
1841 ApplyAttrColor(attr2color
[i
][0], &mmc
);
1844 debug2("SetRendition: mapped to %02x %02x\n", (unsigned char)mc
->attr
, 0x99 - (unsigned char)mc
->color
);
1847 if (D_hascolor
&& D_CC8
&& (mc
->attr
& (A_BFG
|A_BBG
)))
1850 if ((mc
->attr
& A_BFG
) && D_MD
)
1852 if ((mc
->attr
& A_BBG
) && D_MB
)
1854 if (D_rend
.attr
!= a
)
1858 # endif /* COLORS16 */
1860 if (D_rend
.attr
!= mc
->attr
)
1864 if (D_rend
.color
!= mc
->color
1866 || D_rend
.colorx
!= mc
->colorx
1872 SetColor(rend_getfg(mc
), rend_getbg(mc
));
1875 if (D_rend
.font
!= mc
->font
)
1881 SetRenditionMline(ml
, x
)
1888 if (nattr2color
&& D_hascolor
&& (ml
->attr
[x
] & nattr2color
) != 0)
1891 copy_mline2mchar(&mc
, ml
, x
);
1896 if (D_hascolor
&& D_CC8
&& (ml
->attr
[x
] & (A_BFG
|A_BBG
)))
1898 int a
= ml
->attr
[x
];
1899 if ((ml
->attr
[x
] & A_BFG
) && D_MD
)
1901 if ((ml
->attr
[x
] & A_BBG
) && D_MB
)
1903 if (D_rend
.attr
!= a
)
1907 # endif /* COLORS16 */
1909 if (D_rend
.attr
!= ml
->attr
[x
])
1910 SetAttr(ml
->attr
[x
]);
1912 if (D_rend
.color
!= ml
->color
[x
]
1914 || D_rend
.colorx
!= ml
->colorx
[x
]
1922 copy_mline2mchar(&mc
, ml
, x
);
1923 SetColor(rend_getfg(&mc
), rend_getbg(&mc
));
1927 if (D_rend
.font
!= ml
->font
[x
])
1928 SetFont(ml
->font
[x
]);
1936 register char *s
, *t
;
1946 debug("tc not inited, just writing msg\n");
1947 if (D_processinputdata
)
1948 return; /* XXX: better */
1954 if (!use_hardstatus
|| !D_HS
)
1961 max
= D_WS
> 0 ? D_WS
: (D_width
- !D_CLP
);
1965 if (strcmp(msg
, D_status_lastmsg
) == 0)
1967 debug("same message - increase timeout");
1968 if (!D_status_obufpos
)
1969 SetTimeout(&D_statusev
, MsgWait
);
1972 RemoveStatusMinWait();
1974 for (s
= t
= msg
; *s
&& t
- msg
< max
; ++s
)
1977 else if ((unsigned char)*s
>= ' ' && *s
!= 0177)
1982 if (t
- msg
>= D_status_buflen
)
1985 if (D_status_lastmsg
)
1986 buf
= realloc(D_status_lastmsg
, t
- msg
+ 1);
1988 buf
= malloc(t
- msg
+ 1);
1991 D_status_lastmsg
= buf
;
1992 D_status_buflen
= t
- msg
+ 1;
1995 if (t
- msg
< D_status_buflen
)
1996 strcpy(D_status_lastmsg
, msg
);
1997 D_status_len
= t
- msg
;
1998 D_status_lastx
= D_x
;
1999 D_status_lasty
= D_y
;
2000 if (!use_hardstatus
|| D_has_hstatus
== HSTATUS_IGNORE
|| D_has_hstatus
== HSTATUS_MESSAGE
)
2002 D_status
= STATUS_ON_WIN
;
2003 debug1("using STATLINE %d\n", STATLINE
);
2004 GotoPos(0, STATLINE
);
2005 SetRendition(&mchar_so
);
2008 if (D_status_len
< max
)
2010 /* Wayne Davison: add extra space for readability */
2012 SetRendition(&mchar_null
);
2014 if (D_status_len
< max
)
2026 D_status
= STATUS_ON_HS
;
2030 D_status_obufpos
= D_obufp
- D_obuf
;
2031 ASSERT(D_status_obufpos
> 0);
2033 if (D_status
== STATUS_ON_WIN
)
2035 struct display
*olddisplay
= display
;
2036 struct layer
*oldflayer
= flayer
;
2038 /* this is copied over from RemoveStatus() */
2040 GotoPos(0, STATLINE
);
2041 RefreshLine(STATLINE
, 0, D_status_len
- 1, 0);
2042 GotoPos(D_status_lastx
, D_status_lasty
);
2043 flayer
= D_forecv
? D_forecv
->c_layer
: 0;
2046 display
= olddisplay
;
2048 D_status
= STATUS_ON_WIN
;
2055 struct display
*olddisplay
;
2056 struct layer
*oldflayer
;
2061 if (!(where
= D_status
))
2064 debug("RemoveStatus\n");
2065 if (D_status_obuffree
>= 0)
2067 D_obuflen
= D_status_obuflen
;
2068 D_obuffree
= D_status_obuffree
;
2069 D_status_obuffree
= -1;
2072 D_status_obufpos
= 0;
2075 olddisplay
= display
;
2077 if (where
== STATUS_ON_WIN
)
2079 if (captionalways
|| (D_canvas
.c_slperp
&& D_canvas
.c_slperp
->c_slnext
))
2081 GotoPos(0, STATLINE
);
2082 RefreshLine(STATLINE
, 0, D_status_len
- 1, 0);
2083 GotoPos(D_status_lastx
, D_status_lasty
);
2088 flayer
= D_forecv
? D_forecv
->c_layer
: 0;
2091 display
= olddisplay
;
2095 /* Remove the status but make sure that it is seen for MsgMinWait ms */
2097 RemoveStatusMinWait()
2099 /* XXX: should flush output first if D_status_obufpos is set */
2100 if (!D_status_bell
&& !D_status_obufpos
)
2104 gettimeofday(&now
, NULL
);
2105 ti
= (now
.tv_sec
- D_status_time
.tv_sec
) * 1000 + (now
.tv_usec
- D_status_time
.tv_usec
) / 1000;
2106 if (ti
< MsgMinWait
)
2107 DisplaySleep1000(MsgMinWait
- ti
, 0);
2112 /* refresh the display's hstatus line */
2119 if (D_status
== STATUS_ON_WIN
&& D_has_hstatus
== HSTATUS_LASTLINE
&& STATLINE
== D_height
-1)
2120 return; /* sorry, in use */
2124 if (D_HS
&& D_has_hstatus
== HSTATUS_HS
)
2126 if (!D_hstatus
&& (str
== 0 || *str
== 0))
2128 debug("ShowHStatus: using HS\n");
2129 SetRendition(&mchar_null
);
2134 if (str
== 0 || *str
== 0)
2137 max
= D_WS
> 0 ? D_WS
: (D_width
- !D_CLP
);
2138 if ((int)strlen(str
) > max
)
2145 else if (D_has_hstatus
== HSTATUS_LASTLINE
)
2147 debug("ShowHStatus: using last line\n");
2150 str
= str
? str
: "";
2154 GotoPos(0, D_height
- 1);
2155 SetRendition(captionalways
|| D_cvlist
== 0 || D_cvlist
->c_next
? &mchar_null
: &mchar_so
);
2156 PutWinMsg(str
, 0, l
);
2157 if (!captionalways
&& D_cvlist
&& !D_cvlist
->c_next
)
2158 while (l
++ < D_width
)
2161 ClearArea(l
, D_height
- 1, l
, D_width
- 1, D_width
- 1, D_height
- 1, 0, 0);
2162 if (ox
!= -1 && oy
!= -1)
2164 D_hstatus
= *str
? 1 : 0;
2165 SetRendition(&mchar_null
);
2167 else if (str
&& *str
&& D_has_hstatus
== HSTATUS_MESSAGE
)
2169 debug("ShowHStatus: using message\n");
2176 * Refreshes the harstatus of the fore window. Shouldn't be here...
2183 evdeq(&D_hstatusev
);
2184 if (D_status
== STATUS_ON_HS
)
2186 buf
= MakeWinMsgEv(hstatusstring
, D_fore
, '%', (D_HS
&& D_has_hstatus
== HSTATUS_HS
&& D_WS
> 0) ? D_WS
: D_width
- !D_CLP
, &D_hstatusev
, 0);
2190 if (D_has_hstatus
!= HSTATUS_IGNORE
&& D_hstatusev
.timeout
.tv_sec
)
2191 evenq(&D_hstatusev
);
2194 ShowHStatus((char *)0);
2197 /*********************************************************************/
2199 * Here come the routines that refresh an arbitrary part of the screen.
2209 debug("Signalling full refresh!\n");
2210 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
)
2212 CV_CALL(cv
, LayRedisplayLine(-1, -1, -1, isblank
));
2213 display
= cv
->c_display
; /* just in case! */
2215 RefreshArea(0, 0, D_width
- 1, D_height
- 1, isblank
);
2219 RefreshArea(xs
, ys
, xe
, ye
, isblank
)
2220 int xs
, ys
, xe
, ye
, isblank
;
2224 debug2("Refresh Area: %d,%d", xs
, ys
);
2225 debug3(" - %d,%d (isblank=%d)\n", xe
, ye
, isblank
);
2226 if (!isblank
&& xs
== 0 && xe
== D_width
- 1 && ye
== D_height
- 1 && (ys
== 0 || D_CD
))
2228 ClearArea(xs
, ys
, xs
, xe
, xe
, ye
, 0, 0);
2231 for (y
= ys
; y
<= ye
; y
++)
2232 RefreshLine(y
, xs
, xe
, isblank
);
2236 RefreshLine(y
, from
, to
, isblank
)
2237 int y
, from
, to
, isblank
;
2239 struct viewport
*vp
, *lvp
;
2240 struct canvas
*cv
, *lcv
, *cvlist
, *cvlnext
;
2241 struct layer
*oldflayer
;
2248 debug2("RefreshLine %d %d", y
, from
);
2249 debug2(" %d %d\n", to
, isblank
);
2251 if (D_status
== STATUS_ON_WIN
&& y
== STATLINE
)
2253 if (to
>= D_status_len
)
2254 D_status_len
= to
+ 1;
2255 return; /* can't refresh status */
2258 /* The following check makes plenty of sense. Unfortunately,
2259 vte-based terminals (notably gnome-terminal) experience a quirk
2260 that causes the final line not to update properly when it falls outside
2261 the scroll region; clearing the line with D_CE avoids the glitch,
2262 so we'll disable this perfectly sensible shortcut until such a time
2263 as widespread vte installations lack the glitch.
2265 See http://bugzilla.gnome.org/show_bug.cgi?id=542087 for current
2266 status of the VTE bug report, and
2267 https://savannah.gnu.org/bugs/index.php?23699 for the history from
2268 the Savannah BTS. */
2270 if (y
== D_height
- 1 && D_has_hstatus
== HSTATUS_LASTLINE
)
2277 if (isblank
== 0 && D_CE
&& to
== D_width
- 1 && from
< to
)
2281 SetRendition(&mchar_null
);
2289 for (cv
= display
->d_cvlist
; cv
; cv
= cv
->c_next
)
2291 if (y
== cv
->c_ye
+ 1 && from
>= cv
->c_xs
&& from
<= cv
->c_xe
)
2293 p
= Layer2Window(cv
->c_layer
);
2294 buf
= MakeWinMsgEv(captionstring
, p
, '%', cv
->c_xe
- cv
->c_xs
+ (cv
->c_xe
+ 1 < D_width
|| D_CLP
), &cv
->c_captev
, 0);
2295 if (cv
->c_captev
.timeout
.tv_sec
)
2296 evenq(&cv
->c_captev
);
2297 xx
= to
> cv
->c_xe
? cv
->c_xe
: to
;
2300 SetRendition(&mchar_so
);
2301 if (l
> xx
- cv
->c_xs
+ 1)
2302 l
= xx
- cv
->c_xs
+ 1;
2303 PutWinMsg(buf
, from
- cv
->c_xs
, l
);
2304 from
= cv
->c_xs
+ l
;
2305 for (; from
<= xx
; from
++)
2309 if (from
== cv
->c_xe
+ 1 && y
>= cv
->c_ys
&& y
<= cv
->c_ye
+ 1)
2312 SetRendition(&mchar_so
);
2317 if (y
< cv
->c_ys
|| y
> cv
->c_ye
|| to
< cv
->c_xs
|| from
> cv
->c_xe
)
2319 debug2("- canvas hit: %d %d", cv
->c_xs
, cv
->c_ys
);
2320 debug2(" %d %d\n", cv
->c_xe
, cv
->c_ye
);
2321 for (vp
= cv
->c_vplist
; vp
; vp
= vp
->v_next
)
2323 debug2(" - vp: %d %d", vp
->v_xs
, vp
->v_ys
);
2324 debug2(" %d %d\n", vp
->v_xe
, vp
->v_ye
);
2325 /* find leftmost overlapping vp */
2326 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
))
2334 continue; /* we advanced from */
2337 if (from
< lvp
->v_xs
)
2340 DisplayLine(&mline_null
, &mline_blank
, y
, from
, lvp
->v_xs
- 1);
2344 /* call LayRedisplayLine on canvas lcv viewport lvp */
2345 yy
= y
- lvp
->v_yoff
;
2346 xx
= to
< lvp
->v_xe
? to
: lvp
->v_xe
;
2348 if (lcv
->c_layer
&& yy
== lcv
->c_layer
->l_height
)
2351 SetRendition(&mchar_blank
);
2352 while (from
<= lvp
->v_xe
&& from
- lvp
->v_xoff
< lcv
->c_layer
->l_width
)
2357 if (from
>= lvp
->v_xe
+ 1)
2360 if (lcv
->c_layer
== 0 || yy
>= lcv
->c_layer
->l_height
|| from
- lvp
->v_xoff
>= lcv
->c_layer
->l_width
)
2363 DisplayLine(&mline_null
, &mline_blank
, y
, from
, lvp
->v_xe
);
2364 from
= lvp
->v_xe
+ 1;
2368 if (xx
- lvp
->v_xoff
>= lcv
->c_layer
->l_width
)
2369 xx
= lcv
->c_layer
->l_width
+ lvp
->v_xoff
- 1;
2371 flayer
= lcv
->c_layer
;
2372 cvlist
= flayer
->l_cvlist
;
2373 cvlnext
= lcv
->c_lnext
;
2374 flayer
->l_cvlist
= lcv
;
2376 LayRedisplayLine(yy
, from
- lvp
->v_xoff
, xx
- lvp
->v_xoff
, isblank
);
2377 flayer
->l_cvlist
= cvlist
;
2378 lcv
->c_lnext
= cvlnext
;
2383 if (!isblank
&& from
<= to
)
2384 DisplayLine(&mline_null
, &mline_blank
, y
, from
, to
);
2387 /*********************************************************************/
2389 /* clear lp_missing by writing the char on the screen. The
2390 * position must be safe.
2396 struct mchar oldrend
;
2399 ASSERT(D_lp_missing
);
2401 debug2("WriteLP(%d,%d)\n", x2
, y2
);
2408 D_lpchar
= mchar_blank
;
2411 /* Can't use PutChar */
2413 SetRendition(&D_lpchar
);
2414 PUTCHAR(D_lpchar
.image
);
2417 PUTCHAR(D_lpchar
.mbcs
);
2420 SetRendition(&oldrend
);
2424 ClearLine(oml
, y
, from
, to
, bce
)
2426 int from
, to
, y
, bce
;
2430 struct mchar bcechar
;
2433 debug3("ClearLine %d,%d-%d\n", y
, from
, to
);
2434 if (D_UT
) /* Safe to erase ? */
2435 SetRendition(&mchar_null
);
2440 if (from
== 0 && D_CB
&& (to
!= D_width
- 1 || (D_x
== to
&& D_y
== y
)) && (!bce
|| D_BE
))
2446 if (to
== D_width
- 1 && D_CE
&& (!bce
|| D_BE
))
2457 DisplayLine(oml
, &mline_blank
, y
, from
, to
);
2460 bcechar
= mchar_blank
;
2461 rend_setbg(&bcechar
, bce
);
2462 for (x
= from
; x
<= to
; x
++)
2463 copy_mchar2mline(&bcechar
, &mline_old
, x
);
2464 DisplayLine(oml
, &mline_old
, y
, from
, to
);
2466 DisplayLine(oml
, &mline_blank
, y
, from
, to
);
2471 DisplayLine(oml
, ml
, y
, from
, to
)
2472 struct mline
*oml
, *ml
;
2476 int last2flag
= 0, delete_lp
= 0;
2479 ASSERT(y
>= 0 && y
< D_height
);
2480 ASSERT(from
>= 0 && from
< D_width
);
2481 ASSERT(to
>= 0 && to
< D_width
);
2482 if (!D_CLP
&& y
== D_bot
&& to
== D_width
- 1)
2484 if (D_lp_missing
|| !cmp_mline(oml
, ml
, to
))
2487 if ((D_IC
|| D_IM
) && from
< to
&& !dw_left(ml
, to
, D_encoding
))
2489 if ((D_IC
|| D_IM
) && from
< to
)
2498 delete_lp
= !cmp_mchar_mline(&mchar_blank
, oml
, to
) && (D_CE
|| D_DC
|| D_CDC
);
2499 D_lp_missing
= !cmp_mchar_mline(&mchar_blank
, ml
, to
);
2500 copy_mline2mchar(&D_lpchar
, ml
, to
);
2508 /* finish dw-char (can happen after a wrap) */
2509 debug("DisplayLine finishing kanji\n");
2510 SetRenditionMline(ml
, from
);
2511 PUTCHAR(ml
->image
[from
]);
2515 for (x
= from
; x
<= to
; x
++)
2517 #if 0 /* no longer needed */
2518 if (x
|| D_x
!= D_width
|| D_y
!= y
- 1)
2521 if (x
< to
|| x
!= D_width
- 1 || ml
->image
[x
+ 1])
2522 if (cmp_mline(oml
, ml
, x
))
2527 if (dw_right(ml
, x
, D_encoding
))
2530 debug1("DisplayLine on right side of dw char- x now %d\n", x
);
2533 if (x
== to
&& dw_left(ml
, x
, D_encoding
))
2534 break; /* don't start new kanji */
2536 SetRenditionMline(ml
, x
);
2537 PUTCHAR(ml
->image
[x
]);
2539 if (dw_left(ml
, x
, D_encoding
))
2540 PUTCHAR(ml
->image
[++x
]);
2543 #if 0 /* not needed any longer */
2544 /* compare != 0 because ' ' can happen when clipping occures */
2545 if (to
== D_width
- 1 && y
< D_height
- 1 && D_x
== D_width
&& ml
->image
[to
+ 1])
2551 SetRenditionMline(ml
, x
+ 1);
2552 PUTCHAR(ml
->image
[x
+ 1]);
2554 SetRenditionMline(ml
, x
);
2555 INSERTCHAR(ml
->image
[x
]);
2560 SetRendition(&mchar_null
);
2577 PUTCHARLP(c
->image
);
2582 if (D_encoding
== UTF8
)
2591 InsChar(c
, x
, xe
, y
, oml
)
2597 if (y
== D_bot
&& !D_CLP
)
2599 if (x
== D_width
- 1)
2605 if (xe
== D_width
- 1)
2611 PUTCHARLP(c
->image
);
2614 if (!(D_IC
|| D_CIC
|| D_IM
) || xe
!= D_width
- 1)
2616 RefreshLine(y
, x
, xe
, 0);
2618 /* UpdateLine(oml, y, x, xe); */
2625 if (c
->mbcs
&& D_IC
)
2630 AddCStr2(D_CIC
, c
->mbcs
? 2 : 1);
2639 RAW_PUTCHAR(c
->image
);
2644 if (D_encoding
== UTF8
)
2647 if (D_x
== D_width
- 1)
2650 RAW_PUTCHAR(c
->mbcs
);
2656 WrapChar(c
, x
, y
, xs
, ys
, xe
, ye
, ins
)
2665 bce
= rend_getbg(c
);
2670 debug2(" x %d y %d", x
, y
);
2671 debug2(" Dx %d Dy %d", D_x
, D_y
);
2672 debug2(" xs %d ys %d", xs
, ys
);
2673 debug3(" xe %d ye %d ins %d\n", xe
, ye
, ins
);
2674 if (xs
!= 0 || x
!= D_width
|| !D_AM
)
2677 ScrollV(xs
, ys
, xe
, ye
, 1, bce
);
2678 else if (y
< D_height
- 1)
2681 InsChar(c
, xs
, xe
, y
, 0);
2686 if (y
== ye
) /* we have to scroll */
2688 debug("- scrolling\n");
2689 ChangeScrollRegion(ys
, ye
);
2690 if (D_bot
!= y
|| D_x
!= D_width
|| (!bce
&& !D_BE
))
2692 debug("- have to call ScrollV\n");
2693 ScrollV(xs
, ys
, xe
, ye
, 1, bce
);
2697 else if (y
== D_bot
) /* remove unusable region? */
2698 ChangeScrollRegion(0, D_height
- 1);
2699 if (D_x
!= D_width
|| D_y
!= y
)
2701 if (D_CLP
&& y
>= 0) /* don't even try if !LP */
2702 RefreshLine(y
, D_width
- 1, D_width
- 1, 0);
2703 debug2("- refresh last char -> x,y now %d,%d\n", D_x
, D_y
);
2704 if (D_x
!= D_width
|| D_y
!= y
) /* sorry, no bonus */
2707 ScrollV(xs
, ys
, xe
, ye
, 1, bce
);
2708 GotoPos(xs
, y
== ye
|| y
== D_height
- 1 ? y
: y
+ 1);
2711 debug("- writeing new char");
2712 if (y
!= ye
&& y
< D_height
- 1)
2714 if (ins
!= D_insert
)
2716 if (ins
&& !D_insert
)
2718 InsChar(c
, 0, xe
, y
, 0);
2719 debug2(" -> done with insert (%d,%d)\n", D_x
, D_y
);
2725 RAW_PUTCHAR(c
->image
);
2730 if (D_encoding
== UTF8
)
2733 RAW_PUTCHAR(c
->mbcs
);
2736 debug2(" -> done (%d,%d)\n", D_x
, D_y
);
2740 ResizeDisplay(wi
, he
)
2744 debug2("ResizeDisplay: to (%d,%d).\n", wi
, he
);
2745 if (D_width
== wi
&& D_height
== he
)
2747 debug("ResizeDisplay: No change\n");
2750 if (D_width
!= wi
&& (D_height
== he
|| !D_CWS
) && D_CZ0
&& (wi
== Z0width
|| wi
== Z1width
))
2752 debug("ResizeDisplay: using Z0/Z1\n");
2753 AddCStr(wi
== Z0width
? D_CZ0
: D_CZ1
);
2754 ChangeScreenSize(wi
, D_height
, 0);
2755 return (he
== D_height
) ? 0 : -1;
2759 debug("ResizeDisplay: using WS\n");
2760 AddCStr(tgoto(D_CWS
, wi
, he
));
2761 ChangeScreenSize(wi
, he
, 0);
2768 ChangeScrollRegion(newtop
, newbot
)
2773 if (newtop
== newbot
)
2774 return; /* xterm etc can't do it */
2778 newbot
= D_height
- 1;
2782 D_bot
= D_height
- 1;
2785 if (D_top
== newtop
&& D_bot
== newbot
)
2787 debug2("ChangeScrollRegion: (%d - %d)\n", newtop
, newbot
);
2788 AddCStr(tgoto(D_CS
, newbot
, newtop
));
2791 D_y
= D_x
= -1; /* Just in case... */
2800 static char oscs
[] = "0;\000\00020;\00039;\00049;\000";
2807 if (!D_xtermosc
[i
] && !*s
)
2810 s
= "screen"; /* always set icon name */
2812 s
= ""; /* no background */
2814 s
= "black"; /* black text */
2816 s
= "white"; /* on white background */
2819 AddStr(oscs
+ i
* 4);
2828 for (i
= 3; i
>= 0; i
--)
2834 * Output buffering routines
2846 if (D_encoding
== UTF8
)
2848 while ((c
= *str
++))
2849 AddUtf8((unsigned char)c
);
2853 while ((c
= *str
++))
2866 if (D_encoding
== UTF8
)
2868 while ((c
= *str
++) && n
-- > 0)
2869 AddUtf8((unsigned char)c
);
2873 while ((c
= *str
++) && n
-- > 0)
2888 l
= D_obufp
- D_obuf
;
2889 debug1("Flush(): %d\n", l
);
2892 ASSERT(l
+ D_obuffree
== D_obuflen
);
2902 if (fcntl(D_userfd
, F_SETFL
, 0))
2903 debug1("Warning: BLOCK fcntl failed: %d\n", errno
);
2911 FD_SET(D_userfd
, &w
);
2913 t
.tv_sec
= progress
;
2915 wr
= select(FD_SETSIZE
, (fd_set
*)0, &w
, (fd_set
*)0, &t
);
2920 debug1("Warning: select failed: %d\n", errno
);
2925 /* no progress after 3 seconds. sorry. */
2926 debug1("Warning: no progress after %d seconds\n", progress
);
2930 wr
= write(D_userfd
, p
, l
);
2935 debug1("Writing to display: %d\n", errno
);
2943 debug1("Warning: Flush could not write %d bytes\n", l
);
2948 if (fcntl(D_userfd
, F_SETFL
, FNBLOCK
))
2949 debug1("Warning: NBLOCK fcntl failed: %d\n", errno
);
2961 debug1("did freetty %d\n", D_userfd
);
2969 D_obuflenmax
= -D_obufmax
;
2975 * Asynchronous output routines by
2976 * Tim MacKenzie (tym@dibbler.cs.monash.edu.au)
2985 if (D_status_obuffree
>= 0)
2987 ASSERT(D_obuffree
== -1);
2988 RemoveStatusMinWait();
2989 if (--D_obuffree
> 0) /* redo AddChar decrement */
2992 if (D_obuflen
&& D_obuf
)
2994 ind
= D_obufp
- D_obuf
;
2996 D_obuffree
+= GRAIN
;
2997 D_obuf
= realloc(D_obuf
, D_obuflen
);
3004 D_obuf
= malloc(D_obuflen
);
3007 Panic(0, "Out of memory");
3008 D_obufp
= D_obuf
+ ind
;
3009 D_obuflenmax
= D_obuflen
- D_obufmax
;
3010 debug1("ResizeObuf: resized to %d\n", D_obuflen
);
3014 DisplaySleep1000(n
, eat
)
3026 debug("DisplaySleep has no display sigh\n");
3030 t
.tv_usec
= (n
% 1000) * 1000;
3031 t
.tv_sec
= n
/ 1000;
3033 FD_SET(D_userfd
, &r
);
3034 if (select(FD_SETSIZE
, &r
, (fd_set
*)0, (fd_set
*)0, &t
) > 0)
3036 debug("display activity stopped sleep\n");
3038 read(D_userfd
, &buf
, 1);
3040 debug2("DisplaySleep(%d) ending, eat was %d\n", n
, eat
);
3046 {/* Nuke pending output in current display, clear screen */
3048 int oldtop
= D_top
, oldbot
= D_bot
;
3049 struct mchar oldrend
;
3050 int oldkeypad
= D_keypad
, oldcursorkeys
= D_cursorkeys
;
3051 int oldcurvis
= D_curvis
;
3052 int oldmouse
= D_mouse
;
3055 len
= D_obufp
- D_obuf
;
3056 debug1("NukePending: nuking %d chars\n", len
);
3058 /* Throw away any output that we can... */
3060 tcflush(D_userfd
, TCOFLUSH
);
3063 (void) ioctl(D_userfd
, TCFLSH
, (char *) 1);
3072 /* Turn off all attributes. (Tim MacKenzie) */
3079 AddStr("\033[m"); /* why is D_ME not set? */
3084 /* Check for toggle */
3085 if (D_IM
&& strcmp(D_IM
, D_EI
))
3088 /* Check for toggle */
3090 if (D_KS
&& strcmp(D_KS
, D_KE
))
3092 if (D_CCS
&& strcmp(D_CCS
, D_CCE
))
3095 if (D_KS
&& strcmp(D_KS
, D_KE
))
3098 if (D_CCS
&& strcmp(D_CCS
, D_CCE
))
3103 D_rend
= mchar_null
;
3109 ChangeScrollRegion(oldtop
, oldbot
);
3110 SetRendition(&oldrend
);
3111 KeypadMode(oldkeypad
);
3112 CursorkeysMode(oldcursorkeys
);
3113 CursorVisibility(oldcurvis
);
3114 MouseMode(oldmouse
);
3117 debug("ResizeDisplay: using WS\n");
3118 AddCStr(tgoto(D_CWS
, D_width
, D_height
));
3120 else if (D_CZ0
&& (D_width
== Z0width
|| D_width
== Z1width
))
3122 debug("ResizeDisplay: using Z0/Z1\n");
3123 AddCStr(D_width
== Z0width
? D_CZ0
: D_CZ1
);
3126 #endif /* AUTO_NUKE */
3129 /* linux' select can't handle flow control, so wait 100ms if
3133 disp_writeev_eagain(ev
, data
)
3137 display
= (struct display
*)data
;
3139 D_writeev
.type
= EV_WRITE
;
3140 D_writeev
.handler
= disp_writeev_fn
;
3146 disp_writeev_fn(ev
, data
)
3150 int len
, size
= OUTPUT_BLOCK_SIZE
;
3152 display
= (struct display
*)data
;
3153 len
= D_obufp
- D_obuf
;
3156 if (D_status_obufpos
&& size
> D_status_obufpos
)
3157 size
= D_status_obufpos
;
3159 size
= write(D_userfd
, D_obuf
, size
);
3165 bcopy(D_obuf
+ size
, D_obuf
, len
);
3166 debug2("ASYNC: wrote %d - remaining %d\n", size
, len
);
3170 if (D_status_obufpos
)
3172 D_status_obufpos
-= size
;
3173 if (!D_status_obufpos
)
3175 debug("finished writing the status message\n");
3176 /* we're finished displaying the message! */
3177 if (D_status
== STATUS_ON_WIN
)
3179 /* setup continue trigger */
3180 D_status_obuflen
= D_obuflen
;
3181 D_status_obuffree
= D_obuffree
;
3182 /* setting obbuffree to 0 will make AddChar call
3184 D_obuffree
= D_obuflen
= 0;
3186 gettimeofday(&D_status_time
, NULL
);
3187 SetTimeout(&D_statusev
, MsgWait
);
3190 RefreshBraille(); /* let user see multiple Msg()s */
3196 D_blocked_fuzz
-= size
;
3197 if (D_blocked_fuzz
< 0)
3200 if (D_blockedev
.queued
)
3202 if (D_obufp
- D_obuf
> D_obufmax
/ 2)
3204 debug2("%s: resetting timeout to %g secs\n", D_usertty
, D_nonblock
/1000.);
3205 SetTimeout(&D_blockedev
, D_nonblock
);
3209 debug1("%s: deleting blocked timeout\n", D_usertty
);
3210 evdeq(&D_blockedev
);
3213 if (D_blocked
== 1 && D_obuf
== D_obufp
)
3215 /* empty again, restart output */
3216 debug1("%s: buffer empty, unblocking\n", D_usertty
);
3218 Activate(D_fore
? D_fore
->w_norefresh
: 0);
3219 D_blocked_fuzz
= D_obufp
- D_obuf
;
3225 /* linux flow control is badly broken */
3226 if (errno
== EAGAIN
)
3229 D_writeev
.type
= EV_TIMEOUT
;
3230 D_writeev
.handler
= disp_writeev_eagain
;
3231 SetTimeout(&D_writeev
, 100);
3235 if (errno
!= EINTR
&& errno
!= EAGAIN
)
3236 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
3237 if (errno
!= EWOULDBLOCK
)
3239 Msg(errno
, "Error writing output to display");
3244 disp_readev_fn(ev
, data
)
3252 display
= (struct display
*)data
;
3254 /* Hmmmm... a bit ugly... */
3256 for (cv
= D_forecv
->c_layer
->l_cvlist
; cv
; cv
= cv
->c_lnext
)
3258 display
= cv
->c_display
;
3259 if (D_status
== STATUS_ON_WIN
)
3263 display
= (struct display
*)data
;
3270 size
= sizeof(D_fore
->w_pwin
->p_inbuf
) - D_fore
->w_pwin
->p_inlen
;
3273 size
= sizeof(D_fore
->w_inbuf
) - D_fore
->w_inlen
;
3279 size
= 1; /* Always allow one char for command keys */
3281 size
= read(D_userfd
, buf
, size
);
3284 if (errno
== EINTR
|| errno
== EAGAIN
)
3286 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
3287 if (errno
== EWOULDBLOCK
)
3290 debug1("Read error: %d - hangup!\n", errno
);
3297 debug("Found EOF - hangup!\n");
3308 Activate(D_fore
? D_fore
->w_norefresh
: 0);
3313 if (D_blocked
> 1) /* 2, 3 */
3319 for (p
= windows
; p
; p
= p
->w_next
)
3320 if (p
->w_zdisplay
== display
)
3322 flayer
= &p
->w_layer
;
3325 LayProcess(&bufp
, &size
);
3328 debug("zmodem window gone, deblocking display");
3329 zmodem_abort(0, display
);
3335 D_fore
->w_lastdisp
= display
;
3336 if (D_mouse
&& D_forecv
)
3338 unsigned char *bp
= (unsigned char *)buf
;
3341 /* XXX this assumes that the string is read in as a whole... */
3342 for (i
= size
; i
> 0; i
--, bp
++)
3344 if (i
> 5 && bp
[0] == 033 && bp
[1] == '[' && bp
[2] == 'M')
3349 else if (i
< 5 || bp
[0] != 0233 || bp
[1] != 'M')
3353 if (x
>= D_forecv
->c_xs
&& x
<= D_forecv
->c_xe
&& y
>= D_forecv
->c_ys
&& y
<= D_forecv
->c_ye
)
3355 if (D_fore
&& D_fore
->w_mouse
)
3357 /* Send clicks only if the window is expecting clicks */
3358 x
-= D_forecv
->c_xoff
;
3359 y
-= D_forecv
->c_yoff
;
3360 if (x
>= 0 && x
< D_forecv
->c_layer
->l_width
&& y
>= 0 && y
< D_forecv
->c_layer
->l_height
)
3370 else if (D_mousetrack
&& bp
[2] == '#')
3372 /* 'focus' to the clicked region, only on mouse up */
3373 struct canvas
*cv
= FindCanvas(x
, y
);
3376 SetForeCanvas(display
, cv
);
3377 /* XXX: Do we want to reset the input buffer? */
3382 bcopy((char *)bp
+ 1, (char *)bp
, i
);
3387 bcopy((char *)bp
+ 5, (char *)bp
, i
- 5);
3394 if (D_encoding
!= (D_forecv
? D_forecv
->c_layer
->l_encoding
: 0))
3397 char buf2
[IOSIZE
* 2 + 10];
3398 enc
= D_forecv
? D_forecv
->c_layer
->l_encoding
: 0;
3399 for (i
= j
= 0; i
< size
; i
++)
3401 c
= ((unsigned char *)buf
)[i
];
3402 c
= DecodeChar(c
, D_encoding
, &D_decodestate
);
3404 i
--; /* try char again */
3410 j
+= EncodeChar(buf2
+ j
, c
, enc
, &font
);
3411 j
+= EncodeChar(buf2
+ j
, -1, enc
, &font
);
3414 j
+= EncodeChar(buf2
+ j
, c
, enc
, 0);
3415 if (j
> (int)sizeof(buf2
) - 10) /* just in case... */
3418 (*D_processinput
)(buf2
, j
);
3422 (*D_processinput
)(buf
, size
);
3426 disp_status_fn(ev
, data
)
3430 display
= (struct display
*)data
;
3431 debug1("disp_status_fn for display %x\n", (int)display
);
3437 disp_hstatus_fn(ev
, data
)
3441 display
= (struct display
*)data
;
3442 if (D_status
== STATUS_ON_HS
)
3452 disp_blocked_fn(ev
, data
)
3458 display
= (struct display
*)data
;
3459 debug1("blocked timeout %s\n", D_usertty
);
3460 if (D_obufp
- D_obuf
> D_obufmax
+ D_blocked_fuzz
)
3462 debug("stopping output to display\n");
3464 /* re-enable all windows */
3465 for (p
= windows
; p
; p
= p
->w_next
)
3466 if (p
->w_readev
.condneg
== &D_obuflenmax
)
3468 debug1("freeing window #%d\n", p
->w_number
);
3469 p
->w_readev
.condpos
= p
->w_readev
.condneg
= 0;
3476 disp_map_fn(ev
, data
)
3483 display
= (struct display
*)data
;
3484 debug("Flushing map sequence\n");
3487 p
= (char *)D_seqp
- l
;
3488 D_seqp
= D_kmaps
+ 3;
3490 if ((q
= D_seqh
) != 0)
3493 i
= q
[0] << 8 | q
[1];
3494 i
&= ~KMAP_NOTIMEOUT
;
3495 debug1("Mapping former hit #%d - ", i
);
3496 debug2("%d(%s) - ", q
[2], q
+ 3);
3498 ProcessInput2((char *)q
+ 3, q
[2]);
3511 disp_idle_fn(ev
, data
)
3515 struct display
*olddisplay
;
3516 display
= (struct display
*)data
;
3517 debug("idle timeout\n");
3518 if (idletimo
<= 0 || idleaction
.nr
== RC_ILLEGAL
)
3520 olddisplay
= display
;
3521 flayer
= D_forecv
->c_layer
;
3523 DoAction(&idleaction
, -1);
3524 if (idleaction
.nr
== RC_BLANKER
)
3526 for (display
= displays
; display
; display
= display
->d_next
)
3527 if (olddisplay
== display
)
3538 SetTimeout(&D_idleev
, idletimo
);
3539 if (!D_idleev
.queued
)
3550 disp_blanker_fn(ev
, data
)
3554 char buf
[IOSIZE
], *b
;
3557 display
= (struct display
*)data
;
3558 size
= read(D_blankerev
.fd
, buf
, IOSIZE
);
3561 evdeq(&D_blankerev
);
3562 close(D_blankerev
.fd
);
3563 D_blankerev
.fd
= -1;
3566 for (b
= buf
; size
; size
--)
3573 int oldtop
= D_top
, oldbot
= D_bot
;
3574 struct mchar oldrend
;
3576 if (D_blankerev
.fd
== -1)
3580 evdeq(&D_blankerev
);
3581 close(D_blankerev
.fd
);
3582 D_blankerev
.fd
= -1;
3583 Kill(D_blankerpid
, SIGHUP
);
3595 AddStr("\033[m\033[m"); /* why is D_ME not set? */
3602 D_rend
= mchar_null
;
3606 ChangeScrollRegion(oldtop
, oldbot
);
3607 SetRendition(&oldrend
);
3620 char libuf
[20], cobuf
[20];
3624 strcpy(termname
, "TERM=");
3625 strncpy(termname
+ 5, D_termname
, sizeof(termname
) - 6);
3626 termname
[sizeof(termname
) - 1] = 0;
3629 if ((D_blankerev
.fd
= OpenPTY(&m
)) == -1)
3631 Msg(0, "OpenPty failed");
3637 if ((slave
= open(m
, O_RDWR
|O_NOCTTY
)) == -1)
3639 Msg(errno
, "%s", m
);
3640 close(D_blankerev
.fd
);
3641 D_blankerev
.fd
= -1;
3646 switch (pid
= (int)fork())
3650 close(D_blankerev
.fd
);
3651 D_blankerev
.fd
= -1;
3656 if (dfp
&& dfp
!= stderr
)
3662 if (setgid(real_gid
) || setuid(real_uid
))
3663 Panic(errno
, "setuid/setgid");
3669 closeallfiles(slave
);
3670 if (open(m
, O_RDWR
))
3671 Panic(errno
, "Cannot open %s", m
);
3678 SetTTY(0, &D_OldMode
);
3683 glwz
.ws_col
= D_width
;
3684 glwz
.ws_row
= D_height
;
3685 (void)ioctl(0, TIOCSWINSZ
, (char *)&glwz
);
3687 sprintf(libuf
, "LINES=%d", D_height
);
3688 sprintf(cobuf
, "COLUMNS=%d", D_width
);
3693 signal(SIGPIPE
, SIG_DFL
);
3696 execvpe(*cmdv
, cmdv
, NewEnv
+ 3);
3697 Panic(errno
, "%s", *cmdv
);
3702 evenq(&D_blankerev
);
3707 #endif /* BLANKER_PRG */