1 /* $XTermId: util.c,v 1.889 2021/11/08 23:56:52 tom Exp $ */
2 
3 /*
4  * Copyright 1999-2020,2021 by Thomas E. Dickey
5  *
6  *                         All Rights Reserved
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sublicense, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject to
14  * the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Except as contained in this notice, the name(s) of the above copyright
28  * holders shall not be used in advertising or otherwise to promote the
29  * sale, use or other dealings in this Software without prior written
30  * authorization.
31  *
32  *
33  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34  *
35  *                         All Rights Reserved
36  *
37  * Permission to use, copy, modify, and distribute this software and its
38  * documentation for any purpose and without fee is hereby granted,
39  * provided that the above copyright notice appear in all copies and that
40  * both that copyright notice and this permission notice appear in
41  * supporting documentation, and that the name of Digital Equipment
42  * Corporation not be used in advertising or publicity pertaining to
43  * distribution of the software without specific, written prior permission.
44  *
45  *
46  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52  * SOFTWARE.
53  */
54 
55 /* util.c */
56 
57 #include <xterm.h>
58 
59 #include <data.h>
60 #include <error.h>
61 #include <menu.h>
62 #include <fontutils.h>
63 #include <xstrings.h>
64 
65 #include <stdio.h>
66 #include <string.h>
67 #include <ctype.h>
68 #include <assert.h>
69 
70 #if OPT_WIDE_CHARS
71 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
72 #include <wchar.h>
73 #endif
74 #include <wcwidth.h>
75 #endif
76 
77 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
78 #include <X11/extensions/Xinerama.h>
79 #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
80 
81 #include <graphics.h>
82 
83 #define IncrementSavedLines(amount) \
84 	    if (screen->savedlines < screen->savelines) { \
85 		if ((screen->savedlines += amount) > screen->savelines) \
86 		    screen->savedlines = screen->savelines; \
87 		ScrollBarDrawThumb(xw, 1); \
88 	    }
89 
90 static int handle_translated_exposure(XtermWidget xw,
91 				      int rect_x,
92 				      int rect_y,
93 				      int rect_width,
94 				      int rect_height);
95 static void ClearLeft(XtermWidget xw);
96 static void CopyWait(XtermWidget xw);
97 static void horizontal_copy_area(XtermWidget xw,
98 				 int firstchar,
99 				 int nchars,
100 				 int amount);
101 static void vertical_copy_area(XtermWidget xw,
102 			       int firstline,
103 			       int nlines,
104 			       int amount,
105 			       int left,
106 			       int right);
107 
108 #if OPT_WIDE_CHARS
109 unsigned first_widechar;
110 int (*my_wcwidth) (wchar_t);
111 #endif
112 
113 #if OPT_WIDE_CHARS
114 /*
115  * We will modify the 'n' cells beginning at the current position.
116  * Some of those cells may be part of multi-column characters, including
117  * carryover from the left.  Find the limits of the multi-column characters
118  * that we should fill with blanks, return true if filling is needed.
119  */
120 int
DamagedCells(TScreen * screen,unsigned n,int * klp,int * krp,int row,int col)121 DamagedCells(TScreen *screen, unsigned n, int *klp, int *krp, int row, int col)
122 {
123     CLineData *ld = getLineData(screen, row);
124     int result = False;
125 
126     assert(ld);
127     if (col < (int) ld->lineSize) {
128 	int nn = (int) n;
129 	int kl = col;
130 	int kr = col + nn;
131 
132 	if (kr >= (int) ld->lineSize) {
133 	    nn = (ld->lineSize - col - 1);
134 	    kr = col + nn;
135 	}
136 
137 	if (nn > 0) {
138 	    assert(kl < (int) ld->lineSize);
139 	    if (ld->charData[kl] == HIDDEN_CHAR) {
140 		while (kl > 0) {
141 		    if (ld->charData[--kl] != HIDDEN_CHAR) {
142 			break;
143 		    }
144 		}
145 	    } else {
146 		kl = col + 1;
147 	    }
148 
149 	    assert(kr < (int) ld->lineSize);
150 	    if (ld->charData[kr] == HIDDEN_CHAR) {
151 		while (kr < screen->max_col) {
152 		    assert((kr + 1) < (int) ld->lineSize);
153 		    if (ld->charData[++kr] != HIDDEN_CHAR) {
154 			--kr;
155 			break;
156 		    }
157 		}
158 	    } else {
159 		kr = col - 1;
160 	    }
161 
162 	    if (klp)
163 		*klp = kl;
164 	    if (krp)
165 		*krp = kr;
166 	    result = (kr >= kl);
167 	}
168     }
169 
170     return result;
171 }
172 
173 int
DamagedCurCells(TScreen * screen,unsigned n,int * klp,int * krp)174 DamagedCurCells(TScreen *screen, unsigned n, int *klp, int *krp)
175 {
176     return DamagedCells(screen, n, klp, krp, screen->cur_row, screen->cur_col);
177 }
178 #endif /* OPT_WIDE_CHARS */
179 
180 /*
181  * These routines are used for the jump scroll feature
182  */
183 void
FlushScroll(XtermWidget xw)184 FlushScroll(XtermWidget xw)
185 {
186     TScreen *screen = TScreenOf(xw);
187     int i;
188     int shift = INX2ROW(screen, 0);
189     int bot = screen->max_row - shift;
190     int refreshtop;
191     int refreshheight;
192     int scrolltop;
193     int scrollheight;
194     int left = ScrnLeftMargin(xw);
195     int right = ScrnRightMargin(xw);
196     Boolean full_lines = (Boolean) ((left == 0) && (right == screen->max_col));
197 
198     if (screen->cursor_state)
199 	HideCursor(xw);
200 
201     TRACE(("FlushScroll %s-lines scroll:%d refresh %d\n",
202 	   full_lines ? "full" : "partial",
203 	   screen->scroll_amt,
204 	   screen->refresh_amt));
205 
206     if (screen->scroll_amt > 0) {
207 	/*
208 	 * Lines will be scrolled "up".
209 	 */
210 	refreshheight = screen->refresh_amt;
211 	scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
212 	refreshtop = screen->bot_marg - refreshheight + 1 + shift;
213 	i = screen->max_row - screen->scroll_amt + 1;
214 	if (refreshtop > i) {
215 	    refreshtop = i;
216 	}
217 
218 	/*
219 	 * If this is the normal (not alternate) screen, and the top margin is
220 	 * at the top of the screen, then we will shift full lines scrolled out
221 	 * of the scrolling region into the saved-lines.
222 	 */
223 	if (screen->scrollWidget
224 	    && !screen->whichBuf
225 	    && full_lines
226 	    && screen->top_marg == 0) {
227 	    scrolltop = 0;
228 	    scrollheight += shift;
229 	    if (scrollheight > i)
230 		scrollheight = i;
231 	    i = screen->bot_marg - bot;
232 	    if (i > 0) {
233 		refreshheight -= i;
234 		if (refreshheight < screen->scroll_amt) {
235 		    refreshheight = screen->scroll_amt;
236 		}
237 	    }
238 	    IncrementSavedLines(screen->scroll_amt);
239 	} else {
240 	    scrolltop = screen->top_marg + shift;
241 	    i = bot - (screen->bot_marg - screen->refresh_amt + screen->scroll_amt);
242 	    if (i > 0) {
243 		if (bot < screen->bot_marg) {
244 		    refreshheight = screen->scroll_amt + i;
245 		}
246 	    } else {
247 		scrollheight += i;
248 		refreshheight = screen->scroll_amt;
249 		i = screen->top_marg + screen->scroll_amt - 1 - bot;
250 		if (i > 0) {
251 		    refreshtop += i;
252 		    refreshheight -= i;
253 		}
254 	    }
255 	}
256     } else {
257 	/*
258 	 * Lines will be scrolled "down".
259 	 */
260 	refreshheight = -screen->refresh_amt;
261 	scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
262 	refreshtop = screen->top_marg + shift;
263 	scrolltop = refreshtop + refreshheight;
264 	i = screen->bot_marg - bot;
265 	if (i > 0) {
266 	    scrollheight -= i;
267 	}
268 	i = screen->top_marg + refreshheight - 1 - bot;
269 	if (i > 0) {
270 	    refreshheight -= i;
271 	}
272     }
273 
274     vertical_copy_area(xw,
275 		       scrolltop + screen->scroll_amt,
276 		       scrollheight,
277 		       screen->scroll_amt,
278 		       left,
279 		       right);
280     ScrollSelection(screen, -(screen->scroll_amt), False);
281     screen->scroll_amt = 0;
282     screen->refresh_amt = 0;
283 
284     if (refreshheight > 0) {
285 	ClearCurBackground(xw,
286 			   refreshtop,
287 			   left,
288 			   (unsigned) refreshheight,
289 			   (unsigned) (right + 1 - left),
290 			   (unsigned) FontWidth(screen));
291 	ScrnRefresh(xw,
292 		    refreshtop,
293 		    0,
294 		    refreshheight,
295 		    MaxCols(screen),
296 		    False);
297     }
298     xtermTimedDbe(xw);
299     return;
300 }
301 
302 /*
303  * Returns true if there are lines off-screen due to scrolling which should
304  * include the current line.  If false, the line is visible and we should
305  * paint it now rather than waiting for the line to become visible.
306  */
307 static Bool
AddToRefresh(XtermWidget xw)308 AddToRefresh(XtermWidget xw)
309 {
310     TScreen *screen = TScreenOf(xw);
311     int amount = screen->refresh_amt;
312     int row = screen->cur_row;
313     Bool result;
314 
315     if (amount == 0) {
316 	result = False;
317     } else if (amount > 0) {
318 	int bottom;
319 
320 	if (row == (bottom = screen->bot_marg) - amount) {
321 	    screen->refresh_amt++;
322 	    result = True;
323 	} else {
324 	    result = (row >= bottom - amount + 1 && row <= bottom);
325 	}
326     } else {
327 	int top;
328 
329 	amount = -amount;
330 	if (row == (top = screen->top_marg) + amount) {
331 	    screen->refresh_amt--;
332 	    result = True;
333 	} else {
334 	    result = (row <= top + amount - 1 && row >= top);
335 	}
336     }
337 
338     /*
339      * If this line is visible, and there are scrolled-off lines, flush out
340      * those which are now visible.
341      */
342     if (!result && screen->scroll_amt)
343 	FlushScroll(xw);
344 
345     return result;
346 }
347 
348 /*
349  * Returns true if the current row is in the visible area (it should be for
350  * screen operations) and incidentally flush the scrolled-in lines which
351  * have newly become visible.
352  */
353 static Bool
AddToVisible(XtermWidget xw)354 AddToVisible(XtermWidget xw)
355 {
356     TScreen *screen = TScreenOf(xw);
357     Bool result = False;
358 
359     if (INX2ROW(screen, screen->cur_row) <= screen->max_row) {
360 	if (!AddToRefresh(xw)) {
361 	    result = True;
362 	}
363     }
364     return result;
365 }
366 
367 /*
368  * If we're scrolling, leave the selection intact if possible.
369  * If it will bump into one of the extremes of the saved-lines, truncate that.
370  * If the selection is not entirely contained within the margins and not
371  * entirely outside the margins, clear it.
372  */
373 static void
adjustHiliteOnFwdScroll(XtermWidget xw,int amount,Bool all_lines)374 adjustHiliteOnFwdScroll(XtermWidget xw, int amount, Bool all_lines)
375 {
376     TScreen *screen = TScreenOf(xw);
377     int lo_row = (all_lines
378 		  ? (screen->bot_marg - screen->savelines)
379 		  : screen->top_marg);
380     int hi_row = screen->bot_marg;
381     int left = ScrnLeftMargin(xw);
382     int right = ScrnRightMargin(xw);
383 
384     TRACE2(("adjustSelection FWD %s by %d (%s)\n",
385 	    screen->whichBuf ? "alternate" : "normal",
386 	    amount,
387 	    all_lines ? "all" : "visible"));
388     TRACE2(("  before highlite %d.%d .. %d.%d\n",
389 	    screen->startH.row,
390 	    screen->startH.col,
391 	    screen->endH.row,
392 	    screen->endH.col));
393     TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
394     TRACE2(("  limits  %d..%d\n", lo_row, hi_row));
395 
396     if ((left > 0 || right < screen->max_col) &&
397 	((screen->startH.row >= lo_row &&
398 	  screen->startH.row - amount <= hi_row) ||
399 	 (screen->endH.row >= lo_row &&
400 	  screen->endH.row - amount <= hi_row))) {
401 	/*
402 	 * This could be improved slightly by excluding the special case where
403 	 * the selection is on a single line outside left/right margins.
404 	 */
405 	TRACE2(("deselect because selection overlaps with scrolled partial-line\n"));
406 	ScrnDisownSelection(xw);
407     } else if (screen->startH.row >= lo_row
408 	       && screen->startH.row - amount < lo_row) {
409 	/* truncate the selection because its start would move out of region */
410 	if (lo_row + amount <= screen->endH.row) {
411 	    TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
412 		    screen->startH.row,
413 		    screen->startH.col,
414 		    lo_row + amount,
415 		    0));
416 	    screen->startH.row = lo_row + amount;
417 	    screen->startH.col = 0;
418 	} else {
419 	    TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
420 		    screen->startH.row,
421 		    screen->startH.col,
422 		    screen->endH.row,
423 		    screen->endH.col,
424 		    -amount,
425 		    lo_row,
426 		    hi_row));
427 	    ScrnDisownSelection(xw);
428 	}
429     } else if (screen->startH.row <= hi_row && screen->endH.row > hi_row) {
430 	TRACE2(("deselect because selection straddles top-margin\n"));
431 	ScrnDisownSelection(xw);
432     } else if (screen->startH.row < lo_row && screen->endH.row > lo_row) {
433 	TRACE2(("deselect because selection straddles bottom-margin\n"));
434 	ScrnDisownSelection(xw);
435     }
436 
437     TRACE2(("  after highlite %d.%d .. %d.%d\n",
438 	    screen->startH.row,
439 	    screen->startH.col,
440 	    screen->endH.row,
441 	    screen->endH.col));
442 }
443 
444 /*
445  * This is the same as adjustHiliteOnFwdScroll(), but reversed.  In this case,
446  * only the visible lines are affected.
447  */
448 static void
adjustHiliteOnBakScroll(XtermWidget xw,int amount)449 adjustHiliteOnBakScroll(XtermWidget xw, int amount)
450 {
451     TScreen *screen = TScreenOf(xw);
452     int lo_row = screen->top_marg;
453     int hi_row = screen->bot_marg;
454 
455     TRACE2(("adjustSelection BAK %s by %d (%s)\n",
456 	    screen->whichBuf ? "alternate" : "normal",
457 	    amount,
458 	    "visible"));
459     TRACE2(("  before highlite %d.%d .. %d.%d\n",
460 	    screen->startH.row,
461 	    screen->startH.col,
462 	    screen->endH.row,
463 	    screen->endH.col));
464     TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
465 
466     if (screen->endH.row >= hi_row
467 	&& screen->endH.row + amount > hi_row) {
468 	/* truncate the selection because its start would move out of region */
469 	if (hi_row - amount >= screen->startH.row) {
470 	    TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
471 		    screen->startH.row,
472 		    screen->startH.col,
473 		    hi_row - amount,
474 		    0));
475 	    screen->endH.row = hi_row - amount;
476 	    screen->endH.col = 0;
477 	} else {
478 	    TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
479 		    screen->startH.row,
480 		    screen->startH.col,
481 		    screen->endH.row,
482 		    screen->endH.col,
483 		    amount,
484 		    lo_row,
485 		    hi_row));
486 	    ScrnDisownSelection(xw);
487 	}
488     } else if (screen->endH.row >= lo_row && screen->startH.row < lo_row) {
489 	ScrnDisownSelection(xw);
490     } else if (screen->endH.row > hi_row && screen->startH.row > hi_row) {
491 	ScrnDisownSelection(xw);
492     }
493 
494     TRACE2(("  after highlite %d.%d .. %d.%d\n",
495 	    screen->startH.row,
496 	    screen->startH.col,
497 	    screen->endH.row,
498 	    screen->endH.col));
499 }
500 
501 /*
502  * Move cells in LineData's on the current screen to simulate scrolling by the
503  * given amount of lines.
504  */
505 static void
scrollInMargins(XtermWidget xw,int amount,int top)506 scrollInMargins(XtermWidget xw, int amount, int top)
507 {
508     TScreen *screen = TScreenOf(xw);
509     LineData *src;
510     LineData *dst;
511     int row;
512     int left = ScrnLeftMargin(xw);
513     int right = ScrnRightMargin(xw);
514     int length = right + 1 - left;
515 
516     if_OPT_WIDE_CHARS(screen, {
517 	if (amount != 0) {
518 	    for (row = top; row <= screen->bot_marg; ++row) {
519 		LineData *ld;
520 		if ((ld = getLineData(screen, row + amount)) != 0) {
521 		    if (left > 0) {
522 			if (ld->charData[left] == HIDDEN_CHAR) {
523 			    Clear1Cell(ld, left - 1);
524 			    Clear1Cell(ld, left);
525 			}
526 		    }
527 		    if (right + 1 < (int) ld->lineSize) {
528 			if (ld->charData[right + 1] == HIDDEN_CHAR) {
529 			    Clear1Cell(ld, right);
530 			    Clear1Cell(ld, right + 1);
531 			}
532 		    }
533 		}
534 	    }
535 	}
536     });
537 
538     if (amount > 0) {
539 	for (row = top; row <= screen->bot_marg - amount; ++row) {
540 	    if ((src = getLineData(screen, row + amount)) != 0
541 		&& (dst = getLineData(screen, row)) != 0) {
542 		CopyCells(screen, src, dst, left, length, False);
543 	    }
544 	}
545 	while (row <= screen->bot_marg) {
546 	    ClearCells(xw, 0, (unsigned) length, row, left);
547 	    ++row;
548 	}
549     } else if (amount < 0) {
550 	for (row = screen->bot_marg; row >= top - amount; --row) {
551 	    if ((src = getLineData(screen, row + amount)) != 0
552 		&& (dst = getLineData(screen, row)) != 0) {
553 		CopyCells(screen, src, dst, left, length, True);
554 	    }
555 	}
556 	while (row >= top) {
557 	    ClearCells(xw, 0, (unsigned) length, row, left);
558 	    --row;
559 	}
560     }
561 }
562 
563 #if OPT_WIDE_CHARS
564 /*
565  * If we're repainting a section of wide-characters that, e.g., ClearCells has
566  * repaired when finding double-cell characters, then we should account for
567  * that in the repaint.
568  */
569 static void
ScrnUpdate2(XtermWidget xw,int toprow,int leftcol,int nrows,int ncols,Bool force)570 ScrnUpdate2(XtermWidget xw,
571 	    int toprow,
572 	    int leftcol,
573 	    int nrows,
574 	    int ncols,
575 	    Bool force)
576 {
577     if_OPT_WIDE_CHARS(TScreenOf(xw), {
578 	if (leftcol + ncols <= TScreenOf(xw)->max_col)
579 	    ncols++;
580 	if (leftcol > 0) {
581 	    leftcol--;
582 	    ncols++;
583 	}
584     });
585     ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force);
586 }
587 #else
588 #define ScrnUpdate2(xw, toprow, leftcol, nrows, ncols, force) \
589 	ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force)
590 #endif
591 
592 /*
593  * scrolls the screen by amount lines, erases bottom, doesn't alter
594  * cursor position (i.e. cursor moves down amount relative to text).
595  * All done within the scrolling region, of course.
596  * requires: amount > 0
597  */
598 void
xtermScroll(XtermWidget xw,int amount)599 xtermScroll(XtermWidget xw, int amount)
600 {
601     TScreen *screen = TScreenOf(xw);
602     int i;
603     int refreshtop = 0;
604     int refreshheight;
605     Boolean save_wrap = screen->do_wrap;
606     int left = ScrnLeftMargin(xw);
607     int right = ScrnRightMargin(xw);
608     Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
609 					  && !screen->whichBuf
610 					  && screen->top_marg == 0);
611     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
612 
613     TRACE(("xtermScroll count=%d (top %d, saved %d)\n", amount,
614 	   screen->topline, screen->savelines));
615 
616     screen->cursor_busy += 1;
617     screen->cursor_moved = True;
618 
619     if (screen->cursor_state)
620 	HideCursor(xw);
621 
622     i = screen->bot_marg - screen->top_marg + 1;
623     if (amount > i)
624 	amount = i;
625 
626     if (!scroll_full_line) {
627 	refreshheight = 0;
628     } else
629 #if OPT_SCROLL_LOCK
630 	if ((screen->allowScrollLock && screen->scroll_lock)
631 	    || (screen->autoScrollLock && screen->topline < 0)) {
632 	refreshheight = 0;
633 	screen->scroll_amt = 0;
634 	screen->refresh_amt = 0;
635 	if (--(screen->topline) < -screen->savelines) {
636 	    screen->topline = -screen->savelines;
637 	    screen->scroll_dirty = True;
638 	}
639 	if (++(screen->savedlines) > screen->savelines) {
640 	    screen->savedlines = screen->savelines;
641 	}
642     } else
643 #endif
644     {
645 	if (ScrnHaveSelection(screen))
646 	    adjustHiliteOnFwdScroll(xw, amount, scroll_all_lines);
647 
648 	if (screen->jumpscroll) {
649 	    if (screen->scroll_amt > 0) {
650 		if (!screen->fastscroll) {
651 		    if (screen->refresh_amt + amount > i)
652 			FlushScroll(xw);
653 		}
654 		screen->scroll_amt += amount;
655 		screen->refresh_amt += amount;
656 	    } else {
657 		if (!screen->fastscroll) {
658 		    if (screen->scroll_amt < 0)
659 			FlushScroll(xw);
660 		}
661 		screen->scroll_amt = amount;
662 		screen->refresh_amt = amount;
663 	    }
664 	    refreshheight = 0;
665 	} else {
666 	    int scrolltop;
667 	    int scrollheight;
668 	    int shift;
669 	    int bot;
670 
671 	    ScrollSelection(screen, -(amount), False);
672 	    if (amount == i) {
673 		ClearScreen(xw);
674 		goto done;
675 	    }
676 
677 	    shift = INX2ROW(screen, 0);
678 	    bot = screen->max_row - shift;
679 	    scrollheight = i - amount;
680 	    refreshheight = amount;
681 
682 	    if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
683 		(i = screen->max_row - refreshheight + 1))
684 		refreshtop = i;
685 
686 	    if (scroll_all_lines) {
687 		scrolltop = 0;
688 		if ((scrollheight += shift) > i)
689 		    scrollheight = i;
690 		IncrementSavedLines(amount);
691 	    } else {
692 		scrolltop = screen->top_marg + shift;
693 		if ((i = screen->bot_marg - bot) > 0) {
694 		    scrollheight -= i;
695 		    if ((i = screen->top_marg + amount - 1 - bot) >= 0) {
696 			refreshtop += i;
697 			refreshheight -= i;
698 		    }
699 		}
700 	    }
701 
702 	    if (screen->multiscroll && amount == 1 &&
703 		screen->topline == 0 && screen->top_marg == 0 &&
704 		screen->bot_marg == screen->max_row) {
705 		if (screen->incopy < 0 && screen->scrolls == 0)
706 		    CopyWait(xw);
707 		screen->scrolls++;
708 	    }
709 
710 	    vertical_copy_area(xw,
711 			       scrolltop + amount,
712 			       scrollheight,
713 			       amount,
714 			       left,
715 			       right);
716 
717 	    if (refreshheight > 0) {
718 		ClearCurBackground(xw,
719 				   refreshtop,
720 				   left,
721 				   (unsigned) refreshheight,
722 				   (unsigned) (right + 1 - left),
723 				   (unsigned) FontWidth(screen));
724 		if (refreshheight > shift)
725 		    refreshheight = shift;
726 	    }
727 	}
728     }
729 
730     if (amount > 0) {
731 	if (left > 0 || right < screen->max_col) {
732 	    scrollInMargins(xw, amount, screen->top_marg);
733 	    ScrnUpdate2(xw,
734 			screen->top_marg,
735 			left,
736 			screen->bot_marg + 1 - screen->top_marg,
737 			right + 1 - left,
738 			True);
739 	} else if (scroll_all_lines) {
740 	    ScrnDeleteLine(xw,
741 			   screen->saveBuf_index,
742 			   screen->bot_marg + screen->savelines,
743 			   0,
744 			   (unsigned) amount);
745 	} else {
746 	    ScrnDeleteLine(xw,
747 			   screen->visbuf,
748 			   screen->bot_marg,
749 			   screen->top_marg,
750 			   (unsigned) amount);
751 	}
752     }
753 
754     scroll_displayed_graphics(xw, amount);
755 
756     if (refreshheight > 0) {
757 	ScrnRefresh(xw,
758 		    refreshtop,
759 		    left,
760 		    refreshheight,
761 		    right + 1 - left,
762 		    False);
763     }
764 
765   done:
766     screen->do_wrap = save_wrap;
767     screen->cursor_busy -= 1;
768     TRACE(("...xtermScroll count=%d (top %d, saved %d)\n", amount,
769 	   screen->topline, screen->savelines));
770     return;
771 }
772 
773 /*
774  * This is from ISO 6429, not found in any of DEC's terminals.
775  */
776 void
xtermScrollLR(XtermWidget xw,int amount,Bool toLeft)777 xtermScrollLR(XtermWidget xw, int amount, Bool toLeft)
778 {
779     if (amount > 0) {
780 	xtermColScroll(xw, amount, toLeft, ScrnLeftMargin(xw));
781     }
782 }
783 
784 /*
785  * Implement DECBI/DECFI (back/forward column index)
786  */
787 void
xtermColIndex(XtermWidget xw,Bool toLeft)788 xtermColIndex(XtermWidget xw, Bool toLeft)
789 {
790     TScreen *screen = TScreenOf(xw);
791 
792     if (toLeft) {
793 	if (ScrnIsColInMargins(screen, screen->cur_col)) {
794 	    if (screen->cur_col == ScrnLeftMargin(xw)) {
795 		xtermColScroll(xw, 1, False, screen->cur_col);
796 	    } else {
797 		CursorBack(xw, 1);
798 	    }
799 	} else {
800 	    CursorBack(xw, 1);
801 	}
802     } else {
803 	if (ScrnIsColInMargins(screen, screen->cur_col)) {
804 	    if (screen->cur_col == ScrnRightMargin(xw)) {
805 		xtermColScroll(xw, 1, True, ScrnLeftMargin(xw));
806 	    } else {
807 		CursorForward(xw, 1);
808 	    }
809 	} else {
810 	    CursorForward(xw, 1);
811 	}
812     }
813 }
814 
815 /*
816  * Implement DECDC/DECIC (delete/insert column)
817  */
818 void
xtermColScroll(XtermWidget xw,int amount,Bool toLeft,int at_col)819 xtermColScroll(XtermWidget xw, int amount, Bool toLeft, int at_col)
820 {
821     TScreen *screen = TScreenOf(xw);
822 
823     if (amount > 0) {
824 	int min_row;
825 	int max_row;
826 
827 	if (ScrnHaveRowMargins(screen)) {
828 	    min_row = screen->top_marg;
829 	    max_row = screen->bot_marg;
830 	} else {
831 	    min_row = 0;
832 	    max_row = screen->max_row;
833 	}
834 
835 	if (screen->cur_row >= min_row
836 	    && screen->cur_row <= max_row
837 	    && screen->cur_col >= screen->lft_marg
838 	    && screen->cur_col <= screen->rgt_marg) {
839 	    int save_row = screen->cur_row;
840 	    int save_col = screen->cur_col;
841 	    int row;
842 
843 	    screen->cur_col = at_col;
844 	    if (toLeft) {
845 		for (row = min_row; row <= max_row; row++) {
846 		    screen->cur_row = row;
847 		    ScrnDeleteChar(xw, (unsigned) amount);
848 		}
849 	    } else {
850 		for (row = min_row; row <= max_row; row++) {
851 		    screen->cur_row = row;
852 		    ScrnInsertChar(xw, (unsigned) amount);
853 		}
854 	    }
855 	    screen->cur_row = save_row;
856 	    screen->cur_col = save_col;
857 	    xtermRepaint(xw);
858 	}
859     }
860 }
861 
862 /*
863  * Reverse scrolls the screen by amount lines, erases top, doesn't alter
864  * cursor position (i.e. cursor moves up amount relative to text).
865  * All done within the scrolling region, of course.
866  * Requires: amount > 0
867  */
868 void
RevScroll(XtermWidget xw,int amount)869 RevScroll(XtermWidget xw, int amount)
870 {
871     TScreen *screen = TScreenOf(xw);
872     int i = screen->bot_marg - screen->top_marg + 1;
873     int left = ScrnLeftMargin(xw);
874     int right = ScrnRightMargin(xw);
875     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
876 
877     TRACE(("RevScroll count=%d\n", amount));
878 
879     screen->cursor_busy += 1;
880     screen->cursor_moved = True;
881 
882     if (screen->cursor_state)
883 	HideCursor(xw);
884 
885     if (amount > i)
886 	amount = i;
887 
888     if (ScrnHaveSelection(screen))
889 	adjustHiliteOnBakScroll(xw, amount);
890 
891     if (!scroll_full_line) {
892 	;
893     } else if (screen->jumpscroll) {
894 	if (screen->scroll_amt < 0) {
895 	    if (-screen->refresh_amt + amount > i)
896 		FlushScroll(xw);
897 	    screen->scroll_amt -= amount;
898 	    screen->refresh_amt -= amount;
899 	} else {
900 	    if (screen->scroll_amt > 0)
901 		FlushScroll(xw);
902 	    screen->scroll_amt = -amount;
903 	    screen->refresh_amt = -amount;
904 	}
905     } else {
906 	int shift = INX2ROW(screen, 0);
907 	int bot = screen->max_row - shift;
908 	int refreshheight = amount;
909 	int refreshtop = screen->top_marg + shift;
910 	int scrollheight = (screen->bot_marg
911 			    - screen->top_marg - refreshheight + 1);
912 	int scrolltop = refreshtop + refreshheight;
913 
914 	if ((i = screen->bot_marg - bot) > 0)
915 	    scrollheight -= i;
916 	if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
917 	    refreshheight -= i;
918 
919 	if (screen->multiscroll && amount == 1 &&
920 	    screen->topline == 0 && screen->top_marg == 0 &&
921 	    screen->bot_marg == screen->max_row) {
922 	    if (screen->incopy < 0 && screen->scrolls == 0)
923 		CopyWait(xw);
924 	    screen->scrolls++;
925 	}
926 
927 	vertical_copy_area(xw,
928 			   scrolltop - amount,
929 			   scrollheight,
930 			   -amount,
931 			   left,
932 			   right);
933 
934 	if (refreshheight > 0) {
935 	    ClearCurBackground(xw,
936 			       refreshtop,
937 			       left,
938 			       (unsigned) refreshheight,
939 			       (unsigned) (right + 1 - left),
940 			       (unsigned) FontWidth(screen));
941 	}
942     }
943     if (amount > 0) {
944 	if (left > 0 || right < screen->max_col) {
945 	    scrollInMargins(xw, -amount, screen->top_marg);
946 	    ScrnUpdate2(xw,
947 			screen->top_marg,
948 			left,
949 			screen->bot_marg + 1 - screen->top_marg,
950 			right + 1 - left,
951 			True);
952 	} else {
953 	    ScrnInsertLine(xw,
954 			   screen->visbuf,
955 			   screen->bot_marg,
956 			   screen->top_marg,
957 			   (unsigned) amount);
958 	}
959     }
960     screen->cursor_busy -= 1;
961     return;
962 }
963 
964 #if OPT_ZICONBEEP
965 void
initZIconBeep(void)966 initZIconBeep(void)
967 {
968     if (resource.zIconBeep > 100 || resource.zIconBeep < -100) {
969 	resource.zIconBeep = 0;	/* was 100, but I prefer to defaulting off. */
970 	xtermWarning("a number between -100 and 100 is required for zIconBeep.  0 used by default\n");
971     }
972 }
973 
974 static char *
getIconName(void)975 getIconName(void)
976 {
977     static char *icon_name;
978     static Arg args[] =
979     {
980 	{XtNiconName, (XtArgVal) & icon_name}
981     };
982 
983     icon_name = NULL;
984     XtGetValues(toplevel, args, XtNumber(args));
985     return icon_name;
986 }
987 
988 static void
setZIconBeep(XtermWidget xw)989 setZIconBeep(XtermWidget xw)
990 {
991     TScreen *screen = TScreenOf(xw);
992 
993     /* Flag icon name with "***"  on window output when iconified.
994      */
995     if (resource.zIconBeep && mapstate == IsUnmapped && !screen->zIconBeep_flagged) {
996 	char *icon_name = getIconName();
997 	if (icon_name != NULL) {
998 	    screen->zIconBeep_flagged = True;
999 	    ChangeIconName(xw, icon_name);
1000 	}
1001 	xtermBell(xw, XkbBI_Info, 0);
1002     }
1003     mapstate = -1;
1004 }
1005 
1006 /*
1007  * If warning should be given then give it
1008  */
1009 Boolean
showZIconBeep(XtermWidget xw,char * name)1010 showZIconBeep(XtermWidget xw, char *name)
1011 {
1012     Boolean code = False;
1013 
1014     if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) {
1015 	char *format = resource.zIconFormat;
1016 	char *newname = malloc(strlen(name) + strlen(format) + 2);
1017 	if (!newname) {
1018 	    xtermWarning("malloc failed in showZIconBeep\n");
1019 	} else {
1020 	    char *marker = strstr(format, "%s");
1021 	    char *result = newname;
1022 	    if (marker != 0) {
1023 		size_t skip = (size_t) (marker - format);
1024 		if (skip) {
1025 		    strncpy(result, format, skip);
1026 		    result += skip;
1027 		}
1028 		strcpy(result, name);
1029 		strcat(result, marker + 2);
1030 	    } else {
1031 		strcpy(result, format);
1032 		strcat(result, name);
1033 	    }
1034 	    ChangeGroup(xw, XtNiconName, newname);
1035 	    free(newname);
1036 	}
1037 	code = True;
1038     }
1039     return code;
1040 }
1041 
1042 /*
1043  * Restore the icon name, resetting the state for zIconBeep.
1044  */
1045 void
resetZIconBeep(XtermWidget xw)1046 resetZIconBeep(XtermWidget xw)
1047 {
1048     TScreen *screen = TScreenOf(xw);
1049 
1050     if (screen->zIconBeep_flagged) {
1051 	char *icon_name = getIconName();
1052 	screen->zIconBeep_flagged = False;
1053 	if (icon_name != NULL) {
1054 	    char *buf = malloc(strlen(icon_name) + 1);
1055 	    if (buf == NULL) {
1056 		screen->zIconBeep_flagged = True;
1057 	    } else {
1058 		char *format = resource.zIconFormat;
1059 		char *marker = strstr(format, "%s");
1060 		Boolean found = False;
1061 
1062 		if (marker != 0) {
1063 		    if (marker == format
1064 			|| !strncmp(icon_name, format, (size_t) (marker - format))) {
1065 			found = True;
1066 			strcpy(buf, icon_name + (marker - format));
1067 			marker += 2;
1068 			if (*marker != '\0') {
1069 			    size_t len_m = strlen(marker);
1070 			    size_t len_b = strlen(buf);
1071 			    if (len_m < len_b
1072 				&& !strcmp(buf + len_b - len_m, marker)) {
1073 				buf[len_b - len_m] = '\0';
1074 			    }
1075 			}
1076 		    }
1077 		} else if (!strncmp(icon_name, format, strlen(format))) {
1078 		    strcpy(buf, icon_name + strlen(format));
1079 		    found = True;
1080 		}
1081 		if (found)
1082 		    ChangeIconName(xw, buf);
1083 		free(buf);
1084 	    }
1085 	}
1086     }
1087 }
1088 #else
1089 #define setZIconBeep(xw)	/* nothing */
1090 #endif /* OPT_ZICONBEEP */
1091 
1092 /*
1093  * write a string str of length len onto the screen at
1094  * the current cursor position.  update cursor position.
1095  */
1096 void
WriteText(XtermWidget xw,IChar * str,Cardinal len)1097 WriteText(XtermWidget xw, IChar *str, Cardinal len)
1098 {
1099     TScreen *screen = TScreenOf(xw);
1100     XTermDraw params;
1101     CLineData *ld = 0;
1102     unsigned attr_flags = xw->flags;
1103     CellColor fg_bg = xtermColorPair(xw);
1104     unsigned cells = visual_width(str, len);
1105     GC currentGC;
1106 
1107     TRACE(("WriteText %d (%2d,%2d) %3d:%s\n",
1108 	   screen->topline,
1109 	   screen->cur_row,
1110 	   screen->cur_col,
1111 	   len, visibleIChars(str, len)));
1112 
1113     if (cells + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) {
1114 	cells = (unsigned) (MaxCols(screen) - screen->cur_col);
1115     }
1116 
1117     if (ScrnHaveSelection(screen)
1118 	&& ScrnIsRowInSelection(screen, INX2ROW(screen, screen->cur_row))) {
1119 	ScrnDisownSelection(xw);
1120     }
1121 #if OPT_ISO_COLORS
1122     /* if colorBDMode is set, and enabled */
1123     if (screen->colorBDMode &&
1124 	screen->boldColors &&
1125 	!hasDirectFG(attr_flags) &&
1126     /* and bold foreground color on bold background color */
1127 	GetCellColorFG(fg_bg) > COLOR_7 &&
1128 	GetCellColorFG(fg_bg) < MIN_ANSI_COLORS &&
1129     /* and both colors are the same */
1130 	GetCellColorFG(fg_bg) == GetCellColorBG(fg_bg))
1131 	/* clear BOLD flag, else it will be colorBD on bold background color */
1132 	UIntClr(attr_flags, BOLD);
1133 #endif
1134 
1135     /* if we are in insert-mode, reserve space for the new cells */
1136     if (attr_flags & INSERT) {
1137 	InsertChar(xw, cells);
1138     }
1139 
1140     if (AddToVisible(xw)
1141 	&& ((ld = getLineData(screen, screen->cur_row))) != 0) {
1142 	unsigned test;
1143 
1144 	if (screen->cursor_state)
1145 	    HideCursor(xw);
1146 
1147 	/*
1148 	 * If we overwrite part of a multi-column character, fill the rest
1149 	 * of it with blanks.
1150 	 */
1151 	if_OPT_WIDE_CHARS(screen, {
1152 	    int kl;
1153 	    int kr;
1154 	    if (DamagedCurCells(screen, cells, &kl, &kr))
1155 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1156 	});
1157 
1158 	if (attr_flags & INVISIBLE) {
1159 	    Cardinal n;
1160 	    for (n = 0; n < cells; ++n)
1161 		str[n] = ' ';
1162 	}
1163 
1164 	TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n",
1165 	       LineCharSet(screen, ld),
1166 	       screen->cur_row,
1167 	       screen->cur_col));
1168 
1169 	test = attr_flags;
1170 #if OPT_ISO_COLORS
1171 	{
1172 	    int fg;
1173 	    if (screen->colorAttrMode) {
1174 		fg = MapToColorMode(xw->cur_foreground, screen, attr_flags);
1175 	    } else {
1176 		fg = xw->cur_foreground;
1177 	    }
1178 	    checkVeryBoldColors(test, fg);
1179 	}
1180 #endif
1181 
1182 	/* make sure that the correct GC is current */
1183 	currentGC = updatedXtermGC(xw, attr_flags, fg_bg, False);
1184 
1185 	/* *INDENT-EQLS* */
1186 	params.xw          = xw;
1187 	params.attr_flags  = (test & DRAWX_MASK);
1188 	params.draw_flags  = 0;
1189 	params.this_chrset = LineCharSet(screen, ld);
1190 	params.real_chrset = CSET_SWL;
1191 	params.on_wide     = 0;
1192 
1193 	drawXtermText(&params,
1194 		      currentGC,
1195 		      LineCursorX(screen, ld, screen->cur_col),
1196 		      CursorY(screen, screen->cur_row),
1197 		      str, len);
1198 
1199 	resetXtermGC(xw, attr_flags, False);
1200     }
1201 
1202     ScrnWriteText(xw, str, attr_flags, fg_bg, len);
1203     CursorForward(xw, (int) cells);
1204     setZIconBeep(xw);
1205     return;
1206 }
1207 
1208 /*
1209  * If cursor not in scrolling region, returns.  Else,
1210  * inserts n blank lines at the cursor's position.  Lines above the
1211  * bottom margin are lost.
1212  */
1213 void
InsertLine(XtermWidget xw,int n)1214 InsertLine(XtermWidget xw, int n)
1215 {
1216     TScreen *screen = TScreenOf(xw);
1217     int i;
1218     int left = ScrnLeftMargin(xw);
1219     int right = ScrnRightMargin(xw);
1220     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
1221 
1222     if (!ScrnIsRowInMargins(screen, screen->cur_row)
1223 	|| screen->cur_col < left
1224 	|| screen->cur_col > right)
1225 	return;
1226 
1227     TRACE(("InsertLine count=%d\n", n));
1228 
1229     set_cur_col(screen, ScrnLeftMargin(xw));
1230     if (screen->cursor_state)
1231 	HideCursor(xw);
1232 
1233     if (ScrnHaveSelection(screen)
1234 	&& ScrnAreRowsInSelection(screen,
1235 				  INX2ROW(screen, screen->top_marg),
1236 				  INX2ROW(screen, screen->cur_row - 1))
1237 	&& ScrnAreRowsInSelection(screen,
1238 				  INX2ROW(screen, screen->cur_row),
1239 				  INX2ROW(screen, screen->bot_marg))) {
1240 	ScrnDisownSelection(xw);
1241     }
1242 
1243     ResetWrap(screen);
1244     if (n > (i = screen->bot_marg - screen->cur_row + 1))
1245 	n = i;
1246     if (screen->jumpscroll && scroll_full_line) {
1247 	if (screen->scroll_amt <= 0 &&
1248 	    screen->cur_row <= -screen->refresh_amt) {
1249 	    if (-screen->refresh_amt + n > MaxRows(screen))
1250 		FlushScroll(xw);
1251 	    screen->scroll_amt -= n;
1252 	    screen->refresh_amt -= n;
1253 	} else {
1254 	    if (screen->scroll_amt)
1255 		FlushScroll(xw);
1256 	}
1257     }
1258     if (!screen->scroll_amt && scroll_full_line) {
1259 	int shift = INX2ROW(screen, 0);
1260 	int bot = screen->max_row - shift;
1261 	int refreshheight = n;
1262 	int refreshtop = screen->cur_row + shift;
1263 	int scrolltop = refreshtop + refreshheight;
1264 	int scrollheight = (screen->bot_marg
1265 			    - screen->cur_row - refreshheight + 1);
1266 
1267 	if ((i = screen->bot_marg - bot) > 0)
1268 	    scrollheight -= i;
1269 	if ((i = screen->cur_row + refreshheight - 1 - bot) > 0)
1270 	    refreshheight -= i;
1271 	vertical_copy_area(xw, scrolltop - n, scrollheight, -n, left, right);
1272 	if (refreshheight > 0) {
1273 	    ClearCurBackground(xw,
1274 			       refreshtop,
1275 			       left,
1276 			       (unsigned) refreshheight,
1277 			       (unsigned) (right + 1 - left),
1278 			       (unsigned) FontWidth(screen));
1279 	}
1280     }
1281     if (n > 0) {
1282 	if (scroll_full_line) {
1283 	    ScrnInsertLine(xw,
1284 			   screen->visbuf,
1285 			   screen->bot_marg,
1286 			   screen->cur_row,
1287 			   (unsigned) n);
1288 	} else {
1289 	    scrollInMargins(xw, -n, screen->cur_row);
1290 	    ScrnUpdate2(xw,
1291 			screen->cur_row,
1292 			left,
1293 			screen->bot_marg + 1 - screen->cur_row,
1294 			right + 1 - left,
1295 			True);
1296 	}
1297     }
1298 }
1299 
1300 /*
1301  * If cursor not in scrolling region, returns.  Else, deletes n lines
1302  * at the cursor's position, lines added at bottom margin are blank.
1303  */
1304 void
DeleteLine(XtermWidget xw,int n,Bool canSave)1305 DeleteLine(XtermWidget xw, int n, Bool canSave)
1306 {
1307     TScreen *screen = TScreenOf(xw);
1308     int i;
1309     int left = ScrnLeftMargin(xw);
1310     int right = ScrnRightMargin(xw);
1311     Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
1312 					  && !screen->whichBuf
1313 					  && screen->cur_row == 0);
1314     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
1315 
1316     if (!ScrnIsRowInMargins(screen, screen->cur_row) ||
1317 	!ScrnIsColInMargins(screen, screen->cur_col))
1318 	return;
1319 
1320     TRACE(("DeleteLine count=%d\n", n));
1321 
1322     set_cur_col(screen, ScrnLeftMargin(xw));
1323     if (screen->cursor_state)
1324 	HideCursor(xw);
1325 
1326     if (n > (i = screen->bot_marg - screen->cur_row + 1)) {
1327 	n = i;
1328     }
1329     if (ScrnHaveSelection(screen)
1330 	&& ScrnAreRowsInSelection(screen,
1331 				  INX2ROW(screen, screen->cur_row),
1332 				  INX2ROW(screen, screen->cur_row + n - 1))) {
1333 	ScrnDisownSelection(xw);
1334     }
1335 
1336     ResetWrap(screen);
1337     if (screen->jumpscroll && scroll_full_line) {
1338 	if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) {
1339 	    if (screen->refresh_amt + n > MaxRows(screen))
1340 		FlushScroll(xw);
1341 	    if (canSave) {
1342 		screen->scroll_amt += n;
1343 		screen->refresh_amt += n;
1344 	    }
1345 	} else {
1346 	    if (screen->scroll_amt)
1347 		FlushScroll(xw);
1348 	}
1349     }
1350 
1351     /* adjust screen->buf */
1352     if (n > 0) {
1353 	if (left > 0 || right < screen->max_col) {
1354 	    scrollInMargins(xw, n, screen->cur_row);
1355 	} else if (canSave && scroll_all_lines) {
1356 	    ScrnDeleteLine(xw,
1357 			   screen->saveBuf_index,
1358 			   screen->bot_marg + screen->savelines,
1359 			   0,
1360 			   (unsigned) n);
1361 	} else {
1362 	    ScrnDeleteLine(xw,
1363 			   screen->visbuf,
1364 			   screen->bot_marg,
1365 			   screen->cur_row,
1366 			   (unsigned) n);
1367 	}
1368     }
1369 
1370     /* repaint the screen, as needed */
1371     if (!scroll_full_line) {
1372 	ScrnUpdate2(xw,
1373 		    screen->cur_row,
1374 		    left,
1375 		    screen->bot_marg + 1 - screen->cur_row,
1376 		    right + 1 - left,
1377 		    True);
1378     } else if (!screen->scroll_amt) {
1379 	int shift = INX2ROW(screen, 0);
1380 	int bot = screen->max_row - shift;
1381 	int refreshtop;
1382 	int refreshheight = n;
1383 	int scrolltop;
1384 	int scrollheight = i - n;
1385 
1386 	if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
1387 	    (i = screen->max_row - refreshheight + 1))
1388 	    refreshtop = i;
1389 	if (canSave && scroll_all_lines) {
1390 	    scrolltop = 0;
1391 	    if ((scrollheight += shift) > i)
1392 		scrollheight = i;
1393 	    IncrementSavedLines(n);
1394 	} else {
1395 	    scrolltop = screen->cur_row + shift;
1396 	    if ((i = screen->bot_marg - bot) > 0) {
1397 		scrollheight -= i;
1398 		if ((i = screen->cur_row + n - 1 - bot) >= 0) {
1399 		    refreshheight -= i;
1400 		}
1401 	    }
1402 	}
1403 	vertical_copy_area(xw, scrolltop + n, scrollheight, n, left, right);
1404 	if (shift > 0 && refreshheight > 0) {
1405 	    int rows = refreshheight;
1406 	    if (rows > shift)
1407 		rows = shift;
1408 	    ScrnUpdate(xw, refreshtop, 0, rows, MaxCols(screen), True);
1409 	    refreshtop += shift;
1410 	    refreshheight -= shift;
1411 	}
1412 	if (refreshheight > 0) {
1413 	    ClearCurBackground(xw,
1414 			       refreshtop,
1415 			       left,
1416 			       (unsigned) refreshheight,
1417 			       (unsigned) (right + 1 - left),
1418 			       (unsigned) FontWidth(screen));
1419 	}
1420     }
1421 }
1422 
1423 /*
1424  * Insert n blanks at the cursor's position, no wraparound
1425  */
1426 void
InsertChar(XtermWidget xw,unsigned n)1427 InsertChar(XtermWidget xw, unsigned n)
1428 {
1429     TScreen *screen = TScreenOf(xw);
1430     CLineData *ld;
1431     unsigned limit;
1432     int row = INX2ROW(screen, screen->cur_row);
1433     int left = ScrnLeftMargin(xw);
1434     int right = ScrnRightMargin(xw);
1435 
1436     if (screen->cursor_state)
1437 	HideCursor(xw);
1438 
1439     TRACE(("InsertChar count=%d\n", n));
1440 
1441     if (ScrnHaveSelection(screen)
1442 	&& ScrnIsRowInSelection(screen, row)) {
1443 	ScrnDisownSelection(xw);
1444     }
1445     ResetWrap(screen);
1446 
1447     limit = (unsigned) (right + 1 - screen->cur_col);
1448 
1449     if (n > limit)
1450 	n = limit;
1451 
1452     if (screen->cur_col < left || screen->cur_col > right) {
1453 	n = 0;
1454     } else if (AddToVisible(xw)
1455 	       && (ld = getLineData(screen, screen->cur_row)) != 0) {
1456 	int col = right + 1 - (int) n;
1457 
1458 	/*
1459 	 * If we shift part of a multi-column character, fill the rest
1460 	 * of it with blanks.  Do similar repair for the text which will
1461 	 * be shifted into the right-margin.
1462 	 */
1463 	if_OPT_WIDE_CHARS(screen, {
1464 	    int kl;
1465 	    int kr = screen->cur_col;
1466 	    if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) {
1467 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1468 	    }
1469 	    kr = screen->max_col - (int) n + 1;
1470 	    if (DamagedCells(screen, n, &kl, (int *) 0,
1471 			     screen->cur_row,
1472 			     kr) && kr > kl) {
1473 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1474 	    }
1475 	});
1476 
1477 #if OPT_DEC_CHRSET
1478 	if (CSET_DOUBLE(GetLineDblCS(ld))) {
1479 	    col = MaxCols(screen) / 2 - (int) n;
1480 	}
1481 #endif
1482 	/*
1483 	 * prevent InsertChar from shifting the end of a line over
1484 	 * if it is being appended to
1485 	 */
1486 	if (non_blank_line(screen, screen->cur_row,
1487 			   screen->cur_col, MaxCols(screen))) {
1488 	    horizontal_copy_area(xw, screen->cur_col,
1489 				 col - screen->cur_col,
1490 				 (int) n);
1491 	}
1492 
1493 	ClearCurBackground(xw,
1494 			   INX2ROW(screen, screen->cur_row),
1495 			   screen->cur_col,
1496 			   1U,
1497 			   n,
1498 			   (unsigned) LineFontWidth(screen, ld));
1499     }
1500     if (n != 0) {
1501 	/* adjust screen->buf */
1502 	ScrnInsertChar(xw, n);
1503     }
1504 }
1505 
1506 /*
1507  * Deletes n chars at the cursor's position, no wraparound.
1508  */
1509 void
DeleteChar(XtermWidget xw,unsigned n)1510 DeleteChar(XtermWidget xw, unsigned n)
1511 {
1512     TScreen *screen = TScreenOf(xw);
1513     CLineData *ld;
1514     unsigned limit;
1515     int row = INX2ROW(screen, screen->cur_row);
1516     int right = ScrnRightMargin(xw);
1517 
1518     if (screen->cursor_state)
1519 	HideCursor(xw);
1520 
1521     if (!ScrnIsColInMargins(screen, screen->cur_col))
1522 	return;
1523 
1524     TRACE(("DeleteChar count=%d\n", n));
1525 
1526     if (ScrnHaveSelection(screen)
1527 	&& ScrnIsRowInSelection(screen, row)) {
1528 	ScrnDisownSelection(xw);
1529     }
1530     ResetWrap(screen);
1531 
1532     limit = (unsigned) (right + 1 - screen->cur_col);
1533 
1534     if (n > limit)
1535 	n = limit;
1536 
1537     if (AddToVisible(xw)
1538 	&& (ld = getLineData(screen, screen->cur_row)) != 0) {
1539 	int col = right + 1 - (int) n;
1540 
1541 	/*
1542 	 * If we delete part of a multi-column character, fill the rest
1543 	 * of it with blanks.
1544 	 */
1545 	if_OPT_WIDE_CHARS(screen, {
1546 	    int kl;
1547 	    int kr;
1548 	    if (DamagedCurCells(screen, n, &kl, &kr))
1549 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1550 	});
1551 
1552 #if OPT_DEC_CHRSET
1553 	if (CSET_DOUBLE(GetLineDblCS(ld))) {
1554 	    col = MaxCols(screen) / 2 - (int) n;
1555 	}
1556 #endif
1557 	horizontal_copy_area(xw,
1558 			     (screen->cur_col + (int) n),
1559 			     col - screen->cur_col,
1560 			     -((int) n));
1561 
1562 	ClearCurBackground(xw,
1563 			   INX2ROW(screen, screen->cur_row),
1564 			   col,
1565 			   1U,
1566 			   n,
1567 			   (unsigned) LineFontWidth(screen, ld));
1568     }
1569     if (n != 0) {
1570 	/* adjust screen->buf */
1571 	ScrnDeleteChar(xw, n);
1572     }
1573 }
1574 
1575 /*
1576  * Clear from cursor position to beginning of display, inclusive.
1577  */
1578 static void
ClearAbove(XtermWidget xw)1579 ClearAbove(XtermWidget xw)
1580 {
1581     TScreen *screen = TScreenOf(xw);
1582 
1583     if (screen->protected_mode != OFF_PROTECT) {
1584 	int row;
1585 	unsigned len = (unsigned) MaxCols(screen);
1586 
1587 	assert(screen->max_col >= 0);
1588 	for (row = 0; row < screen->cur_row; row++)
1589 	    ClearInLine(xw, row, 0, len);
1590 	ClearInLine(xw, screen->cur_row, 0, (unsigned) screen->cur_col);
1591     } else {
1592 	int top;
1593 
1594 	if (screen->cursor_state)
1595 	    HideCursor(xw);
1596 	if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1597 	    int height;
1598 
1599 	    if (screen->scroll_amt)
1600 		FlushScroll(xw);
1601 	    if ((height = screen->cur_row + top) > screen->max_row)
1602 		height = screen->max_row + 1;
1603 	    if ((height -= top) > 0) {
1604 		chararea_clear_displayed_graphics(screen,
1605 						  0,
1606 						  top,
1607 						  MaxCols(screen),
1608 						  height);
1609 
1610 		ClearCurBackground(xw,
1611 				   top,
1612 				   0,
1613 				   (unsigned) height,
1614 				   (unsigned) MaxCols(screen),
1615 				   (unsigned) FontWidth(screen));
1616 	    }
1617 	}
1618 	ClearBufRows(xw, 0, screen->cur_row - 1);
1619     }
1620 
1621     ClearLeft(xw);
1622 }
1623 
1624 /*
1625  * Clear from cursor position to end of display, inclusive.
1626  */
1627 static void
ClearBelow(XtermWidget xw)1628 ClearBelow(XtermWidget xw)
1629 {
1630     TScreen *screen = TScreenOf(xw);
1631 
1632     ClearRight(xw, -1);
1633 
1634     if (screen->protected_mode != OFF_PROTECT) {
1635 	int row;
1636 	unsigned len = (unsigned) MaxCols(screen);
1637 
1638 	assert(screen->max_col >= 0);
1639 	for (row = screen->cur_row + 1; row <= screen->max_row; row++)
1640 	    ClearInLine(xw, row, 0, len);
1641     } else {
1642 	int top;
1643 
1644 	if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) {
1645 	    if (screen->scroll_amt)
1646 		FlushScroll(xw);
1647 	    if (++top <= screen->max_row) {
1648 		chararea_clear_displayed_graphics(screen,
1649 						  0,
1650 						  top,
1651 						  MaxCols(screen),
1652 						  (screen->max_row - top + 1));
1653 		ClearCurBackground(xw,
1654 				   top,
1655 				   0,
1656 				   (unsigned) (screen->max_row - top + 1),
1657 				   (unsigned) MaxCols(screen),
1658 				   (unsigned) FontWidth(screen));
1659 	    }
1660 	}
1661 	ClearBufRows(xw, screen->cur_row + 1, screen->max_row);
1662     }
1663 }
1664 
1665 /*
1666  * Clear the given row, for the given range of columns, returning 1 if no
1667  * protected characters were found, 0 otherwise.
1668  */
1669 static int
ClearInLine2(XtermWidget xw,int flags,int row,int col,unsigned len)1670 ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len)
1671 {
1672     TScreen *screen = TScreenOf(xw);
1673     CLineData *ld;
1674     int rc = 1;
1675 
1676     TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n",
1677 	   row, col, len,
1678 	   screen->startH.row,
1679 	   screen->startH.col));
1680 
1681     if (ScrnHaveSelection(screen)
1682 	&& ScrnIsRowInSelection(screen, row)) {
1683 	ScrnDisownSelection(xw);
1684     }
1685 
1686     if (col + (int) len >= MaxCols(screen)) {
1687 	len = (unsigned) (MaxCols(screen) - col);
1688     }
1689 
1690     /* If we've marked protected text on the screen, we'll have to
1691      * check each time we do an erase.
1692      */
1693     if (screen->protected_mode != OFF_PROTECT) {
1694 	unsigned n;
1695 	IAttr *attrs = getLineData(screen, row)->attribs + col;
1696 	int saved_mode = screen->protected_mode;
1697 	Bool done;
1698 
1699 	/* disable this branch during recursion */
1700 	screen->protected_mode = OFF_PROTECT;
1701 
1702 	do {
1703 	    done = True;
1704 	    for (n = 0; n < len; n++) {
1705 		if (attrs[n] & PROTECTED) {
1706 		    rc = 0;	/* found a protected segment */
1707 		    if (n != 0) {
1708 			ClearInLine(xw, row, col, n);
1709 		    }
1710 		    while ((n < len)
1711 			   && (attrs[n] & PROTECTED)) {
1712 			n++;
1713 		    }
1714 		    done = False;
1715 		    break;
1716 		}
1717 	    }
1718 	    /* setup for another segment, past the protected text */
1719 	    if (!done) {
1720 		attrs += n;
1721 		col += (int) n;
1722 		len -= n;
1723 	    }
1724 	} while (!done);
1725 
1726 	screen->protected_mode = saved_mode;
1727 	if ((int) len <= 0) {
1728 	    return 0;
1729 	}
1730     }
1731     /* fall through to the final non-protected segment */
1732 
1733     if (screen->cursor_state)
1734 	HideCursor(xw);
1735     ResetWrap(screen);
1736 
1737     if (AddToVisible(xw)
1738 	&& (ld = getLineData(screen, row)) != 0) {
1739 
1740 	ClearCurBackground(xw,
1741 			   INX2ROW(screen, row),
1742 			   col,
1743 			   1U,
1744 			   len,
1745 			   (unsigned) LineFontWidth(screen, ld));
1746     }
1747 
1748     if (len != 0) {
1749 	ClearCells(xw, flags, len, row, col);
1750     }
1751 
1752     return rc;
1753 }
1754 
1755 int
ClearInLine(XtermWidget xw,int row,int col,unsigned len)1756 ClearInLine(XtermWidget xw, int row, int col, unsigned len)
1757 {
1758     TScreen *screen = TScreenOf(xw);
1759     int flags = 0;
1760 
1761     /*
1762      * If we're clearing to the end of the line, we won't count this as
1763      * "drawn" characters.  We'll only do cut/paste on "drawn" characters,
1764      * so this has the effect of suppressing trailing blanks from a
1765      * selection.
1766      */
1767     if (col + (int) len < MaxCols(screen)) {
1768 	flags |= CHARDRAWN;
1769     }
1770     return ClearInLine2(xw, flags, row, col, len);
1771 }
1772 
1773 /*
1774  * Clear the next n characters on the cursor's line, including the cursor's
1775  * position.
1776  */
1777 void
ClearRight(XtermWidget xw,int n)1778 ClearRight(XtermWidget xw, int n)
1779 {
1780     TScreen *screen = TScreenOf(xw);
1781     LineData *ld;
1782     unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col);
1783 
1784     assert(screen->max_col >= 0);
1785     assert(screen->max_col >= screen->cur_col);
1786 
1787     if (n < 0)			/* the remainder of the line */
1788 	n = MaxCols(screen);
1789     if (n == 0)			/* default for 'ECH' */
1790 	n = 1;
1791 
1792     if (len > (unsigned) n)
1793 	len = (unsigned) n;
1794 
1795     ld = getLineData(screen, screen->cur_row);
1796     if (AddToVisible(xw)) {
1797 	if_OPT_WIDE_CHARS(screen, {
1798 	    int col = screen->cur_col;
1799 	    int row = screen->cur_row;
1800 	    int kl;
1801 	    int kr;
1802 	    if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) {
1803 		int xx = col;
1804 		if (kl < xx) {
1805 		    ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl));
1806 		}
1807 		xx = col + (int) len - 1;
1808 		if (kr > xx) {
1809 		    ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx));
1810 		}
1811 	    }
1812 	});
1813 	(void) ClearInLine(xw, screen->cur_row, screen->cur_col, len);
1814     } else {
1815 	ScrnClearCells(xw, screen->cur_row, screen->cur_col, len);
1816     }
1817 
1818     /* with the right part cleared, we can't be wrapping */
1819     LineClrWrapped(ld);
1820     ShowWrapMarks(xw, screen->cur_row, ld);
1821     ResetWrap(screen);
1822 }
1823 
1824 /*
1825  * Clear first part of cursor's line, inclusive.
1826  */
1827 static void
ClearLeft(XtermWidget xw)1828 ClearLeft(XtermWidget xw)
1829 {
1830     TScreen *screen = TScreenOf(xw);
1831     unsigned len = (unsigned) screen->cur_col + 1;
1832 
1833     assert(screen->cur_col >= 0);
1834     if (AddToVisible(xw)) {
1835 	if_OPT_WIDE_CHARS(screen, {
1836 	    int row = screen->cur_row;
1837 	    int kl;
1838 	    int kr;
1839 	    if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
1840 		ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
1841 	    }
1842 	});
1843 	(void) ClearInLine(xw, screen->cur_row, 0, len);
1844     } else {
1845 	ScrnClearCells(xw, screen->cur_row, 0, len);
1846     }
1847 }
1848 
1849 /*
1850  * Erase the cursor's line.
1851  */
1852 static void
ClearLine(XtermWidget xw)1853 ClearLine(XtermWidget xw)
1854 {
1855     TScreen *screen = TScreenOf(xw);
1856     unsigned len = (unsigned) MaxCols(screen);
1857 
1858     assert(screen->max_col >= 0);
1859     (void) ClearInLine(xw, screen->cur_row, 0, len);
1860 }
1861 
1862 void
ClearScreen(XtermWidget xw)1863 ClearScreen(XtermWidget xw)
1864 {
1865     TScreen *screen = TScreenOf(xw);
1866     int top;
1867 
1868     TRACE(("ClearScreen\n"));
1869 
1870     if (screen->cursor_state)
1871 	HideCursor(xw);
1872 
1873     ScrnDisownSelection(xw);
1874     ResetWrap(screen);
1875     if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1876 	if (screen->scroll_amt)
1877 	    FlushScroll(xw);
1878 	chararea_clear_displayed_graphics(screen,
1879 					  0,
1880 					  top,
1881 					  MaxCols(screen),
1882 					  (screen->max_row - top + 1));
1883 	ClearCurBackground(xw,
1884 			   top,
1885 			   0,
1886 			   (unsigned) (screen->max_row - top + 1),
1887 			   (unsigned) MaxCols(screen),
1888 			   (unsigned) FontWidth(screen));
1889     }
1890     ClearBufRows(xw, 0, screen->max_row);
1891 }
1892 
1893 /*
1894  * If we've written protected text DEC-style, and are issuing a non-DEC
1895  * erase, temporarily reset the protected_mode flag so that the erase will
1896  * ignore the protected flags.
1897  */
1898 void
do_erase_char(XtermWidget xw,int param,int mode)1899 do_erase_char(XtermWidget xw, int param, int mode)
1900 {
1901     TScreen *screen = TScreenOf(xw);
1902     int saved_mode = screen->protected_mode;
1903 
1904     if (saved_mode == DEC_PROTECT
1905 	&& saved_mode != mode) {
1906 	screen->protected_mode = OFF_PROTECT;
1907     }
1908 
1909     ClearRight(xw, param);
1910     screen->protected_mode = saved_mode;
1911 }
1912 
1913 void
do_erase_line(XtermWidget xw,int param,int mode)1914 do_erase_line(XtermWidget xw, int param, int mode)
1915 {
1916     TScreen *screen = TScreenOf(xw);
1917     int saved_mode = screen->protected_mode;
1918 
1919     if (saved_mode == DEC_PROTECT
1920 	&& saved_mode != mode) {
1921 	screen->protected_mode = OFF_PROTECT;
1922     }
1923 
1924     switch (param) {
1925     case -1:			/* DEFAULT */
1926     case 0:
1927 	ClearRight(xw, -1);
1928 	break;
1929     case 1:
1930 	ClearLeft(xw);
1931 	break;
1932     case 2:
1933 	ClearLine(xw);
1934 	break;
1935     }
1936     screen->protected_mode = saved_mode;
1937 }
1938 
1939 /*
1940  * Just like 'do_erase_line()', except that this intercepts ED controls.  If we
1941  * clear the whole screen, we'll get the return-value from ClearInLine, and
1942  * find if there were any protected characters left.  If not, reset the
1943  * protected mode flag in the screen data (it's slower).
1944  */
1945 void
do_erase_display(XtermWidget xw,int param,int mode)1946 do_erase_display(XtermWidget xw, int param, int mode)
1947 {
1948     TScreen *screen = TScreenOf(xw);
1949     int saved_mode = screen->protected_mode;
1950 
1951     if (saved_mode == DEC_PROTECT
1952 	&& saved_mode != mode)
1953 	screen->protected_mode = OFF_PROTECT;
1954 
1955     switch (param) {
1956     case -1:			/* DEFAULT */
1957     case 0:
1958 	if (screen->cur_row == 0
1959 	    && screen->cur_col == 0) {
1960 	    screen->protected_mode = saved_mode;
1961 	    do_erase_display(xw, 2, mode);
1962 	    saved_mode = screen->protected_mode;
1963 	} else
1964 	    ClearBelow(xw);
1965 	break;
1966 
1967     case 1:
1968 	if (screen->cur_row == screen->max_row
1969 	    && screen->cur_col == screen->max_col) {
1970 	    screen->protected_mode = saved_mode;
1971 	    do_erase_display(xw, 2, mode);
1972 	    saved_mode = screen->protected_mode;
1973 	} else
1974 	    ClearAbove(xw);
1975 	break;
1976 
1977     case 2:
1978 	/*
1979 	 * We use 'ClearScreen()' throughout the remainder of the
1980 	 * program for places where we don't care if the characters are
1981 	 * protected or not.  So we modify the logic around this call
1982 	 * on 'ClearScreen()' to handle protected characters.
1983 	 */
1984 	if (screen->protected_mode != OFF_PROTECT) {
1985 	    int row;
1986 	    int rc = 1;
1987 	    unsigned len = (unsigned) MaxCols(screen);
1988 
1989 	    assert(screen->max_col >= 0);
1990 	    for (row = 0; row <= screen->max_row; row++)
1991 		rc &= ClearInLine(xw, row, 0, len);
1992 	    if (rc != 0)
1993 		saved_mode = OFF_PROTECT;
1994 	} else {
1995 	    ClearScreen(xw);
1996 	}
1997 	break;
1998 
1999     case 3:
2000 	/* xterm addition - erase saved lines. */
2001 	if (screen->eraseSavedLines) {
2002 	    screen->savedlines = 0;
2003 	    ScrollBarDrawThumb(xw, 1);
2004 	}
2005 	break;
2006     }
2007     screen->protected_mode = saved_mode;
2008 }
2009 
2010 static Boolean
row_has_data(TScreen * screen,int row)2011 row_has_data(TScreen *screen, int row)
2012 {
2013     Boolean result = False;
2014     CLineData *ld;
2015 
2016     if ((ld = getLineData(screen, row)) != 0) {
2017 	int col;
2018 
2019 	for (col = 0; col < screen->max_col; ++col) {
2020 	    if (ld->attribs[col] & CHARDRAWN && ld->charData[col] != ' ') {
2021 		result = True;
2022 		break;
2023 	    }
2024 	}
2025     }
2026     return result;
2027 }
2028 
2029 static Boolean
screen_has_data(XtermWidget xw)2030 screen_has_data(XtermWidget xw)
2031 {
2032     TScreen *screen = TScreenOf(xw);
2033     Boolean result = False;
2034     int row;
2035 
2036     for (row = 0; row < screen->max_row; ++row) {
2037 	if (row_has_data(screen, row)) {
2038 	    result = True;
2039 	    break;
2040 	}
2041     }
2042     return result;
2043 }
2044 
2045 static void
do_extra_scroll(XtermWidget xw,Bool trimmed)2046 do_extra_scroll(XtermWidget xw, Bool trimmed)
2047 {
2048     TScreen *screen = TScreenOf(xw);
2049     int row;
2050 
2051     if (screen_has_data(xw)) {
2052 	TRACE(("do_extra_scroll buffer=%d, trimmed=%s\n", screen->whichBuf,
2053 	       BtoS(trimmed)));
2054 	if (trimmed) {
2055 	    Boolean hadData = (Boolean) ((screen->saved_fifo > 0)
2056 					 ? row_has_data(screen, -1)
2057 					 : False);
2058 
2059 	    for (row = 0; row < screen->max_row; ++row) {
2060 		Boolean hasData = row_has_data(screen, row);
2061 		if (hasData || hadData) {
2062 		    LineData *dst = addScrollback(screen);
2063 		    LineData *src = getLineData(screen, row);
2064 		    copyLineData(dst, src);
2065 		    IncrementSavedLines(1);
2066 		}
2067 		hadData = hasData;
2068 	    }
2069 	} else {
2070 	    xtermScroll(xw, screen->max_row);
2071 	    FlushScroll(xw);
2072 	}
2073 	xtermRepaint(xw);
2074     }
2075 }
2076 
2077 /*
2078  * Like tiXtraScroll, perform a scroll up of the page contents.
2079  *
2080  * In this case, it happens for the special case when erasing the whole
2081  * display, e.g., an erase-below starting from the upper-left corner of the
2082  * screen, or if the erasure applies to the whole screen.
2083  */
2084 void
do_cd_xtra_scroll(XtermWidget xw,int param)2085 do_cd_xtra_scroll(XtermWidget xw, int param)
2086 {
2087     TScreen *screen = TScreenOf(xw);
2088 
2089     TRACE(("do_cd_xtra_scroll param %d, @%d,%d vs %d,%d\n", param,
2090 	   screen->cur_row,
2091 	   screen->cur_col,
2092 	   ScrnTopMargin(xw),
2093 	   ScrnLeftMargin(xw)));
2094     if (xw->misc.cdXtraScroll
2095 	&& (param == 2 ||
2096 	    (param == 0
2097 	     && screen->cur_col <= ScrnLeftMargin(xw)
2098 	     && screen->cur_row <= ScrnTopMargin(xw)))) {
2099 	do_extra_scroll(xw, (xw->misc.cdXtraScroll == edTrim));
2100     }
2101 }
2102 
2103 /*
2104  * Scroll the page up (saving it).  This is called when doing terminal
2105  * initialization (ti) or exiting from that (te).
2106  */
2107 void
do_ti_xtra_scroll(XtermWidget xw)2108 do_ti_xtra_scroll(XtermWidget xw)
2109 {
2110     if (xw->misc.tiXtraScroll) {
2111 	do_extra_scroll(xw, False);
2112     }
2113 }
2114 
2115 static void
CopyWait(XtermWidget xw)2116 CopyWait(XtermWidget xw)
2117 {
2118     TScreen *screen = TScreenOf(xw);
2119     XEvent reply;
2120     XEvent *rep = &reply;
2121 #ifndef NO_ACTIVE_ICON
2122     int retries = 0;
2123 #endif
2124 
2125 #if USE_DOUBLE_BUFFER
2126     if (resource.buffered)
2127 	return;
2128 #endif
2129 
2130     for (;;) {
2131 #ifndef NO_ACTIVE_ICON
2132 	if (xw->work.active_icon != eiFalse) {
2133 	    /*
2134 	     * The XWindowEvent call blocks until an event is available.  That
2135 	     * can hang when using active-icon and iconifying/deiconifying
2136 	     * while the terminal is receiving lots of output.  Checking with
2137 	     * this call on the other hand may lose exposure events which
2138 	     * arrive too late.  As a compromise, try several times with a
2139 	     * time-delay before assuming no more events are available.
2140 	     */
2141 	    if (XCheckWindowEvent(screen->display,
2142 				  VWindow(screen),
2143 				  ExposureMask,
2144 				  &reply)) {
2145 		retries = 0;
2146 	    } else {
2147 		if (++retries >= 1000)
2148 		    return;
2149 		usleep(100U);	/* wait 0.1msec */
2150 		continue;
2151 	    }
2152 	} else
2153 #endif
2154 	    XWindowEvent(screen->display, VWindow(screen), ExposureMask, &reply);
2155 	switch (reply.type) {
2156 	case Expose:
2157 	    HandleExposure(xw, &reply);
2158 	    break;
2159 	case NoExpose:
2160 	case GraphicsExpose:
2161 	    if (screen->incopy <= 0) {
2162 		screen->incopy = 1;
2163 		if (screen->scrolls > 0)
2164 		    screen->scrolls--;
2165 	    }
2166 	    if (reply.type == GraphicsExpose)
2167 		HandleExposure(xw, &reply);
2168 
2169 	    if ((reply.type == NoExpose) ||
2170 		((XExposeEvent *) rep)->count == 0) {
2171 		if (screen->incopy <= 0 && screen->scrolls > 0)
2172 		    screen->scrolls--;
2173 		if (screen->scrolls == 0) {
2174 		    screen->incopy = 0;
2175 		    return;
2176 		}
2177 		screen->incopy = -1;
2178 	    }
2179 	    break;
2180 	}
2181     }
2182 }
2183 
2184 /*
2185  * used by vertical_copy_area and and horizontal_copy_area
2186  */
2187 static void
copy_area(XtermWidget xw,int src_x,int src_y,unsigned width,unsigned height,int dest_x,int dest_y)2188 copy_area(XtermWidget xw,
2189 	  int src_x,
2190 	  int src_y,
2191 	  unsigned width,
2192 	  unsigned height,
2193 	  int dest_x,
2194 	  int dest_y)
2195 {
2196     TScreen *screen = TScreenOf(xw);
2197 
2198     if (width != 0 && height != 0) {
2199 	/* wait for previous CopyArea to complete unless
2200 	   multiscroll is enabled and active */
2201 	if (screen->incopy && screen->scrolls == 0)
2202 	    CopyWait(xw);
2203 	screen->incopy = -1;
2204 
2205 	/* save for translating Expose events */
2206 	screen->copy_src_x = src_x;
2207 	screen->copy_src_y = src_y;
2208 	screen->copy_width = width;
2209 	screen->copy_height = height;
2210 	screen->copy_dest_x = dest_x;
2211 	screen->copy_dest_y = dest_y;
2212 
2213 	XCopyArea(screen->display,
2214 		  VDrawable(screen), VDrawable(screen),
2215 		  NormalGC(xw, screen),
2216 		  src_x, src_y, width, height, dest_x, dest_y);
2217     }
2218 }
2219 
2220 /*
2221  * use when inserting or deleting characters on the current line
2222  */
2223 static void
horizontal_copy_area(XtermWidget xw,int firstchar,int nchars,int amount)2224 horizontal_copy_area(XtermWidget xw,
2225 		     int firstchar,	/* char pos on screen to start copying at */
2226 		     int nchars,
2227 		     int amount)	/* number of characters to move right */
2228 {
2229     TScreen *screen = TScreenOf(xw);
2230     CLineData *ld;
2231 
2232     if ((ld = getLineData(screen, screen->cur_row)) != 0) {
2233 	int src_x = LineCursorX(screen, ld, firstchar);
2234 	int src_y = CursorY(screen, screen->cur_row);
2235 
2236 	copy_area(xw, src_x, src_y,
2237 		  (unsigned) (nchars * LineFontWidth(screen, ld)),
2238 		  (unsigned) FontHeight(screen),
2239 		  src_x + amount * LineFontWidth(screen, ld), src_y);
2240     }
2241 }
2242 
2243 /*
2244  * use when inserting or deleting lines from the screen
2245  */
2246 static void
vertical_copy_area(XtermWidget xw,int firstline,int nlines,int amount,int left,int right)2247 vertical_copy_area(XtermWidget xw,
2248 		   int firstline,	/* line on screen to start copying at */
2249 		   int nlines,
2250 		   int amount,	/* number of lines to move up (neg=down) */
2251 		   int left,
2252 		   int right)
2253 {
2254     TScreen *screen = TScreenOf(xw);
2255 
2256     TRACE(("vertical_copy_area - firstline=%d nlines=%d left=%d right=%d amount=%d\n",
2257 	   firstline, nlines, left, right, amount));
2258 
2259     if (nlines > 0) {
2260 	int src_x = CursorX(screen, left);
2261 	int src_y = firstline * FontHeight(screen) + screen->border;
2262 	unsigned int w = (unsigned) ((right + 1 - left) * FontWidth(screen));
2263 	unsigned int h = (unsigned) (nlines * FontHeight(screen));
2264 	int dst_x = src_x;
2265 	int dst_y = src_y - amount * FontHeight(screen);
2266 
2267 	copy_area(xw, src_x, src_y, w, h, dst_x, dst_y);
2268 
2269 	if (screen->show_wrap_marks) {
2270 	    int row;
2271 	    int first = firstline - amount;
2272 	    int last = firstline + nlines + amount;
2273 
2274 	    for (row = first; row < last; ++row) {
2275 		CLineData *ld;
2276 		int mapped = amount + row + screen->topline;
2277 
2278 		if ((ld = getLineData(screen, mapped)) != 0) {
2279 		    ShowWrapMarks(xw, row, ld);
2280 		}
2281 	    }
2282 	}
2283     }
2284 }
2285 
2286 /*
2287  * use when scrolling the entire screen
2288  */
2289 void
scrolling_copy_area(XtermWidget xw,int firstline,int nlines,int amount)2290 scrolling_copy_area(XtermWidget xw,
2291 		    int firstline,	/* line on screen to start copying at */
2292 		    int nlines,
2293 		    int amount)	/* number of lines to move up (neg=down) */
2294 {
2295 
2296     if (nlines > 0) {
2297 	vertical_copy_area(xw, firstline, nlines, amount, 0, TScreenOf(xw)->max_col);
2298     }
2299 }
2300 
2301 /*
2302  * Handler for Expose events on the VT widget.
2303  * Returns 1 iff the area where the cursor was got refreshed.
2304  */
2305 int
HandleExposure(XtermWidget xw,XEvent * event)2306 HandleExposure(XtermWidget xw, XEvent *event)
2307 {
2308     TScreen *screen = TScreenOf(xw);
2309     XExposeEvent *reply = (XExposeEvent *) event;
2310 
2311 #ifndef NO_ACTIVE_ICON
2312     if (reply->window == screen->iconVwin.window) {
2313 	WhichVWin(screen) = &screen->iconVwin;
2314 	TRACE(("HandleExposure - icon\n"));
2315     } else {
2316 	WhichVWin(screen) = &screen->fullVwin;
2317 	TRACE(("HandleExposure - normal\n"));
2318     }
2319     TRACE((" event %d,%d %dx%d\n",
2320 	   reply->y,
2321 	   reply->x,
2322 	   reply->height,
2323 	   reply->width));
2324 #endif /* NO_ACTIVE_ICON */
2325 
2326     /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
2327     if (!screen->incopy || event->type != Expose) {
2328 	return handle_translated_exposure(xw, reply->x, reply->y,
2329 					  reply->width,
2330 					  reply->height);
2331     } else {
2332 	/* compute intersection of area being copied with
2333 	   area being exposed. */
2334 	int both_x1 = Max(screen->copy_src_x, reply->x);
2335 	int both_y1 = Max(screen->copy_src_y, reply->y);
2336 	int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
2337 			  (reply->x + (int) reply->width));
2338 	int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
2339 			  (reply->y + (int) reply->height));
2340 	int value = 0;
2341 
2342 	/* was anything copied affected? */
2343 	if (both_x2 > both_x1 && both_y2 > both_y1) {
2344 	    /* do the copied area */
2345 	    value = handle_translated_exposure
2346 		(xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
2347 		 reply->y + screen->copy_dest_y - screen->copy_src_y,
2348 		 reply->width, reply->height);
2349 	}
2350 	/* was anything not copied affected? */
2351 	if (reply->x < both_x1 || reply->y < both_y1
2352 	    || reply->x + reply->width > both_x2
2353 	    || reply->y + reply->height > both_y2)
2354 	    value = handle_translated_exposure(xw, reply->x, reply->y,
2355 					       reply->width, reply->height);
2356 
2357 	return value;
2358     }
2359 }
2360 
2361 static void
set_background(XtermWidget xw,int color)2362 set_background(XtermWidget xw, int color)
2363 {
2364     TScreen *screen = TScreenOf(xw);
2365     Pixel c = getXtermBG(xw, xw->flags, color);
2366 
2367 #if OPT_WIDE_ATTRS
2368     TRACE(("set_background(%d) %#lx %s\n", color, c,
2369 	   ((xw->flags & ATR_DIRECT_BG)
2370 	    ? "direct"
2371 	    : "indexed")));
2372 #else
2373     TRACE(("set_background(%d) %#lx\n", color, c));
2374 #endif
2375     XSetWindowBackground(screen->display, VShellWindow(xw), c);
2376     XSetWindowBackground(screen->display, VWindow(screen), c);
2377     initBorderGC(xw, WhichVWin(screen));
2378 }
2379 
2380 void
xtermClear2(XtermWidget xw,int x,int y,unsigned width,unsigned height)2381 xtermClear2(XtermWidget xw, int x, int y, unsigned width, unsigned height)
2382 {
2383     TScreen *screen = TScreenOf(xw);
2384     VTwin *vwin = WhichVWin(screen);
2385     Drawable draw = VDrawable(screen);
2386     GC gc;
2387 
2388     if ((gc = vwin->border_gc) != 0) {
2389 	int vmark1 = screen->border;
2390 	int vmark2 = vwin->height + vmark1;
2391 	int hmark1 = OriginX(screen);
2392 	int hmark2 = vwin->width + hmark1;
2393 	if (y < vmark1) {
2394 	    int yy = y + (int) height;
2395 	    int h1 = (yy <= vmark1) ? (yy - y) : (vmark1 - y);
2396 	    XFillRectangle(screen->display, draw, gc,
2397 			   x, y, width, (unsigned) h1);
2398 	    if (yy > vmark1) {
2399 		xtermClear2(xw, x, vmark1, width, (unsigned) (yy - vmark1));
2400 	    }
2401 	} else if (y < vmark2) {
2402 	    int yy = y + (int) height;
2403 	    int h2 = (yy <= vmark2) ? (yy - y) : (vmark2 - y);
2404 	    int xb = x;
2405 	    int xx = x + (int) width;
2406 	    int ww = (int) width;
2407 	    if (x < hmark1) {
2408 		int w1 = (xx <= hmark1) ? (xx - x) : (hmark1 - x);
2409 		XFillRectangle(screen->display, draw, gc,
2410 			       x, y, (unsigned) w1, (unsigned) h2);
2411 		x += w1;
2412 		ww -= w1;
2413 	    }
2414 	    if ((ww > 0) && (x < hmark2)) {
2415 		int w2 = (xx <= hmark2) ? (xx - x) : (hmark2 - x);
2416 #if USE_DOUBLE_BUFFER
2417 		if (resource.buffered) {
2418 		    XFillRectangle(screen->display, draw,
2419 				   FillerGC(xw, screen),
2420 				   x, y, (unsigned) w2, (unsigned) h2);
2421 		} else
2422 #endif
2423 		    XClearArea(screen->display, VWindow(screen),
2424 			       x, y, (unsigned) w2, (unsigned) h2, False);
2425 		x += w2;
2426 		ww -= w2;
2427 	    }
2428 	    if (ww > 0) {
2429 		XFillRectangle(screen->display, draw, gc,
2430 			       x, y, (unsigned) ww, (unsigned) h2);
2431 	    }
2432 	    if (yy > vmark2) {
2433 		xtermClear2(xw, xb, vmark2, width, (unsigned) (yy - vmark2));
2434 	    }
2435 	} else {
2436 	    XFillRectangle(screen->display, draw, gc, x, y, width, height);
2437 	}
2438     } else {
2439 #if USE_DOUBLE_BUFFER
2440 	if (resource.buffered) {
2441 	    gc = FillerGC(xw, screen);
2442 	    XFillRectangle(screen->display, draw, gc,
2443 			   x, y, width, height);
2444 	} else
2445 #endif
2446 	    XClearArea(screen->display,
2447 		       VWindow(screen),
2448 		       x, y, width, height, False);
2449     }
2450 }
2451 
2452 /*
2453  * Called by the ExposeHandler to do the actual repaint after the coordinates
2454  * have been translated to allow for any CopyArea in progress.
2455  * The rectangle passed in is pixel coordinates.
2456  */
2457 static int
handle_translated_exposure(XtermWidget xw,int rect_x,int rect_y,int rect_width,int rect_height)2458 handle_translated_exposure(XtermWidget xw,
2459 			   int rect_x,
2460 			   int rect_y,
2461 			   int rect_width,
2462 			   int rect_height)
2463 {
2464     TScreen *screen = TScreenOf(xw);
2465     int toprow, leftcol, nrows, ncols;
2466     int x0, x1;
2467     int y0, y1;
2468     int result = 0;
2469 
2470     TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
2471 	   rect_y, rect_x, rect_height, rect_width));
2472 
2473     x0 = (rect_x - OriginX(screen));
2474     x1 = (x0 + rect_width);
2475 
2476     y0 = (rect_y - OriginY(screen));
2477     y1 = (y0 + rect_height);
2478 
2479     if ((x0 < 0 ||
2480 	 y0 < 0 ||
2481 	 x1 > Width(screen) ||
2482 	 y1 > Height(screen))) {
2483 	set_background(xw, -1);
2484 	xtermClear2(xw,
2485 		    rect_x,
2486 		    rect_y,
2487 		    (unsigned) rect_width,
2488 		    (unsigned) rect_height);
2489     }
2490     toprow = y0 / FontHeight(screen);
2491     if (toprow < 0)
2492 	toprow = 0;
2493 
2494     leftcol = x0 / FontWidth(screen);
2495     if (leftcol < 0)
2496 	leftcol = 0;
2497 
2498     nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
2499     ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
2500     toprow -= screen->scrolls;
2501     if (toprow < 0) {
2502 	nrows += toprow;
2503 	toprow = 0;
2504     }
2505     if (toprow + nrows > MaxRows(screen))
2506 	nrows = MaxRows(screen) - toprow;
2507     if (leftcol + ncols > MaxCols(screen))
2508 	ncols = MaxCols(screen) - leftcol;
2509 
2510     if (nrows > 0 && ncols > 0) {
2511 	ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
2512 	first_map_occurred();
2513 	if (screen->cur_row >= toprow &&
2514 	    screen->cur_row < toprow + nrows &&
2515 	    screen->cur_col >= leftcol &&
2516 	    screen->cur_col < leftcol + ncols) {
2517 	    result = 1;
2518 	}
2519 
2520     }
2521     TRACE(("...handle_translated_exposure %d\n", result));
2522     return (result);
2523 }
2524 
2525 /***====================================================================***/
2526 
2527 void
GetColors(XtermWidget xw,ScrnColors * pColors)2528 GetColors(XtermWidget xw, ScrnColors * pColors)
2529 {
2530     TScreen *screen = TScreenOf(xw);
2531     int n;
2532 
2533     pColors->which = 0;
2534     for (n = 0; n < NCOLORS; ++n) {
2535 	SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
2536     }
2537 }
2538 
2539 void
ChangeColors(XtermWidget xw,ScrnColors * pNew)2540 ChangeColors(XtermWidget xw, ScrnColors * pNew)
2541 {
2542     Bool repaint = False;
2543     TScreen *screen = TScreenOf(xw);
2544     VTwin *win = WhichVWin(screen);
2545 
2546     TRACE(("ChangeColors\n"));
2547 
2548     if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
2549 	T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
2550 	TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
2551 	FreeMarkGCs(xw);
2552 	/* no repaint needed */
2553     } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
2554 	       (COLOR_DEFINED(pNew, TEXT_FG))) {
2555 	if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) {
2556 	    T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
2557 	    TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
2558 	    if (screen->Vshow)
2559 		repaint = True;
2560 	}
2561 	FreeMarkGCs(xw);
2562     }
2563 
2564     if (COLOR_DEFINED(pNew, TEXT_FG)) {
2565 	Pixel fg = COLOR_VALUE(pNew, TEXT_FG);
2566 	T_COLOR(screen, TEXT_FG) = fg;
2567 	TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG)));
2568 	if (screen->Vshow) {
2569 	    setCgsFore(xw, win, gcNorm, fg);
2570 	    setCgsBack(xw, win, gcNormReverse, fg);
2571 	    setCgsFore(xw, win, gcBold, fg);
2572 	    setCgsBack(xw, win, gcBoldReverse, fg);
2573 	    repaint = True;
2574 	}
2575 	FreeMarkGCs(xw);
2576     }
2577 
2578     if (COLOR_DEFINED(pNew, TEXT_BG)) {
2579 	Pixel bg = COLOR_VALUE(pNew, TEXT_BG);
2580 	T_COLOR(screen, TEXT_BG) = bg;
2581 	TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG)));
2582 	if (screen->Vshow) {
2583 	    setCgsBack(xw, win, gcNorm, bg);
2584 	    setCgsFore(xw, win, gcNormReverse, bg);
2585 	    setCgsBack(xw, win, gcBold, bg);
2586 	    setCgsFore(xw, win, gcBoldReverse, bg);
2587 	    set_background(xw, -1);
2588 	    repaint = True;
2589 	}
2590     }
2591 #if OPT_HIGHLIGHT_COLOR
2592     if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
2593 	if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) {
2594 	    T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
2595 	    TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
2596 	    if (screen->Vshow)
2597 		repaint = True;
2598 	}
2599     }
2600     if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
2601 	if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) {
2602 	    T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
2603 	    TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
2604 	    if (screen->Vshow)
2605 		repaint = True;
2606 	}
2607     }
2608 #endif
2609 
2610     if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
2611 	if (COLOR_DEFINED(pNew, MOUSE_FG)) {
2612 	    T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
2613 	    TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
2614 	}
2615 	if (COLOR_DEFINED(pNew, MOUSE_BG)) {
2616 	    T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
2617 	    TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
2618 	}
2619 
2620 	if (screen->Vshow) {
2621 	    recolor_cursor(screen,
2622 			   screen->pointer_cursor,
2623 			   T_COLOR(screen, MOUSE_FG),
2624 			   T_COLOR(screen, MOUSE_BG));
2625 	    XDefineCursor(screen->display, VWindow(screen),
2626 			  screen->pointer_cursor);
2627 	}
2628 #if OPT_TEK4014
2629 	if (TEK4014_SHOWN(xw)) {
2630 	    TekScreen *tekscr = TekScreenOf(tekWidget);
2631 	    Window tekwin = TWindow(tekscr);
2632 	    if (tekwin) {
2633 		recolor_cursor(screen,
2634 			       tekscr->arrow,
2635 			       T_COLOR(screen, MOUSE_FG),
2636 			       T_COLOR(screen, MOUSE_BG));
2637 		XDefineCursor(screen->display, tekwin, tekscr->arrow);
2638 	    }
2639 	}
2640 #endif
2641 	/* no repaint needed */
2642     }
2643 
2644     if (COLOR_DEFINED(pNew, TEXT_FG) ||
2645 	COLOR_DEFINED(pNew, TEXT_BG) ||
2646 	COLOR_DEFINED(pNew, TEXT_CURSOR)) {
2647 	if (set_cursor_gcs(xw) && screen->Vshow) {
2648 	    repaint = True;
2649 	}
2650     }
2651 #if OPT_TEK4014
2652     if (COLOR_DEFINED(pNew, TEK_FG) ||
2653 	COLOR_DEFINED(pNew, TEK_BG)) {
2654 	ChangeTekColors(tekWidget, screen, pNew);
2655 	if (TEK4014_SHOWN(xw)) {
2656 	    TekRepaint(tekWidget);
2657 	}
2658     } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
2659 	ChangeTekColors(tekWidget, screen, pNew);
2660     }
2661 #endif
2662     if (repaint)
2663 	xtermRepaint(xw);
2664 }
2665 
2666 void
xtermClear(XtermWidget xw)2667 xtermClear(XtermWidget xw)
2668 {
2669     TScreen *screen = TScreenOf(xw);
2670 
2671     TRACE(("xtermClear\n"));
2672     xtermClear2(xw, 0, 0, FullWidth(screen), FullHeight(screen));
2673 }
2674 
2675 void
xtermRepaint(XtermWidget xw)2676 xtermRepaint(XtermWidget xw)
2677 {
2678     TScreen *screen = TScreenOf(xw);
2679 
2680     TRACE(("xtermRepaint\n"));
2681     xtermClear(xw);
2682     ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), True);
2683 }
2684 
2685 /***====================================================================***/
2686 
2687 Boolean
isDefaultForeground(const char * name)2688 isDefaultForeground(const char *name)
2689 {
2690     return (Boolean) !x_strcasecmp(name, XtDefaultForeground);
2691 }
2692 
2693 Boolean
isDefaultBackground(const char * name)2694 isDefaultBackground(const char *name)
2695 {
2696     return (Boolean) !x_strcasecmp(name, XtDefaultBackground);
2697 }
2698 
2699 #if OPT_WIDE_CHARS
2700 /*
2701  * Check for Unicode BIDI control characters, which may be miscategorized via
2702  * wcwidth() and iswprint() as zero-width printable characters.
2703  */
2704 Boolean
isWideControl(unsigned ch)2705 isWideControl(unsigned ch)
2706 {
2707     Boolean result;
2708 
2709     switch (ch) {
2710     case 0x200E:
2711     case 0x200F:
2712     case 0x202A:
2713     case 0x202B:
2714     case 0x202C:
2715     case 0x202D:
2716     case 0x202E:
2717 	result = True;
2718 	break;
2719     default:
2720 	result = False;
2721 	break;
2722     }
2723     return result;
2724 }
2725 #endif
2726 
2727 /***====================================================================***/
2728 
2729 typedef struct {
2730     Pixel fg;
2731     Pixel bg;
2732 } ToSwap;
2733 
2734 #if OPT_HIGHLIGHT_COLOR
2735 #define hc_param ,Bool hilite_color
2736 #define hc_value ,screen->hilite_color
2737 #else
2738 #define hc_param		/* nothing */
2739 #define hc_value		/* nothing */
2740 #endif
2741 
2742 /*
2743  * Use this to swap the foreground/background color values in the resource
2744  * data, and to build up a list of the pairs which must be swapped in the
2745  * GC cache.
2746  */
2747 static void
swapLocally(ToSwap * list,int * count,ColorRes * fg,ColorRes * bg hc_param)2748 swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
2749 {
2750     ColorRes tmp;
2751     Boolean found = False;
2752 
2753     Pixel fg_color = fg->value;
2754     Pixel bg_color = bg->value;
2755 
2756 #if OPT_HIGHLIGHT_COLOR
2757     if ((fg_color != bg_color) || !hilite_color)
2758 #endif
2759     {
2760 	int n;
2761 
2762 	EXCHANGE(*fg, *bg, tmp);
2763 	for (n = 0; n < *count; ++n) {
2764 	    if ((list[n].fg == fg_color && list[n].bg == bg_color)
2765 		|| (list[n].fg == bg_color && list[n].bg == fg_color)) {
2766 		found = True;
2767 		break;
2768 	    }
2769 	}
2770 	if (!found) {
2771 	    list[*count].fg = fg_color;
2772 	    list[*count].bg = bg_color;
2773 	    *count = *count + 1;
2774 	    TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
2775 		   fg_color, bg_color, *count));
2776 	}
2777     }
2778 }
2779 
2780 static void
reallySwapColors(XtermWidget xw,ToSwap * list,int count)2781 reallySwapColors(XtermWidget xw, ToSwap * list, int count)
2782 {
2783     int j, k;
2784 
2785     TRACE(("reallySwapColors\n"));
2786     for (j = 0; j < count; ++j) {
2787 	for_each_text_gc(k) {
2788 	    redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
2789 	}
2790     }
2791     FreeMarkGCs(xw);
2792 }
2793 
2794 static void
swapVTwinGCs(XtermWidget xw,VTwin * win)2795 swapVTwinGCs(XtermWidget xw, VTwin *win)
2796 {
2797     swapCgs(xw, win, gcNorm, gcNormReverse);
2798     swapCgs(xw, win, gcBold, gcBoldReverse);
2799 }
2800 
2801 void
ReverseVideo(XtermWidget xw)2802 ReverseVideo(XtermWidget xw)
2803 {
2804     TScreen *screen = TScreenOf(xw);
2805     ToSwap listToSwap[5];
2806     int numToSwap = 0;
2807 
2808     TRACE(("ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2809 
2810     /*
2811      * Swap SGR foreground and background colors.  By convention, these are
2812      * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
2813      * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
2814      * #7, respectively.
2815      *
2816      * We don't swap colors that happen to match the screen's foreground
2817      * and background because that tends to produce bizarre effects.
2818      */
2819 #define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
2820 #define swapAColor(a,b) swapAnyColor(Acolors, a, b)
2821     if_OPT_ISO_COLORS(screen, {
2822 	swapAColor(0, 7);
2823 	swapAColor(8, 15);
2824     });
2825 
2826     if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) {
2827 	T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
2828     }
2829 #define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
2830     swapTColor(TEXT_FG, TEXT_BG);
2831     swapTColor(MOUSE_FG, MOUSE_BG);
2832 
2833     reallySwapColors(xw, listToSwap, numToSwap);
2834 
2835     swapVTwinGCs(xw, &(screen->fullVwin));
2836 #ifndef NO_ACTIVE_ICON
2837     swapVTwinGCs(xw, &(screen->iconVwin));
2838 #endif /* NO_ACTIVE_ICON */
2839 
2840     xw->misc.re_verse = (Boolean) !xw->misc.re_verse;
2841     TRACE(("...swapping done, set ReverseVideo %s\n", BtoS(xw->misc.re_verse)));
2842 
2843     if (XtIsRealized((Widget) xw)) {
2844 	xtermDisplayPointer(xw);
2845     }
2846 #if OPT_TEK4014
2847     if (TEK4014_SHOWN(xw)) {
2848 	TekScreen *tekscr = TekScreenOf(tekWidget);
2849 	Window tekwin = TWindow(tekscr);
2850 	recolor_cursor(screen,
2851 		       tekscr->arrow,
2852 		       T_COLOR(screen, MOUSE_FG),
2853 		       T_COLOR(screen, MOUSE_BG));
2854 	XDefineCursor(screen->display, tekwin, tekscr->arrow);
2855     }
2856 #endif
2857 
2858     if (screen->scrollWidget)
2859 	ScrollBarReverseVideo(screen->scrollWidget);
2860 
2861     if (XtIsRealized((Widget) xw)) {
2862 	set_background(xw, -1);
2863     }
2864 #if OPT_TEK4014
2865     TekReverseVideo(xw, tekWidget);
2866 #endif
2867     if (XtIsRealized((Widget) xw)) {
2868 	xtermRepaint(xw);
2869     }
2870 #if OPT_TEK4014
2871     if (TEK4014_SHOWN(xw)) {
2872 	TekRepaint(tekWidget);
2873     }
2874 #endif
2875     ReverseOldColors(xw);
2876     set_cursor_gcs(xw);
2877     update_reversevideo();
2878     TRACE(("...ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2879 }
2880 
2881 void
recolor_cursor(TScreen * screen,Cursor cursor,unsigned long fg,unsigned long bg)2882 recolor_cursor(TScreen *screen,
2883 	       Cursor cursor,	/* X cursor ID to set */
2884 	       unsigned long fg,	/* pixel indexes to look up */
2885 	       unsigned long bg)	/* pixel indexes to look up */
2886 {
2887     Display *dpy = screen->display;
2888     XColor colordefs[2];	/* 0 is foreground, 1 is background */
2889 
2890     colordefs[0].pixel = fg;
2891     colordefs[1].pixel = bg;
2892     XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
2893 		 colordefs, 2);
2894     XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
2895     cleanup_colored_cursor();
2896     return;
2897 }
2898 
2899 #if OPT_RENDERFONT
2900 #define XFT_CACHE_LIMIT ((unsigned)(~0) >> 1)
2901 #define XFT_CACHE_SIZE  16
2902 typedef struct {
2903     XftColor color;
2904     unsigned use;
2905 } XftColorCache;
2906 
2907 static int
compare_xft_color_cache(const void * a,const void * b)2908 compare_xft_color_cache(const void *a, const void *b)
2909 {
2910     return (int) (((const XftColorCache *) a)->use -
2911 		  ((const XftColorCache *) b)->use);
2912 }
2913 
2914 static XftColor *
getXftColor(XtermWidget xw,Pixel pixel)2915 getXftColor(XtermWidget xw, Pixel pixel)
2916 {
2917     static XftColorCache cache[XFT_CACHE_SIZE + 1];
2918     static unsigned latest_use;
2919     int i;
2920     int oldest;
2921     unsigned oldest_use;
2922     XColor color;
2923     Boolean found = False;
2924 
2925     oldest_use = XFT_CACHE_LIMIT;
2926     oldest = 0;
2927     if (latest_use == XFT_CACHE_LIMIT) {
2928 	latest_use = 0;
2929 	qsort(cache, (size_t) XFT_CACHE_SIZE, sizeof(XftColorCache), compare_xft_color_cache);
2930 	for (i = 0; i < XFT_CACHE_SIZE; i++) {
2931 	    if (cache[i].use) {
2932 		cache[i].use = ++latest_use;
2933 	    }
2934 	}
2935     }
2936     for (i = 0; i < XFT_CACHE_SIZE; i++) {
2937 	if (cache[i].use) {
2938 	    if (cache[i].color.pixel == pixel) {
2939 		found = True;
2940 		break;
2941 	    }
2942 	}
2943 	if (cache[i].use < oldest_use) {
2944 	    oldest_use = cache[i].use;
2945 	    oldest = i;
2946 	}
2947     }
2948     if (!found) {
2949 	i = oldest;
2950 	color.pixel = pixel;
2951 	(void) QueryOneColor(xw, &color);
2952 	cache[i].color.color.red = color.red;
2953 	cache[i].color.color.green = color.green;
2954 	cache[i].color.color.blue = color.blue;
2955 	cache[i].color.color.alpha = 0xffff;
2956 	cache[i].color.pixel = pixel;
2957     }
2958     cache[i].use = ++latest_use;
2959     return &cache[i].color;
2960 }
2961 
2962 /*
2963  * The cell-width is related to, but not the same as the wide-character width.
2964  * We will only get useful values from wcwidth() for codes above 255.
2965  * Otherwise, interpret according to internal data.
2966  */
2967 #if OPT_RENDERWIDE
2968 
2969 #if OPT_C1_PRINT
2970 #define XtermCellWidth(xw, ch) \
2971 	(((ch) == 0 || (ch) == 127) \
2972 	  ? 0 \
2973 	  : (((ch) < 256) \
2974 	      ? (((ch) >= 128 && (ch) < 160) \
2975 		  ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
2976 		  : 1) \
2977 	      : CharWidth(TScreenOf(xw), ch)))
2978 #else
2979 #define XtermCellWidth(xw, ch) \
2980 	(((ch) == 0 || (ch) == 127) \
2981 	  ? 0 \
2982 	  : (((ch) < 256) \
2983 	      ? 1 \
2984 	      : CharWidth(TScreenOf(xw), ch)))
2985 #endif
2986 
2987 #endif /* OPT_RENDERWIDE */
2988 
2989 #define XFT_FONT(which) getXftFont(params->xw, which, fontnum)
2990 
2991 #if OPT_ISO_COLORS
2992 #define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
2993 #else
2994 #define UseBoldFont(screen) 1
2995 #endif
2996 
2997 #if OPT_RENDERWIDE
2998 /*
2999  * Find Xft (truetype) double-width font for the given normal/bold attributes.
3000  */
3001 static XftFont *
getWideXftFont(XTermDraw * params,unsigned attr_flags)3002 getWideXftFont(XTermDraw * params,
3003 	       unsigned attr_flags)
3004 {
3005     TScreen *screen = TScreenOf(params->xw);
3006     int fontnum = screen->menu_font_number;
3007     XftFont *wfont = 0;
3008 
3009 #if OPT_WIDE_ATTRS
3010     if ((attr_flags & ATR_ITALIC)
3011 #if OPT_ISO_COLORS
3012 	&& !screen->colorITMode
3013 #endif
3014 	) {
3015 	if ((attr_flags & BOLDATTR(screen))
3016 	    && UseBoldFont(screen)
3017 	    && XFT_FONT(fWBtal)) {
3018 	    wfont = XFT_FONT(fWBtal);
3019 	} else if (XFT_FONT(fWItal)) {
3020 	    wfont = XFT_FONT(fWItal);
3021 	}
3022     }
3023     if (wfont != 0) {
3024 	;			/* skip the other tests */
3025     } else
3026 #endif
3027 #if OPT_ISO_COLORS
3028 	if ((attr_flags & UNDERLINE)
3029 	    && !screen->colorULMode
3030 	    && screen->italicULMode
3031 	    && XFT_FONT(fWItal)) {
3032 	wfont = XFT_FONT(fWItal);
3033     } else
3034 #endif
3035 	if ((attr_flags & BOLDATTR(screen))
3036 	    && UseBoldFont(screen)
3037 	    && XFT_FONT(fWBold)) {
3038 	wfont = XFT_FONT(fWBold);
3039     } else {
3040 	wfont = XFT_FONT(fWide);
3041     }
3042     return wfont;
3043 }
3044 #endif /* OPT_RENDERWIDE */
3045 
3046 /*
3047  * Find Xft (truetype) single-width font for the given normal/bold attributes.
3048  */
3049 static XftFont *
getNormXftFont(XTermDraw * params,unsigned attr_flags,Bool * did_ul)3050 getNormXftFont(XTermDraw * params,
3051 	       unsigned attr_flags,
3052 	       Bool *did_ul)
3053 {
3054     TScreen *screen = TScreenOf(params->xw);
3055     int fontnum = screen->menu_font_number;
3056     XftFont *font = 0;
3057 
3058     (void) did_ul;
3059 #if OPT_DEC_CHRSET
3060     if (CSET_DOUBLE(params->real_chrset)) {
3061 	font = xterm_DoubleFT(params, params->real_chrset, attr_flags);
3062     }
3063     if (font != 0) {
3064 	;			/* found a usable double-sized font */
3065     } else
3066 #endif
3067 #if OPT_WIDE_ATTRS
3068 	if ((attr_flags & ATR_ITALIC)
3069 #if OPT_ISO_COLORS
3070 	    && !screen->colorITMode
3071 #endif
3072 	) {
3073 	if ((attr_flags & BOLDATTR(screen))
3074 	    && UseBoldFont(screen)
3075 	    && XFT_FONT(fBtal)) {
3076 	    font = XFT_FONT(fBtal);
3077 	} else if (XFT_FONT(fItal)) {
3078 	    font = XFT_FONT(fItal);
3079 	}
3080     }
3081     if (font != 0) {
3082 	;			/* skip the other tests */
3083     } else
3084 #endif
3085 #if OPT_ISO_COLORS
3086 	if ((attr_flags & UNDERLINE)
3087 	    && !screen->colorULMode
3088 	    && screen->italicULMode
3089 	    && XFT_FONT(fItal)) {
3090 	font = XFT_FONT(fItal);
3091 	*did_ul = True;
3092     } else
3093 #endif
3094 	if ((attr_flags & BOLDATTR(screen))
3095 	    && UseBoldFont(screen)
3096 	    && XFT_FONT(fBold)) {
3097 	font = XFT_FONT(fBold);
3098     } else {
3099 	font = XFT_FONT(fNorm);
3100     }
3101     return font;
3102 }
3103 
3104 #if OPT_RENDERWIDE
3105 #define pickXftFont(width, nf, wf) ((width == 2 && wf != 0) ? wf : nf)
3106 #else
3107 #define pickXftFont(width, nf, wf) (nf)
3108 #endif
3109 
3110 /*
3111  * fontconfig/Xft combination prior to 2.2 has a problem with
3112  * CJK truetype 'double-width' (bi-width/monospace) fonts leading
3113  * to the 's p a c e d o u t' rendering. Consequently, we can't
3114  * rely on XftDrawString8/16 when one of those fonts is used.
3115  * Instead, we need to roll out our own using XftDrawCharSpec.
3116  * A patch in the same spirit (but in a rather different form)
3117  * was applied to gnome vte and gtk2 port of vim.
3118  * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
3119  */
3120 static int
xtermXftDrawString(XTermDraw * params,unsigned attr_flags,XftColor * color,XftFont * font,int x,int y,const IChar * text,Cardinal len,Bool really)3121 xtermXftDrawString(XTermDraw * params,
3122 		   unsigned attr_flags,
3123 		   XftColor *color,
3124 		   XftFont *font,
3125 		   int x,
3126 		   int y,
3127 		   const IChar *text,
3128 		   Cardinal len,
3129 		   Bool really)
3130 {
3131     TScreen *screen = TScreenOf(params->xw);
3132     int ncells = 0;
3133 
3134     (void) attr_flags;
3135     if (len != 0) {
3136 #if OPT_RENDERWIDE
3137 	XftCharSpec *sbuf;
3138 	XftFont *wfont = getWideXftFont(params, attr_flags);
3139 	Cardinal src, dst;
3140 	XftFont *lastFont = 0;
3141 	XftFont *currFont = 0;
3142 	Cardinal start = 0;
3143 	int charWidth;
3144 	int fwidth = FontWidth(screen);
3145 #if OPT_DEC_CHRSET
3146 	Boolean forceDbl = CSET_DOUBLE(params->real_chrset);
3147 #else
3148 	Boolean forceDbl = False;
3149 #endif
3150 
3151 	BumpTypedBuffer(XftCharSpec, 2 * len);
3152 	sbuf = BfBuf(XftCharSpec);
3153 
3154 	for (src = dst = 0; src < len; src++) {
3155 	    FcChar32 wc = *text++;
3156 
3157 	    charWidth = XtermCellWidth(params->xw, (wchar_t) wc);
3158 	    if (charWidth < 0)
3159 		continue;
3160 
3161 	    sbuf[dst].ucs4 = wc;
3162 	    sbuf[dst].x = (short) (x + fwidth * ncells);
3163 	    sbuf[dst].y = (short) (y);
3164 
3165 	    currFont = pickXftFont(charWidth, font, wfont);
3166 	    ncells += charWidth;
3167 
3168 	    if (lastFont != currFont) {
3169 		if ((lastFont != 0) && really) {
3170 		    XftDrawCharSpec(screen->renderDraw,
3171 				    color,
3172 				    lastFont,
3173 				    sbuf + start,
3174 				    (int) (dst - start));
3175 		}
3176 		start = dst;
3177 		lastFont = currFont;
3178 	    }
3179 	    ++dst;
3180 
3181 	    if (forceDbl && charWidth < 2) {
3182 		sbuf[dst].ucs4 = ' ';
3183 		sbuf[dst].x = (short) (x + fwidth * ncells);
3184 		sbuf[dst].y = (short) (y);
3185 		++dst;
3186 		ncells += charWidth;
3187 	    }
3188 	}
3189 	if ((dst != start) && really) {
3190 	    XftDrawCharSpec(screen->renderDraw,
3191 			    color,
3192 			    lastFont,
3193 			    sbuf + start,
3194 			    (int) (dst - start));
3195 	}
3196 #else /* !OPT_RENDERWIDE */
3197 	if (really) {
3198 	    XftChar8 *buffer;
3199 	    int dst;
3200 
3201 	    BumpTypedBuffer(XftChar8, len);
3202 	    buffer = BfBuf(XftChar8);
3203 
3204 	    for (dst = 0; dst < (int) len; ++dst)
3205 		buffer[dst] = CharOf(text[dst]);
3206 
3207 	    XftDrawString8(screen->renderDraw,
3208 			   color,
3209 			   font,
3210 			   x, y, buffer, (int) len);
3211 	}
3212 	ncells = (int) len;
3213 #endif
3214 	xtermNeedSwap(params->xw, 1);
3215     }
3216     return ncells;
3217 }
3218 #define xtermXftWidth(params, attr_flags, color, font, x, y, chars, len) \
3219    xtermXftDrawString(params, attr_flags, color, font, x, y, chars, len, False)
3220 #endif /* OPT_RENDERFONT */
3221 
3222 #if OPT_WIDE_CHARS
3223 /*
3224  * Map characters commonly "fixed" by groff back to their ASCII equivalents.
3225  * Also map other useful equivalents.
3226  */
3227 unsigned
AsciiEquivs(unsigned ch)3228 AsciiEquivs(unsigned ch)
3229 {
3230     switch (ch) {
3231     case 0x2010:		/* groff "-" */
3232     case 0x2011:
3233     case 0x2012:
3234     case 0x2013:
3235     case 0x2014:
3236     case 0x2015:
3237     case 0x2212:		/* groff "\-" */
3238 	ch = '-';
3239 	break;
3240     case 0x2018:		/* groff "`" */
3241 	ch = '`';
3242 	break;
3243     case 0x2019:		/* groff ' */
3244 	ch = '\'';
3245 	break;
3246     case 0x201C:		/* groff lq */
3247     case 0x201D:		/* groff rq */
3248 	ch = '"';
3249 	break;
3250     case 0x2329:		/* groff ".URL" */
3251 	ch = '<';
3252 	break;
3253     case 0x232a:		/* groff ".URL" */
3254 	ch = '>';
3255 	break;
3256     default:
3257 	if (ch >= 0xff01 && ch <= 0xff5e) {
3258 	    /* "Fullwidth" codes (actually double-width) */
3259 	    ch -= 0xff00;
3260 	    ch += ANSI_SPA;
3261 	    break;
3262 	}
3263     }
3264     return ch;
3265 }
3266 
3267 /*
3268  * Actually this should be called "groff_workaround()" - for the places where
3269  * groff stomps on compatibility.  Still, if enough people get used to it,
3270  * this might someday become a quasi-standard.
3271  */
3272 #if OPT_BOX_CHARS
3273 static int
ucs_workaround(XTermDraw * params,unsigned ch,GC gc,int x,int y)3274 ucs_workaround(XTermDraw * params,
3275 	       unsigned ch,
3276 	       GC gc,
3277 	       int x,
3278 	       int y)
3279 {
3280     TScreen *screen = TScreenOf(params->xw);
3281     int fixed = False;
3282 
3283     if (screen->wide_chars && screen->utf8_mode && ch > 256) {
3284 	IChar eqv = (IChar) AsciiEquivs(ch);
3285 
3286 	if (eqv != (IChar) ch) {
3287 	    int width = CharWidth(screen, ch);
3288 
3289 	    do {
3290 		drawXtermText(params,
3291 			      gc,
3292 			      x,
3293 			      y,
3294 			      &eqv,
3295 			      1);
3296 		x += FontWidth(screen);
3297 		eqv = BAD_ASCII;
3298 	    } while (width-- > 1);
3299 
3300 	    fixed = True;
3301 	} else if (ch == HIDDEN_CHAR) {
3302 	    fixed = True;
3303 	}
3304     }
3305     return fixed;
3306 }
3307 #endif /* OPT_BOX_CHARS */
3308 #endif /* OPT_WIDE_CHARS */
3309 
3310 /*
3311  * Use this when the characters will not fill the cell area properly.  Fill the
3312  * area where we'll write the characters, otherwise we'll get gaps between
3313  * them, e.g., in the original background color.
3314  *
3315  * The cursor is a special case, because the XFillRectangle call only uses the
3316  * foreground, while we've set the cursor color in the background.  So we need
3317  * a special GC for that.
3318  */
3319 static void
xtermFillCells(XTermDraw * params,GC gc,int x,int y,Cardinal len)3320 xtermFillCells(XTermDraw * params,
3321 	       GC gc,
3322 	       int x,
3323 	       int y,
3324 	       Cardinal len)
3325 {
3326     TScreen *screen = TScreenOf(params->xw);
3327     VTwin *currentWin = WhichVWin(screen);
3328 
3329     if (!(params->draw_flags & NOBACKGROUND)) {
3330 	CgsEnum srcId = getCgsId(params->xw, currentWin, gc);
3331 	CgsEnum dstId = gcMAX;
3332 	Pixel fg = getCgsFore(params->xw, currentWin, gc);
3333 	Pixel bg = getCgsBack(params->xw, currentWin, gc);
3334 
3335 	switch (srcId) {
3336 	case gcVTcursNormal:
3337 	case gcVTcursReverse:
3338 	    dstId = gcVTcursOutline;
3339 	    break;
3340 	case gcVTcursFilled:
3341 	case gcVTcursOutline:
3342 	    /* FIXME */
3343 	    break;
3344 	case gcNorm:
3345 	    dstId = gcNormReverse;
3346 	    break;
3347 	case gcNormReverse:
3348 	    dstId = gcNorm;
3349 	    break;
3350 	case gcBold:
3351 	    dstId = gcBoldReverse;
3352 	    break;
3353 	case gcBoldReverse:
3354 	    dstId = gcBold;
3355 	    break;
3356 	case gcBorder:
3357 	case gcFiller:
3358 	    dstId = srcId;
3359 	    break;
3360 #if OPT_BOX_CHARS
3361 	case gcLine:
3362 	case gcDots:
3363 	    /* FIXME */
3364 	    break;
3365 #endif
3366 #if OPT_DEC_CHRSET
3367 	case gcCNorm:
3368 	case gcCBold:
3369 	    /* FIXME */
3370 	    break;
3371 #endif
3372 #if OPT_WIDE_CHARS
3373 	case gcWide:
3374 	    dstId = gcWideReverse;
3375 	    break;
3376 	case gcWBold:
3377 	    dstId = gcBoldReverse;
3378 	    break;
3379 	case gcWideReverse:
3380 	case gcWBoldReverse:
3381 	    /* FIXME */
3382 	    break;
3383 #endif
3384 #if OPT_TEK4014
3385 	case gcTKcurs:
3386 	    /* FIXME */
3387 	    break;
3388 #endif
3389 	case gcMAX:
3390 	    break;
3391 	}
3392 
3393 	if (dstId != gcMAX) {
3394 	    setCgsFore(params->xw, currentWin, dstId, bg);
3395 	    setCgsBack(params->xw, currentWin, dstId, fg);
3396 
3397 	    XFillRectangle(screen->display, VDrawable(screen),
3398 			   getCgsGC(params->xw, currentWin, dstId),
3399 			   x, y,
3400 			   len * (Cardinal) FontWidth(screen),
3401 			   (unsigned) FontHeight(screen));
3402 	}
3403     }
3404 }
3405 
3406 #if OPT_TRACE
3407 static void
xtermSetClipRectangles(Display * dpy,GC gc,int x,int y,XRectangle * rp,Cardinal nr,int order)3408 xtermSetClipRectangles(Display *dpy,
3409 		       GC gc,
3410 		       int x,
3411 		       int y,
3412 		       XRectangle * rp,
3413 		       Cardinal nr,
3414 		       int order)
3415 {
3416 #if 0
3417     TScreen *screen = TScreenOf(term);
3418     Drawable draw = VDrawable(screen);
3419 
3420     XSetClipMask(dpy, gc, None);
3421     XDrawRectangle(screen->display, draw, gc,
3422 		   x + rp->x - 1,
3423 		   y + rp->y - 1,
3424 		   rp->width,
3425 		   rp->height);
3426 #endif
3427 
3428     XSetClipRectangles(dpy, gc,
3429 		       x, y, rp, (int) nr, order);
3430     TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
3431 	   y, x,
3432 	   rp->y, rp->x, rp->height, rp->width));
3433 }
3434 
3435 #else
3436 #define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
3437 	    XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
3438 #endif
3439 
3440 #if OPT_CLIP_BOLD
3441 /*
3442  * This special case is a couple of percent slower, but avoids a lot of pixel
3443  * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
3444  */
3445 #define beginClipping(screen,gc,pwidth,plength) \
3446 	if (pwidth > 2) { \
3447 	    if (screen->use_clipping) { \
3448 		XRectangle clip; \
3449 		int clip_x = x; \
3450 		int clip_y = y - FontHeight(screen) + FontDescent(screen); \
3451 		clip.x = 0; \
3452 		clip.y = 0; \
3453 		clip.height = (unsigned short) FontHeight(screen); \
3454 		clip.width = (unsigned short) ((pwidth) * (plength)); \
3455 		xtermSetClipRectangles(screen->display, gc, \
3456 				       clip_x, clip_y, \
3457 				       &clip, 1, Unsorted); \
3458 	    } else if (screen->use_border_clipping) { \
3459 		XRectangle clip; \
3460 		clip.x = 0; \
3461 		clip.y = 0; \
3462 		clip.height = (unsigned short) Height(screen); \
3463 		clip.width = (unsigned short) Width(screen); \
3464 		xtermSetClipRectangles(screen->display, gc, \
3465 				       0, 0, \
3466 				       &clip, 1, Unsorted); \
3467 	    } \
3468 	}
3469 #define endClipping(screen,gc) \
3470 	    XSetClipMask(screen->display, gc, None)
3471 #else
3472 #define beginClipping(screen,gc,pwidth,plength)		/* nothing */
3473 #define endClipping(screen,gc)	/* nothing */
3474 #endif /* OPT_CLIP_BOLD */
3475 
3476 #if OPT_RENDERFONT
3477 static int
drawClippedXftString(XTermDraw * params,unsigned attr_flags,XftFont * font,XftColor * fg_color,int x,int y,const IChar * text,Cardinal len)3478 drawClippedXftString(XTermDraw * params,
3479 		     unsigned attr_flags,
3480 		     XftFont *font,
3481 		     XftColor *fg_color,
3482 		     int x,
3483 		     int y,
3484 		     const IChar *text,
3485 		     Cardinal len)
3486 {
3487     int ncells = xtermXftWidth(params, attr_flags,
3488 			       fg_color,
3489 			       font, x, y,
3490 			       text,
3491 			       len);
3492     TScreen *screen = TScreenOf(params->xw);
3493     int fontHigh = FontHeight(screen);
3494     int fontWide = FontWidth(screen);
3495 
3496     if (fontWide > 2) {
3497 	int plength = (ncells ? ncells : 1);
3498 	Boolean halfHigh = False;
3499 #if OPT_DEC_CHRSET
3500 	Boolean halfWide = False;
3501 
3502 	switch (params->real_chrset) {
3503 	case CSET_SWL:
3504 	    break;
3505 	case CSET_DWL:
3506 	    halfWide = True;
3507 	    break;
3508 	case CSET_DHL_TOP:
3509 	    halfHigh = True;
3510 	    halfWide = True;
3511 	    break;
3512 	case CSET_DHL_BOT:
3513 	    halfHigh = True;
3514 	    halfWide = True;
3515 	    break;
3516 	}
3517 	if (CSET_DOUBLE(params->real_chrset)) {
3518 	    fontHigh = font->height;
3519 	    fontWide = font->max_advance_width;
3520 	    /* check if this is really a double-height font */
3521 	    if (halfHigh) {
3522 		int wantHigh = (int) (FontHeight(screen) * 1.8);
3523 		halfHigh = (fontHigh >= wantHigh);
3524 		TRACE(("comparing fontHigh %d/%d vs %d:"
3525 		       " double-height %s for %s\n",
3526 		       fontHigh, FontHeight(screen),
3527 		       wantHigh, BtoS(halfHigh),
3528 		       visibleDblChrset(params->real_chrset)));
3529 	    }
3530 	    fontHigh = (halfHigh
3531 			? (2 * FontHeight(screen))
3532 			: FontHeight(screen));
3533 	    /* check if this is really a double-width font */
3534 	    if (halfWide) {
3535 		int wantWide = (int) (FontWidth(screen) * 1.8);
3536 		halfWide = (fontWide >= wantWide);
3537 		TRACE(("comparing fontWide %d/%d vs %d:"
3538 		       " double-width %s for %s\n",
3539 		       fontWide, FontWidth(screen),
3540 		       wantWide, BtoS(halfWide),
3541 		       visibleDblChrset(params->real_chrset)));
3542 	    }
3543 	    fontWide = (halfWide
3544 			? (2 * FontWidth(screen))
3545 			: FontWidth(screen));
3546 	}
3547 #endif
3548 	if (screen->use_clipping || halfHigh) {
3549 	    XRectangle clip;
3550 	    double adds = ((double) screen->scale_height - 1.0f) * fontHigh;
3551 	    int height = dimRound(adds + fontHigh);
3552 	    int descnt = dimRound(adds / 2.0) + FontDescent(screen);
3553 	    int clip_x = (x);
3554 	    int clip_y = (y) - height + descnt;
3555 
3556 	    clip.x = 0;
3557 	    clip.y = 0;
3558 	    clip.height = (Dimension) height;
3559 	    clip.width = (Dimension) (fontWide * (Dimension) (plength));
3560 
3561 #if OPT_DEC_CHRSET
3562 	    if (halfHigh) {
3563 		int adjust;
3564 
3565 		clip.height = (unsigned short) FontHeight(screen);
3566 		clip_y += descnt;
3567 		if (params->real_chrset == CSET_DHL_BOT) {
3568 		    y -= clip.height;
3569 		}
3570 		adjust = ((clip_y - OriginY(screen)) % FontHeight(screen));
3571 		if (adjust) {
3572 		    if (adjust > FontHeight(screen) / 2)
3573 			adjust -= FontHeight(screen);
3574 		    clip_y -= adjust;
3575 		}
3576 	    }
3577 #endif
3578 	    XftDrawSetClipRectangles(screen->renderDraw,
3579 				     clip_x, clip_y,
3580 				     &clip, 1);
3581 	} else if (screen->use_border_clipping) {
3582 	    XRectangle clip;
3583 
3584 	    clip.x = (Position) OriginX(screen);
3585 	    clip.y = (Position) OriginY(screen);
3586 	    clip.height = (Dimension) Height(screen);
3587 	    clip.width = (Dimension) Width(screen);
3588 
3589 	    XftDrawSetClipRectangles(screen->renderDraw,
3590 				     0, 0,
3591 				     &clip, 1);
3592 	}
3593     }
3594 
3595     xtermXftDrawString(params, attr_flags,
3596 		       fg_color,
3597 		       font, x, y + ScaleShift(screen),
3598 		       text,
3599 		       len,
3600 		       True);
3601     XftDrawSetClip(screen->renderDraw, 0);
3602     return ncells;
3603 }
3604 #endif
3605 
3606 #ifndef NO_ACTIVE_ICON
3607 #define WhichVFontData(screen,name) \
3608 		(IsIcon(screen) ? getIconicFont(screen) \
3609 				: GetNormalFont(screen, name))
3610 #else
3611 #define WhichVFontData(screen,name) \
3612 				GetNormalFont(screen, name)
3613 #endif
3614 
3615 static void
drawUnderline(XtermWidget xw,GC gc,unsigned attr_flags,unsigned underline_len,int font_width,int x,int y,Bool did_ul)3616 drawUnderline(XtermWidget xw,
3617 	      GC gc,
3618 	      unsigned attr_flags,
3619 	      unsigned underline_len,
3620 	      int font_width,
3621 	      int x,
3622 	      int y,
3623 	      Bool did_ul)
3624 {
3625     TScreen *screen = TScreenOf(xw);
3626 
3627     if (screen->underline && !did_ul) {
3628 	int repeat = 0;
3629 	int descent = FontDescent(screen);
3630 	int length = x + (int) underline_len * font_width - 1;
3631 
3632 #if OPT_WIDE_ATTRS
3633 	if ((attr_flags & ATR_STRIKEOUT)) {
3634 	    int where = y - ((3 * FontAscent(screen)) / 8);
3635 	    XDrawLine(screen->display, VDrawable(screen), gc,
3636 		      x, where,
3637 		      length,
3638 		      where);
3639 	}
3640 	if ((attr_flags & ATR_DBL_UNDER)) {
3641 	    repeat = 2;
3642 	} else
3643 #endif
3644 	if ((attr_flags & UNDERLINE)) {
3645 	    repeat = 1;
3646 	}
3647 	while (repeat-- > 0) {
3648 	    if (descent-- > 1)
3649 		y++;
3650 	    XDrawLine(screen->display, VDrawable(screen), gc,
3651 		      x, y,
3652 		      length,
3653 		      y);
3654 	}
3655     }
3656 }
3657 
3658 #if OPT_WIDE_ATTRS
3659 /*
3660  * As a special case, we are currently allowing italic fonts to be inexact
3661  * matches for the normal font's size.  That introduces a problem:  either the
3662  * ascent or descent may be shorter, leaving a gap that has to be filled in.
3663  * Or they may be larger, requiring clipping.  Check for both cases.
3664  */
3665 static int
fixupItalics(XTermDraw * params,GC gc,XTermFonts * curFont,int y,int x,int font_width,Cardinal len)3666 fixupItalics(XTermDraw * params,
3667 	     GC gc,
3668 	     XTermFonts * curFont,
3669 	     int y, int x,
3670 	     int font_width,
3671 	     Cardinal len)
3672 {
3673     TScreen *screen = TScreenOf(params->xw);
3674     VTwin *cgsWin = WhichVWin(screen);
3675     XFontStruct *realFp = curFont->fs;
3676     XFontStruct *thisFp = getCgsFont(params->xw, cgsWin, gc)->fs;
3677     int need_clipping = 0;
3678     int need_filling = 0;
3679 
3680     if (thisFp->ascent > realFp->ascent)
3681 	need_clipping = 1;
3682     else if (thisFp->ascent < realFp->ascent)
3683 	need_filling = 1;
3684 
3685     if (thisFp->descent > realFp->descent)
3686 	need_clipping = 1;
3687     else if (thisFp->descent < realFp->descent)
3688 	need_filling = 1;
3689 
3690     if (need_clipping) {
3691 	beginClipping(screen, gc, font_width, (int) len);
3692     }
3693     if (need_filling) {
3694 	xtermFillCells(params,
3695 		       gc,
3696 		       x,
3697 		       y - realFp->ascent,
3698 		       len);
3699     }
3700     return need_clipping;
3701 }
3702 #endif
3703 
3704 #if OPT_DEC_CHRSET
3705 static int
fakeDoubleChars(XTermDraw * params,GC gc,int y,int x,const IChar * text,Cardinal len)3706 fakeDoubleChars(XTermDraw * params,
3707 		GC gc,
3708 		int y,
3709 		int x,
3710 		const IChar *text,
3711 		Cardinal len)
3712 {
3713     unsigned need = 2 * len;
3714     IChar *temp = TypeMallocN(IChar, need);
3715 
3716     if (temp != 0) {
3717 	unsigned n = 0;
3718 	XTermDraw recur = *params;
3719 
3720 	recur.this_chrset = CSET_SWL;
3721 
3722 	while (len--) {
3723 	    temp[n++] = *text++;
3724 	    temp[n++] = ' ';
3725 	}
3726 	x = drawXtermText(&recur,
3727 			  gc,
3728 			  x, y,
3729 			  temp,
3730 			  n);
3731 	free(temp);
3732     }
3733     return x;
3734 }
3735 #endif /* OPT_DEC_CHRSET */
3736 
3737 #define SetMissing(tag) \
3738 	TRACE(("%s %s: missing %d %04X\n", __FILE__, tag, missing, ch)); \
3739 	missing = 1
3740 
3741 #define MaxImageString 255
3742 
3743 /*
3744  * Draws text with the specified combination of bold/underline.  The return
3745  * value is the updated x position.
3746  */
3747 int
drawXtermText(XTermDraw * params,GC gc,int start_x,int start_y,const IChar * text,Cardinal len)3748 drawXtermText(XTermDraw * params,
3749 	      GC gc,
3750 	      int start_x,
3751 	      int start_y,
3752 	      const IChar *text,
3753 	      Cardinal len)
3754 {
3755     XTermDraw recur = *params;
3756     TScreen *screen = TScreenOf(recur.xw);
3757     int x = start_x;
3758     int y = start_y;
3759     int y_shift = ScaleShift(screen);
3760     Cardinal real_length = len;
3761     Cardinal underline_len = 0;
3762     /* Intended width of the font to draw (as opposed to the actual width of
3763        the X font, and the width of the default font) */
3764     int font_width = (((recur.draw_flags & DOUBLEWFONT) ? 2 : 1)
3765 		      * screen->fnt_wide);
3766     Bool did_ul = False;
3767     XTermFonts *curFont;
3768 #if OPT_WIDE_ATTRS
3769     int need_clipping = 0;
3770 #endif
3771 
3772 #if OPT_WIDE_CHARS
3773     if (text == 0)
3774 	return 0;
3775 #endif
3776     TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
3777 	   screen->cursor_state == OFF ? ' ' : '*',
3778 	   y, x, recur.this_chrset, len,
3779 	   visibleIChars(text, len)));
3780 
3781 #if OPT_DEC_CHRSET
3782     if (CSET_DOUBLE(recur.this_chrset)) {
3783 	/* We could try drawing double-size characters in the icon, but
3784 	 * given that the icon font is usually nil or nil2, there
3785 	 * doesn't seem to be much point.
3786 	 */
3787 	int inx = 0;
3788 	GC gc2;
3789 
3790 #if OPT_RENDERFONT
3791 	if (UsingRenderFont(recur.xw)) {
3792 	    if (!IsIcon(screen) && screen->font_doublesize) {
3793 		TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
3794 		recur.real_chrset = recur.this_chrset;
3795 		recur.this_chrset = CSET_SWL;
3796 		x = drawXtermText(&recur,
3797 				  gc,
3798 				  x, y,
3799 				  text,
3800 				  len);
3801 		x += ((int) len) * FontWidth(screen);
3802 	    } else {
3803 		x = fakeDoubleChars(&recur,
3804 				    gc, y, x,
3805 				    text, len);
3806 	    }
3807 	} else
3808 #endif
3809 	    if ((!IsIcon(screen) && screen->font_doublesize)
3810 		&& (gc2 = xterm_DoubleGC(&recur, gc, &inx)) != 0) {
3811 	    /* draw actual double-sized characters */
3812 	    XFontStruct *fs = getDoubleFont(screen, inx)->fs;
3813 	    XRectangle rect, *rp = &rect;
3814 	    Cardinal nr = 1;
3815 
3816 	    font_width *= 2;
3817 	    recur.draw_flags |= DOUBLEWFONT;
3818 
3819 	    rect.x = 0;
3820 	    rect.y = 0;
3821 	    rect.width = (unsigned short) ((int) len * font_width);
3822 	    rect.height = (unsigned short) (FontHeight(screen));
3823 
3824 	    TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
3825 	    switch (recur.this_chrset) {
3826 	    case CSET_DHL_TOP:
3827 		rect.y = (short) -(fs->ascent / 2);
3828 		y -= rect.y;
3829 		recur.draw_flags |= DOUBLEHFONT;
3830 		break;
3831 	    case CSET_DHL_BOT:
3832 		rect.y = (short) (rect.height - (fs->ascent / 2));
3833 		y -= rect.y;
3834 		recur.draw_flags |= DOUBLEHFONT;
3835 		break;
3836 	    case CSET_DWL:
3837 		break;
3838 	    default:
3839 		nr = 0;
3840 		break;
3841 	    }
3842 
3843 	    if (nr) {
3844 		xtermSetClipRectangles(screen->display, gc2,
3845 				       x, y, rp, nr, YXBanded);
3846 		xtermFillCells(&recur, gc, x, y + rect.y, len * 2);
3847 	    } else {
3848 		XSetClipMask(screen->display, gc2, None);
3849 	    }
3850 
3851 	    /* Call ourselves recursively with the new gc */
3852 
3853 	    /*
3854 	     * If we're trying to use proportional font, or if the
3855 	     * font server didn't give us what we asked for wrt
3856 	     * width, position each character independently.
3857 	     */
3858 	    if (screen->fnt_prop
3859 		|| (fs->min_bounds.width != fs->max_bounds.width)
3860 		|| (fs->min_bounds.width != 2 * FontWidth(screen))) {
3861 		/* It is hard to fall-through to the main
3862 		   branch: in a lot of places the check
3863 		   for the cached font info is for
3864 		   normal/bold fonts only. */
3865 		XTermDraw param2 = recur;
3866 		param2.this_chrset = CSET_SWL;
3867 		while (len--) {
3868 		    x = drawXtermText(&param2,
3869 				      gc2,
3870 				      x, y,
3871 				      text++,
3872 				      1);
3873 		    x += FontWidth(screen);
3874 		}
3875 	    } else {
3876 		XTermDraw param2 = recur;
3877 		param2.this_chrset = CSET_SWL;
3878 		x = drawXtermText(&param2,
3879 				  gc2,
3880 				  x, y,
3881 				  text,
3882 				  len);
3883 		x += (int) len *FontWidth(screen);
3884 	    }
3885 
3886 	} else {		/* simulate double-sized characters */
3887 	    x = fakeDoubleChars(&recur,
3888 				gc, y, x,
3889 				text, len);
3890 	}
3891 	TRACE(("DrewText [%4d,%4d]\n", y, x));
3892 	return x;
3893     }
3894 #endif
3895 #if OPT_RENDERFONT
3896     if (UsingRenderFont(recur.xw)) {
3897 	VTwin *currentWin = WhichVWin(screen);
3898 	Display *dpy = screen->display;
3899 	XftFont *font;
3900 	XftFont *font0;
3901 	XGCValues values;
3902 #if OPT_RENDERWIDE
3903 	XftFont *wfont;
3904 	XftFont *wfont0;
3905 #endif
3906 
3907 #if OPT_DEC_CHRSET
3908 	/*
3909 	 * If this is a VT100-style double-width font, ensure that everything
3910 	 * is printed using two-columns per character.
3911 	 */
3912 	Boolean forceDbl = CSET_DOUBLE(recur.real_chrset);
3913 #else
3914 	Boolean forceDbl = False;
3915 #endif
3916 
3917 	(void) forceDbl;
3918 	/*
3919 	 * Defer creating the drawable until we need it.
3920 	 */
3921 	if (!screen->renderDraw) {
3922 	    int scr;
3923 	    Drawable draw = VDrawable(screen);
3924 	    Visual *visual;
3925 
3926 	    scr = DefaultScreen(dpy);
3927 	    visual = DefaultVisual(dpy, scr);
3928 	    screen->renderDraw = XftDrawCreate(dpy, draw, visual,
3929 					       DefaultColormap(dpy, scr));
3930 	}
3931 
3932 	/*
3933 	 * font0/wfont0 provide fallback to non-bolded Xft font if a glyph is
3934 	 * not found in the bold version.
3935 	 */
3936 #define IS_BOLD  (recur.attr_flags & BOLDATTR(screen))
3937 #define NOT_BOLD (recur.attr_flags & ~BOLDATTR(screen))
3938 	font = getNormXftFont(&recur, recur.attr_flags, &did_ul);
3939 	font0 = IS_BOLD ? getNormXftFont(&recur, NOT_BOLD, &did_ul) : font;
3940 	(void) font0;
3941 #if OPT_RENDERWIDE
3942 	wfont = getWideXftFont(&recur, recur.attr_flags);
3943 	wfont0 = IS_BOLD ? getWideXftFont(&recur, NOT_BOLD) : wfont;
3944 #endif
3945 
3946 #define GET_XFT_FG() getXftColor(recur.xw, values.foreground)
3947 #define GET_XFT_BG() getXftColor(recur.xw, values.background)
3948 
3949 	values.foreground = getCgsFore(recur.xw, currentWin, gc);
3950 	values.background = getCgsBack(recur.xw, currentWin, gc);
3951 
3952 	if (!(recur.draw_flags & NOBACKGROUND)) {
3953 	    XftColor *bg_color = GET_XFT_BG();
3954 	    int ncells = xtermXftWidth(&recur, recur.attr_flags,
3955 				       bg_color,
3956 				       font, x, y,
3957 				       text,
3958 				       len);
3959 	    XftDrawRect(screen->renderDraw,
3960 			bg_color,
3961 			x, y,
3962 			(unsigned) (ncells * FontWidth(screen)),
3963 			(unsigned) FontHeight(screen));
3964 	}
3965 
3966 	y += font->ascent;
3967 #if OPT_BOX_CHARS
3968 	{
3969 	    /* adding code to substitute simulated line-drawing characters */
3970 	    int last, first = 0;
3971 	    int curX = x;
3972 
3973 	    for (last = 0; last < (int) len; last++) {
3974 		Boolean replace = False;
3975 		Boolean missing = False;
3976 		unsigned ch = (unsigned) text[last];
3977 		int filler = 0;
3978 #if OPT_WIDE_CHARS
3979 		int needed = forceDbl ? 2 : CharWidth(screen, ch);
3980 		XftFont *currFont = pickXftFont(needed, font, wfont);
3981 		XftFont *tempFont = 0;
3982 #define CURR_TEMP (tempFont ? tempFont : currFont)
3983 
3984 		if (xtermIsDecGraphic(ch) || ch == 0) {
3985 		    /*
3986 		     * Xft generally does not have the line-drawing characters
3987 		     * in cells 0-31.  Assume this (we cannot inspect the
3988 		     * picture easily...), and attempt to fill in from real
3989 		     * line-drawing character in the font at the Unicode
3990 		     * position.  Failing that, use our own box-characters.
3991 		     */
3992 		    if (screen->force_box_chars
3993 			|| screen->broken_box_chars
3994 			|| xtermXftMissing(recur.xw,
3995 					   currFont,
3996 					   dec2ucs(screen, ch))) {
3997 			SetMissing("case 1");
3998 		    } else {
3999 			ch = dec2ucs(screen, ch);
4000 			replace = True;
4001 		    }
4002 		} else if (ch >= 256) {
4003 		    /*
4004 		     * If we're reading UTF-8 from the client, we may have a
4005 		     * line-drawing character.  Translate it back to our
4006 		     * box-code if Xft tells us that the glyph is missing.
4007 		     */
4008 		    if_OPT_WIDE_CHARS(screen, {
4009 			unsigned part = ucs2dec(screen, ch);
4010 			if (xtermIsDecGraphic(part)) {
4011 			    if (screen->force_box_chars
4012 				|| screen->broken_box_chars) {
4013 				SetMissing("case 2");
4014 				ch = part;
4015 			    }
4016 			}
4017 			if (!missing && xtermXftMissing(recur.xw, currFont, ch)) {
4018 			    XftFont *test = findXftGlyph(recur.xw, currFont, ch);
4019 			    if (test == 0)
4020 				test = pickXftFont(needed, font0, wfont0);
4021 			    if (!xtermXftMissing(recur.xw, test, ch)) {
4022 				tempFont = test;
4023 				replace = True;
4024 				filler = 0;
4025 			    } else if ((part = AsciiEquivs(ch)) != ch) {
4026 				filler = needed - 1;
4027 				ch = part;
4028 				replace = True;
4029 			    } else if (ch != HIDDEN_CHAR) {
4030 				SetMissing("case 3");
4031 			    }
4032 			}
4033 		    });
4034 		}
4035 #else
4036 		XftFont *currFont = font;
4037 #define CURR_TEMP (currFont)
4038 		if (xtermIsDecGraphic(ch)) {
4039 		    /*
4040 		     * Xft generally does not have the line-drawing characters
4041 		     * in cells 1-31.  Check for this, and attempt to fill in
4042 		     * from real line-drawing character in the font at the
4043 		     * Unicode position.  Failing that, use our own
4044 		     * box-characters.
4045 		     */
4046 		    if (xtermXftMissing(recur.xw, currFont, ch)) {
4047 			SetMissing("case 4");
4048 		    }
4049 		}
4050 #endif
4051 
4052 		/*
4053 		 * If we now have one of our box-codes, draw it directly.
4054 		 */
4055 		if (missing || replace) {
4056 		    /* line drawing character time */
4057 		    if (last > first) {
4058 			int nc = drawClippedXftString(&recur,
4059 						      recur.attr_flags,
4060 						      currFont,
4061 						      GET_XFT_FG(),
4062 						      curX,
4063 						      y,
4064 						      text + first,
4065 						      (Cardinal) (last - first));
4066 			curX += nc * FontWidth(screen);
4067 			underline_len += (Cardinal) nc;
4068 		    }
4069 		    if (missing) {
4070 			Dimension old_wide = screen->fnt_wide;
4071 			Dimension old_high = screen->fnt_high;
4072 			screen->fnt_wide = (Dimension) FontWidth(screen);
4073 			screen->fnt_high = (Dimension) FontHeight(screen);
4074 
4075 			xtermDrawBoxChar(&recur, ch,
4076 					 gc,
4077 					 curX,
4078 					 y - FontAscent(screen),
4079 					 1,
4080 					 True);
4081 			curX += FontWidth(screen);
4082 			underline_len += 1;
4083 			screen->fnt_wide = old_wide;
4084 			screen->fnt_high = old_high;
4085 		    } else {
4086 			IChar ch2 = (IChar) ch;
4087 			int nc = drawClippedXftString(&recur,
4088 						      recur.attr_flags,
4089 						      CURR_TEMP,
4090 						      GET_XFT_FG(),
4091 						      curX,
4092 						      y,
4093 						      &ch2,
4094 						      1);
4095 			curX += nc * FontWidth(screen);
4096 			underline_len += (Cardinal) nc;
4097 			if (filler) {
4098 			    ch2 = ' ';
4099 			    nc = drawClippedXftString(&recur,
4100 						      recur.attr_flags,
4101 						      CURR_TEMP,
4102 						      GET_XFT_FG(),
4103 						      curX,
4104 						      y,
4105 						      &ch2,
4106 						      1);
4107 			    curX += nc * FontWidth(screen);
4108 			    underline_len += (Cardinal) nc;
4109 			}
4110 		    }
4111 		    first = last + 1;
4112 		}
4113 	    }
4114 	    if (last > first) {
4115 		underline_len += (Cardinal)
4116 		    drawClippedXftString(&recur,
4117 					 recur.attr_flags,
4118 					 font,
4119 					 GET_XFT_FG(),
4120 					 curX,
4121 					 y,
4122 					 text + first,
4123 					 (Cardinal) (last - first));
4124 	    }
4125 	}
4126 #else
4127 	{
4128 	    underline_len += (Cardinal)
4129 		drawClippedXftString(&recur,
4130 				     recur.attr_flags,
4131 				     font,
4132 				     GET_XFT_FG(),
4133 				     x,
4134 				     y,
4135 				     text,
4136 				     len);
4137 	}
4138 #endif /* OPT_BOX_CHARS */
4139 
4140 	drawUnderline(recur.xw,
4141 		      gc,
4142 		      recur.attr_flags,
4143 		      underline_len,
4144 		      FontWidth(screen),
4145 		      x,
4146 		      y + y_shift,
4147 		      did_ul);
4148 
4149 	x += (int) len *FontWidth(screen);
4150 
4151 	return x;
4152     }
4153 #endif /* OPT_RENDERFONT */
4154     curFont = ((recur.attr_flags & BOLDATTR(screen))
4155 	       ? WhichVFontData(screen, fBold)
4156 	       : WhichVFontData(screen, fNorm));
4157     /*
4158      * If we're asked to display a proportional font, do this with a fixed
4159      * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
4160      * as a dumb terminal vs its use as in fullscreen programs such as vi.
4161      * Hint: do not try to use a proportional font in the icon.
4162      */
4163     if (!IsIcon(screen) && !(recur.draw_flags & CHARBYCHAR) && screen->fnt_prop) {
4164 	int adj, width;
4165 
4166 	while (len--) {
4167 	    int cells = WideCells(*text);
4168 #if OPT_BOX_CHARS
4169 #if OPT_WIDE_CHARS
4170 	    if (*text == HIDDEN_CHAR) {
4171 		++text;
4172 		continue;
4173 	    } else
4174 #endif
4175 	    if (IsXtermMissingChar(screen, *text, curFont)) {
4176 		adj = 0;
4177 	    } else
4178 #endif
4179 	    {
4180 		if_WIDE_OR_NARROW(screen, {
4181 		    XChar2b temp[1];
4182 		    temp[0].byte2 = LO_BYTE(*text);
4183 		    temp[0].byte1 = HI_BYTE(*text);
4184 		    width = XTextWidth16(curFont->fs, temp, 1);
4185 		}
4186 		, {
4187 		    char temp[1];
4188 		    temp[0] = (char) LO_BYTE(*text);
4189 		    width = XTextWidth(curFont->fs, temp, 1);
4190 		});
4191 		adj = (FontWidth(screen) - width) / 2;
4192 		if (adj < 0)
4193 		    adj = 0;
4194 	    }
4195 	    xtermFillCells(&recur, gc, x, y, (Cardinal) cells);
4196 	    recur.draw_flags |= (NOBACKGROUND | CHARBYCHAR);
4197 	    x = drawXtermText(&recur,
4198 			      gc, x + adj, y,
4199 			      text++, 1) - adj;
4200 	}
4201 
4202 	return x;
4203     }
4204 #if OPT_BOX_CHARS
4205     /*
4206      * Draw some substitutions, if needed.  The font may not include the
4207      * line-drawing set, or it may be incomplete (in which case we'll draw an
4208      * empty space via xtermDrawBoxChar), or we may be told to force our
4209      * line-drawing.
4210      *
4211      * The empty space is a special case which can be overridden with the
4212      * showMissingGlyphs resource to produce an outline.  Not all fonts in
4213      * "modern" (sic) X provide an empty space; some use a thick outline or
4214      * something like the replacement character.  If you would rather not see
4215      * that, you can set assumeAllChars.
4216      */
4217     if (!IsIcon(screen)
4218 	&& !(recur.draw_flags & NOTRANSLATION)
4219 	&& (!screen->fnt_boxes
4220 	    || (FontIsIncomplete(curFont) && !screen->assume_all_chars)
4221 	    || recur.xw->work.force_wideFont
4222 	    || screen->force_box_chars)) {
4223 	/*
4224 	 * Fill in missing box-characters.  Find regions without missing
4225 	 * characters, and draw them calling ourselves recursively.  Draw
4226 	 * missing characters via xtermDrawBoxChar().
4227 	 */
4228 	int last, first = 0;
4229 	Bool drewBoxes = False;
4230 
4231 	for (last = 0; last < (int) len; last++) {
4232 	    unsigned ch = (unsigned) text[last];
4233 	    Bool isMissing;
4234 	    int ch_width;
4235 #if OPT_WIDE_CHARS
4236 
4237 	    if (ch == HIDDEN_CHAR) {
4238 		if (last > first) {
4239 		    XTermDraw param2 = recur;
4240 		    param2.draw_flags |= NOTRANSLATION;
4241 		    x = drawXtermText(&param2,
4242 				      gc,
4243 				      x, y,
4244 				      text + first,
4245 				      (unsigned) (last - first));
4246 		}
4247 		first = last + 1;
4248 		drewBoxes = True;
4249 		continue;
4250 	    }
4251 	    ch_width = CharWidth(screen, ch);
4252 	    isMissing =
4253 		IsXtermMissingChar(screen, ch,
4254 				   ((recur.on_wide || ch_width > 1)
4255 				    && okFont(NormalWFont(screen)))
4256 				   ? WhichVFontData(screen, fWide)
4257 				   : curFont);
4258 #else
4259 	    isMissing = IsXtermMissingChar(screen, ch, curFont);
4260 	    ch_width = 1;
4261 #endif
4262 	    /*
4263 	     * If the character is not missing, but we're in wide-character
4264 	     * mode and the character happens to be a wide-character that
4265 	     * corresponds to the line-drawing set, allow the forceBoxChars
4266 	     * resource (or menu entry) to force it to display using our
4267 	     * tables.
4268 	     */
4269 	    if_OPT_WIDE_CHARS(screen, {
4270 		if (!isMissing
4271 		    && TScreenOf(recur.xw)->force_box_chars) {
4272 		    if (ch > 255
4273 			&& ucs2dec(screen, ch) < 32) {
4274 			ch = ucs2dec(screen, ch);
4275 			isMissing = True;
4276 		    } else if (ch < 32) {
4277 			isMissing = True;
4278 		    }
4279 		}
4280 	    });
4281 
4282 	    if (isMissing) {
4283 		if (last > first) {
4284 		    XTermDraw param2 = recur;
4285 		    param2.draw_flags |= NOTRANSLATION;
4286 		    x = drawXtermText(&recur,
4287 				      gc,
4288 				      x, y,
4289 				      text + first,
4290 				      (unsigned) (last - first));
4291 		}
4292 #if OPT_WIDE_CHARS
4293 		if (ch_width <= 0 && ch < 32)
4294 		    ch_width = 1;	/* special case for line-drawing */
4295 		else if (ch_width < 0)
4296 		    ch_width = 1;	/* special case for combining char */
4297 		if (!ucs_workaround(&recur, ch, gc, x, y)) {
4298 		    xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4299 		}
4300 #else
4301 		xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4302 #endif
4303 		x += (ch_width * FontWidth(screen));
4304 		first = last + 1;
4305 		drewBoxes = True;
4306 	    } else if (ch_width == 2
4307 		       && recur.xw->work.force_wideFont
4308 		       && !(recur.draw_flags & NOTRANSLATION)) {
4309 		XTermDraw param2 = recur;
4310 		param2.draw_flags |= NOTRANSLATION;
4311 		/*
4312 		 * Not a "box" character, but a special case treated similarly.
4313 		 */
4314 		(void) drawXtermText(&param2,
4315 				     gc,
4316 				     x, y,
4317 				     text + first,
4318 				     (unsigned) (1 + last - first));
4319 		x += (ch_width * FontWidth(screen));
4320 		first = last + 1;
4321 		drewBoxes = True;
4322 	    }
4323 	}
4324 	if (last <= first) {
4325 	    return x;
4326 	}
4327 	text += first;
4328 	len = (Cardinal) (last - first);
4329 	recur.draw_flags |= NOTRANSLATION;
4330 	if (drewBoxes) {
4331 	    return drawXtermText(&recur,
4332 				 gc,
4333 				 x,
4334 				 y,
4335 				 text,
4336 				 len);
4337 	}
4338     }
4339 #endif /* OPT_BOX_CHARS */
4340     /*
4341      * Behave as if the font has (maybe Unicode-replacements for) drawing
4342      * characters in the range 1-31 (either we were not asked to ignore them,
4343      * or the caller made sure that there is none).
4344      */
4345 #if OPT_WIDE_ATTRS
4346 #define AttrFlags() recur.attr_flags
4347 #define DrawFlags() recur.draw_flags
4348 #else
4349 #define AttrFlags() (recur.attr_flags & DRAWX_MASK)
4350 #define DrawFlags() (recur.draw_flags & (unsigned)~DRAWX_MASK)
4351 #endif
4352     TRACE(("drawtext%c[%4d,%4d] {%#x,%#x} (%d) %d:%s\n",
4353 	   screen->cursor_state == OFF ? ' ' : '*',
4354 	   y, x,
4355 	   AttrFlags(),
4356 	   DrawFlags(),
4357 	   recur.this_chrset, len,
4358 	   visibleIChars(text, len)));
4359     if (screen->scale_height != 1.0f) {
4360 	xtermFillCells(&recur, gc, x, y, (Cardinal) len);
4361     }
4362     y += FontAscent(screen);
4363 
4364 #if OPT_WIDE_CHARS
4365 
4366     if (screen->wide_chars || screen->unicode_font) {
4367 	XChar2b *buffer;
4368 	Bool needWide = False;
4369 	int src, dst;
4370 	Bool useBoldFont;
4371 	int ascent_adjust = 0;
4372 
4373 	BumpTypedBuffer(XChar2b, len);
4374 	buffer = BfBuf(XChar2b);
4375 
4376 	for (src = dst = 0; src < (int) len; src++) {
4377 	    IChar ch = text[src];
4378 
4379 	    if (ch == HIDDEN_CHAR)
4380 		continue;
4381 
4382 #if OPT_BOX_CHARS
4383 	    if ((screen->fnt_boxes == 1) && (ch >= 256)) {
4384 		unsigned part = ucs2dec(screen, ch);
4385 		if (part < 32)
4386 		    ch = (IChar) part;
4387 	    }
4388 #endif
4389 
4390 	    if (!needWide
4391 		&& !IsIcon(screen)
4392 		&& ((recur.on_wide || CharWidth(screen, ch) > 1)
4393 		    && okFont(NormalWFont(screen)))) {
4394 		needWide = True;
4395 	    }
4396 #if OPT_WIDER_ICHAR
4397 	    /*
4398 	     * bitmap-fonts are limited to 16-bits.
4399 	     */
4400 	    if (ch > NARROW_ICHAR) {
4401 		ch = 0;
4402 	    }
4403 #endif
4404 	    buffer[dst].byte2 = LO_BYTE(ch);
4405 	    buffer[dst].byte1 = HI_BYTE(ch);
4406 #if OPT_MINI_LUIT
4407 #define UCS2SBUF(value)	buffer[dst].byte2 = LO_BYTE(value);\
4408 			buffer[dst].byte1 = HI_BYTE(value)
4409 
4410 #define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); }
4411 
4412 	    if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) {
4413 
4414 		/* see http://www.cs.tut.fi/~jkorpela/latin9.html */
4415 		/* *INDENT-OFF* */
4416 		if Map2Sbuf(0xa4, 0x20ac)
4417 		else if Map2Sbuf(0xa6, 0x0160)
4418 		else if Map2Sbuf(0xa8, 0x0161)
4419 		else if Map2Sbuf(0xb4, 0x017d)
4420 		else if Map2Sbuf(0xb8, 0x017e)
4421 		else if Map2Sbuf(0xbc, 0x0152)
4422 		else if Map2Sbuf(0xbd, 0x0153)
4423 		else if Map2Sbuf(0xbe, 0x0178)
4424 		/* *INDENT-ON* */
4425 
4426 	    }
4427 	    if (screen->unicode_font
4428 		&& (text[src] == ANSI_DEL ||
4429 		    text[src] < ANSI_SPA)) {
4430 		unsigned ni = dec2ucs(screen,
4431 				      (unsigned) ((text[src] == ANSI_DEL)
4432 						  ? 0
4433 						  : text[src]));
4434 		UCS2SBUF(ni);
4435 	    }
4436 #endif /* OPT_MINI_LUIT */
4437 	    ++dst;
4438 	}
4439 
4440 	/*
4441 	 * Check for special case where the bold font lacks glyphs found in the
4442 	 * normal font, and drop down to normal fonts with overstriking to help
4443 	 * show the actual characters.
4444 	 */
4445 	useBoldFont = ((recur.attr_flags & BOLDATTR(screen)) != 0);
4446 	if (useBoldFont) {
4447 	    XTermFonts *norm = 0;
4448 	    XTermFonts *bold = 0;
4449 	    Bool noBold, noNorm;
4450 
4451 	    if (needWide && okFont(BoldWFont(screen))) {
4452 		norm = WhichVFontData(screen, fWide);
4453 		bold = WhichVFontData(screen, fWBold);
4454 	    } else if (okFont(BoldFont(screen))) {
4455 		norm = WhichVFontData(screen, fNorm);
4456 		bold = WhichVFontData(screen, fBold);
4457 	    } else {
4458 		useBoldFont = False;
4459 	    }
4460 
4461 	    if (useBoldFont && FontIsIncomplete(bold)) {
4462 		for (src = 0; src < (int) len; src++) {
4463 		    IChar ch = text[src];
4464 
4465 		    if (ch == HIDDEN_CHAR)
4466 			continue;
4467 
4468 		    noBold = IsXtermMissingChar(screen, ch, bold);
4469 		    if (noBold) {
4470 			noNorm = IsXtermMissingChar(screen, ch, norm);
4471 			if (!noNorm) {
4472 			    useBoldFont = False;
4473 			    break;
4474 			}
4475 		    }
4476 		}
4477 	    }
4478 	}
4479 
4480 	/* FIXME This is probably wrong. But it works. */
4481 	underline_len = len;
4482 
4483 	/* Set the drawing font */
4484 	if (!(recur.draw_flags & (DOUBLEHFONT | DOUBLEWFONT))) {
4485 	    VTwin *currentWin = WhichVWin(screen);
4486 	    VTFontEnum fntId;
4487 	    CgsEnum cgsId;
4488 	    Pixel fg = getCgsFore(recur.xw, currentWin, gc);
4489 	    Pixel bg = getCgsBack(recur.xw, currentWin, gc);
4490 
4491 	    if (needWide
4492 		&& useBoldFont
4493 		&& okFont(BoldWFont(screen))) {
4494 		fntId = fWBold;
4495 		cgsId = gcWBold;
4496 	    } else if (needWide) {
4497 		fntId = fWide;
4498 		cgsId = gcWide;
4499 	    } else if (useBoldFont) {
4500 		fntId = fBold;
4501 		cgsId = gcBold;
4502 	    } else {
4503 		fntId = fNorm;
4504 		cgsId = gcNorm;
4505 	    }
4506 
4507 	    setCgsFore(recur.xw, currentWin, cgsId, fg);
4508 	    setCgsBack(recur.xw, currentWin, cgsId, bg);
4509 	    gc = getCgsGC(recur.xw, currentWin, cgsId);
4510 
4511 #if OPT_WIDE_ATTRS
4512 #if OPT_DEC_CHRSET
4513 	    if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4514 #endif
4515 		need_clipping = fixupItalics(&recur,
4516 					     gc,
4517 					     getCgsFont(recur.xw,
4518 							currentWin, gc),
4519 					     y, x, font_width, len);
4520 #endif
4521 	    if (fntId != fNorm) {
4522 		XFontStruct *thisFp = WhichVFont(screen, fntId);
4523 		ascent_adjust = (thisFp->ascent
4524 				 - NormalFont(screen)->ascent);
4525 		if (thisFp->max_bounds.width ==
4526 		    NormalFont(screen)->max_bounds.width * 2) {
4527 		    underline_len = real_length = (Cardinal) (dst * 2);
4528 		} else if (cgsId == gcWide || cgsId == gcWBold) {
4529 		    underline_len = real_length = (Cardinal) (dst * 2);
4530 		    xtermFillCells(&recur,
4531 				   gc,
4532 				   x,
4533 				   y - thisFp->ascent,
4534 				   real_length);
4535 		}
4536 	    }
4537 	}
4538 
4539 	if (recur.draw_flags & NOBACKGROUND) {
4540 	    XDrawString16(screen->display,
4541 			  VDrawable(screen), gc,
4542 			  x, y + y_shift + ascent_adjust,
4543 			  buffer, dst);
4544 	} else if (dst <= MaxImageString) {
4545 	    XDrawImageString16(screen->display,
4546 			       VDrawable(screen), gc,
4547 			       x, y + y_shift + ascent_adjust,
4548 			       buffer, dst);
4549 	} else {
4550 	    int b_pos;
4551 	    int b_max = MaxImageString;
4552 	    for (b_pos = 0; b_pos < dst; b_pos += b_max) {
4553 		if (b_pos + b_max > dst)
4554 		    b_max = (dst - b_pos);
4555 		XDrawImageString16(screen->display,
4556 				   VDrawable(screen), gc,
4557 				   x + (b_pos * FontWidth(screen)),
4558 				   y + y_shift + ascent_adjust,
4559 				   buffer + b_pos,
4560 				   b_max);
4561 	    }
4562 	}
4563 #if OPT_WIDE_ATTRS
4564 	if (need_clipping) {
4565 	    endClipping(screen, gc);
4566 	}
4567 #endif
4568 
4569 	if ((recur.attr_flags & BOLDATTR(screen)) && (screen->enbolden || !useBoldFont)) {
4570 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4571 		beginClipping(screen, gc, (Cardinal) font_width, len);
4572 	    }
4573 	    XDrawString16(screen->display, VDrawable(screen), gc,
4574 			  x + 1,
4575 			  y + y_shift + ascent_adjust,
4576 			  buffer, dst);
4577 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4578 		endClipping(screen, gc);
4579 	    }
4580 	}
4581 
4582     } else
4583 #endif /* OPT_WIDE_CHARS */
4584     {
4585 	int length = (int) len;	/* X should have used unsigned */
4586 #if OPT_WIDE_CHARS
4587 	char *buffer;
4588 	int dst;
4589 
4590 	BumpTypedBuffer(char, len);
4591 	buffer = BfBuf(char);
4592 
4593 	for (dst = 0; dst < length; ++dst)
4594 	    buffer[dst] = (char) LO_BYTE(text[dst]);
4595 #else
4596 	char *buffer = (char *) text;
4597 #endif
4598 
4599 #if OPT_WIDE_ATTRS
4600 #if OPT_DEC_CHRSET
4601 	if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4602 #endif
4603 	    need_clipping = fixupItalics(&recur, gc, curFont,
4604 					 y, x, font_width, len);
4605 #endif
4606 
4607 	if (recur.draw_flags & NOBACKGROUND) {
4608 	    XDrawString(screen->display, VDrawable(screen), gc,
4609 			x, y + y_shift, buffer, length);
4610 	} else if (length <= MaxImageString) {
4611 	    XDrawImageString(screen->display, VDrawable(screen), gc,
4612 			     x, y + y_shift, buffer, length);
4613 	} else {
4614 	    int b_pos;
4615 	    int b_max = MaxImageString;
4616 	    for (b_pos = 0; b_pos < length; b_pos += b_max) {
4617 		if (b_pos + b_max > length)
4618 		    b_max = (length - b_pos);
4619 		XDrawImageString(screen->display,
4620 				 VDrawable(screen), gc,
4621 				 x + (b_pos * FontWidth(screen)),
4622 				 y + y_shift,
4623 				 buffer + b_pos,
4624 				 b_max);
4625 	    }
4626 	}
4627 
4628 #if OPT_WIDE_ATTRS
4629 	if (need_clipping) {
4630 	    endClipping(screen, gc);
4631 	}
4632 #endif
4633 	underline_len = (Cardinal) length;
4634 	if ((recur.attr_flags & BOLDATTR(screen)) && screen->enbolden) {
4635 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4636 		beginClipping(screen, gc, font_width, length);
4637 	    }
4638 	    XDrawString(screen->display, VDrawable(screen), gc,
4639 			x + 1, y + y_shift, buffer, length);
4640 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4641 		endClipping(screen, gc);
4642 	    }
4643 	}
4644     }
4645 
4646     drawUnderline(recur.xw,
4647 		  gc,
4648 		  recur.attr_flags,
4649 		  underline_len,
4650 		  font_width,
4651 		  x,
4652 		  y + y_shift,
4653 		  did_ul);
4654 
4655     x += ((int) real_length) * FontWidth(screen);
4656     return x;
4657 }
4658 
4659 #if OPT_WIDE_CHARS
4660 /*
4661  * Allocate buffer - workaround for wide-character interfaces.
4662  */
4663 void
allocXtermChars(ScrnPtr * buffer,Cardinal length)4664 allocXtermChars(ScrnPtr *buffer, Cardinal length)
4665 {
4666     if (*buffer == 0) {
4667 	*buffer = (ScrnPtr) XtMalloc(length);
4668     } else {
4669 	*buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
4670     }
4671 }
4672 #endif
4673 
4674 /* set up size hints for window manager; min 1 char by 1 char */
4675 void
xtermSizeHints(XtermWidget xw,int scrollbarWidth)4676 xtermSizeHints(XtermWidget xw, int scrollbarWidth)
4677 {
4678     TScreen *screen = TScreenOf(xw);
4679 
4680     TRACE(("xtermSizeHints\n"));
4681     TRACE(("   border    %d\n", xw->core.border_width));
4682     TRACE(("   scrollbar %d\n", scrollbarWidth));
4683 
4684     xw->hints.base_width = 2 * screen->border + scrollbarWidth;
4685     xw->hints.base_height = 2 * screen->border;
4686 
4687 #if OPT_TOOLBAR
4688     TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
4689 
4690     xw->hints.base_height += ToolbarHeight(xw);
4691     xw->hints.base_height += BorderWidth(xw) * 2;
4692     xw->hints.base_width += BorderWidth(xw) * 2;
4693 #endif
4694 
4695     if (xw->misc.resizeByPixel) {
4696 	xw->hints.width_inc = 1;
4697 	xw->hints.height_inc = 1;
4698     } else {
4699 	xw->hints.width_inc = FontWidth(screen);
4700 	xw->hints.height_inc = FontHeight(screen);
4701     }
4702     xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
4703     xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
4704 
4705     xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
4706     xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
4707 
4708     xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
4709 
4710     TRACE_HINTS(&(xw->hints));
4711 }
4712 
4713 void
getXtermSizeHints(XtermWidget xw)4714 getXtermSizeHints(XtermWidget xw)
4715 {
4716     TScreen *screen = TScreenOf(xw);
4717     long supp;
4718 
4719     if (!XGetWMNormalHints(screen->display, VShellWindow(xw),
4720 			   &xw->hints, &supp))
4721 	memset(&xw->hints, 0, sizeof(xw->hints));
4722     TRACE_HINTS(&(xw->hints));
4723 }
4724 
4725 CgsEnum
whichXtermCgs(XtermWidget xw,unsigned attr_flags,Bool hilite)4726 whichXtermCgs(XtermWidget xw, unsigned attr_flags, Bool hilite)
4727 {
4728     TScreen *screen = TScreenOf(xw);
4729     CgsEnum cgsId = gcMAX;
4730 
4731     if (ReverseOrHilite(screen, attr_flags, hilite)) {
4732 	if (attr_flags & BOLDATTR(screen)) {
4733 	    cgsId = gcBoldReverse;
4734 	} else {
4735 	    cgsId = gcNormReverse;
4736 	}
4737     } else {
4738 	if (attr_flags & BOLDATTR(screen)) {
4739 	    cgsId = gcBold;
4740 	} else {
4741 	    cgsId = gcNorm;
4742 	}
4743     }
4744     return cgsId;
4745 }
4746 
4747 /*
4748  * Returns a GC, selected according to the font (reverse/bold/normal) that is
4749  * required for the current position (implied).  The GC is updated with the
4750  * current screen foreground and background colors.
4751  */
4752 GC
updatedXtermGC(XtermWidget xw,unsigned attr_flags,CellColor fg_bg,Bool hilite)4753 updatedXtermGC(XtermWidget xw, unsigned attr_flags, CellColor fg_bg,
4754 	       Bool hilite)
4755 {
4756     TScreen *screen = TScreenOf(xw);
4757     VTwin *win = WhichVWin(screen);
4758     CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
4759     Pixel my_fg = extract_fg(xw, fg_bg, attr_flags);
4760     Pixel my_bg = extract_bg(xw, fg_bg, attr_flags);
4761     Pixel fg_pix = getXtermFG(xw, attr_flags, (int) my_fg);
4762     Pixel bg_pix = getXtermBG(xw, attr_flags, (int) my_bg);
4763     Pixel xx_pix;
4764 #if OPT_HIGHLIGHT_COLOR
4765     Boolean reverse2 = ((attr_flags & INVERSE) && hilite);
4766     Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
4767     Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
4768     Boolean always = screen->hilite_color;
4769     Boolean use_selbg = (Boolean) (always &&
4770 				   isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
4771     Boolean use_selfg = (Boolean) (always &&
4772 				   isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
4773 #endif
4774 
4775     (void) fg_bg;
4776     (void) my_bg;
4777     (void) my_fg;
4778 
4779     /*
4780      * Discard video attributes overridden by colorXXXMode's.
4781      */
4782     checkVeryBoldColors(attr_flags, my_fg);
4783 
4784     if (ReverseOrHilite(screen, attr_flags, hilite)) {
4785 #if OPT_HIGHLIGHT_COLOR
4786 	if (!screen->hilite_color) {
4787 	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
4788 		&& selbg_pix != fg_pix
4789 		&& selbg_pix != bg_pix
4790 		&& selbg_pix != xw->dft_foreground) {
4791 		bg_pix = fg_pix;
4792 		fg_pix = selbg_pix;
4793 	    }
4794 	}
4795 #endif
4796 	EXCHANGE(fg_pix, bg_pix, xx_pix);
4797 #if OPT_HIGHLIGHT_COLOR
4798 	if (screen->hilite_color) {
4799 	    if (screen->hilite_reverse) {
4800 		if (use_selbg) {
4801 		    if (use_selfg) {
4802 			bg_pix = fg_pix;
4803 		    } else {
4804 			fg_pix = bg_pix;
4805 			bg_pix = selbg_pix;
4806 		    }
4807 		}
4808 		if (use_selfg)
4809 		    fg_pix = selfg_pix;
4810 	    }
4811 	}
4812 #endif
4813     } else if ((attr_flags & INVERSE) && hilite) {
4814 #if OPT_HIGHLIGHT_COLOR
4815 	if (!screen->hilite_color) {
4816 	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
4817 		&& selbg_pix != fg_pix
4818 		&& selbg_pix != bg_pix
4819 		&& selbg_pix != xw->dft_foreground) {
4820 		bg_pix = fg_pix;
4821 		fg_pix = selbg_pix;
4822 	    }
4823 	}
4824 #endif
4825 	/* double-reverse... EXCHANGE(fg_pix, bg_pix, xx_pix); */
4826 #if OPT_HIGHLIGHT_COLOR
4827 	if (screen->hilite_color) {
4828 	    if (screen->hilite_reverse) {
4829 		if (use_selbg) {
4830 		    if (use_selfg ^ reverse2) {
4831 			bg_pix = fg_pix;
4832 		    } else {
4833 			fg_pix = bg_pix;
4834 		    }
4835 		    if (reverse2) {
4836 			fg_pix = selbg_pix;
4837 		    } else {
4838 			bg_pix = selbg_pix;
4839 		    }
4840 		}
4841 		if (use_selfg) {
4842 		    if (reverse2) {
4843 			bg_pix = selfg_pix;
4844 		    } else {
4845 			fg_pix = selfg_pix;
4846 		    }
4847 		}
4848 	    }
4849 	}
4850 #endif
4851     }
4852 #if OPT_HIGHLIGHT_COLOR
4853     if (!screen->hilite_color || !screen->hilite_reverse) {
4854 	if (hilite && !screen->hilite_reverse) {
4855 	    if (use_selbg) {
4856 		if (reverse2)
4857 		    fg_pix = selbg_pix;
4858 		else
4859 		    bg_pix = selbg_pix;
4860 	    }
4861 	    if (use_selfg) {
4862 		if (reverse2)
4863 		    bg_pix = selfg_pix;
4864 		else
4865 		    fg_pix = selfg_pix;
4866 	    }
4867 	}
4868     }
4869 #endif
4870 
4871 #if OPT_BLINK_TEXT
4872     if ((screen->blink_state == ON) &&
4873 	(!screen->blink_as_bold) &&
4874 	(attr_flags & BLINK)) {
4875 	fg_pix = bg_pix;
4876     }
4877 #endif
4878 
4879     setCgsFore(xw, win, cgsId, fg_pix);
4880     setCgsBack(xw, win, cgsId, bg_pix);
4881     return getCgsGC(xw, win, cgsId);
4882 }
4883 
4884 /*
4885  * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
4886  * to the values that would be set in SGR_Foreground and SGR_Background. This
4887  * duplicates some logic, but only modifies 1/4 as many GC's.
4888  */
4889 void
resetXtermGC(XtermWidget xw,unsigned attr_flags,Bool hilite)4890 resetXtermGC(XtermWidget xw, unsigned attr_flags, Bool hilite)
4891 {
4892     TScreen *screen = TScreenOf(xw);
4893     VTwin *win = WhichVWin(screen);
4894     CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
4895     Pixel fg_pix = getXtermFG(xw, attr_flags, xw->cur_foreground);
4896     Pixel bg_pix = getXtermBG(xw, attr_flags, xw->cur_background);
4897 
4898     checkVeryBoldColors(attr_flags, xw->cur_foreground);
4899 
4900     if (ReverseOrHilite(screen, attr_flags, hilite)) {
4901 	setCgsFore(xw, win, cgsId, bg_pix);
4902 	setCgsBack(xw, win, cgsId, fg_pix);
4903     } else {
4904 	setCgsFore(xw, win, cgsId, fg_pix);
4905 	setCgsBack(xw, win, cgsId, bg_pix);
4906     }
4907 }
4908 
4909 #if OPT_ISO_COLORS
4910 /*
4911  * Extract the foreground-color index from a color pair.
4912  * If we've got BOLD or UNDERLINE color-mode active, those will be used.
4913  */
4914 Pixel
extract_fg(XtermWidget xw,CellColor color,unsigned attr_flags)4915 extract_fg(XtermWidget xw, CellColor color, unsigned attr_flags)
4916 {
4917     unsigned fg = ExtractForeground(color);
4918 
4919     if (TScreenOf(xw)->colorAttrMode
4920 	|| (fg == ExtractBackground(color))) {
4921 	fg = MapToColorMode(fg, TScreenOf(xw), attr_flags);
4922     }
4923     return fg;
4924 }
4925 
4926 /*
4927  * Extract the background-color index from a color pair.
4928  * If we've got INVERSE color-mode active, that will be used.
4929  */
4930 Pixel
extract_bg(XtermWidget xw,CellColor color,unsigned attr_flags)4931 extract_bg(XtermWidget xw, CellColor color, unsigned attr_flags)
4932 {
4933     unsigned bg = ExtractBackground(color);
4934 
4935     if (TScreenOf(xw)->colorAttrMode
4936 	|| (bg == ExtractForeground(color))) {
4937 	if (TScreenOf(xw)->colorRVMode && (attr_flags & INVERSE))
4938 	    bg = COLOR_RV;
4939     }
4940     return bg;
4941 }
4942 
4943 /*
4944  * Combine the current foreground and background into a single 8-bit number.
4945  * Note that we're storing the SGR foreground, since cur_foreground may be set
4946  * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
4947  * bits.
4948  *
4949  * This assumes that fg/bg are equal when we override with one of the special
4950  * attribute colors.
4951  */
4952 CellColor
makeColorPair(XtermWidget xw)4953 makeColorPair(XtermWidget xw)
4954 {
4955     CellColor result;
4956 
4957 #if OPT_DIRECT_COLOR
4958     result.fg = xw->cur_foreground;
4959     result.bg = xw->cur_background;
4960 #else
4961     int fg = xw->cur_foreground;
4962     int bg = xw->cur_background;
4963     unsigned my_bg = okIndexedColor(bg) ? (unsigned) bg : 0;
4964     unsigned my_fg = okIndexedColor(fg) ? (unsigned) fg : my_bg;
4965 
4966     result = (CellColor) (my_fg | (my_bg << COLOR_BITS));
4967 #endif
4968 
4969     return result;
4970 }
4971 
4972 /*
4973  * Using the "current" SGR background, clear a rectangle.
4974  */
4975 void
ClearCurBackground(XtermWidget xw,int top,int left,unsigned height,unsigned width,unsigned fw)4976 ClearCurBackground(XtermWidget xw,
4977 		   int top,
4978 		   int left,
4979 		   unsigned height,
4980 		   unsigned width,
4981 		   unsigned fw)
4982 {
4983     TScreen *screen = TScreenOf(xw);
4984 
4985     TRACE(("ClearCurBackground %d,%d %dx%d with %d\n",
4986 	   top, left, height, width, xw->cur_background));
4987 
4988     assert((int) width > 0);
4989     assert((left + (int) width) <= screen->max_col + 1);
4990     assert((int) height <= screen->max_row + 1);
4991 
4992     if (VWindow(screen)) {
4993 	set_background(xw, xw->cur_background);
4994 
4995 	xtermClear2(xw,
4996 		    CursorX2(screen, left, fw),
4997 		    CursorY2(screen, top),
4998 		    (width * fw),
4999 		    (height * (unsigned) FontHeight(screen)));
5000 
5001 	set_background(xw, -1);
5002     }
5003 }
5004 #endif /* OPT_ISO_COLORS */
5005 
5006 Pixel
getXtermBackground(XtermWidget xw,unsigned attr_flags,int color)5007 getXtermBackground(XtermWidget xw, unsigned attr_flags, int color)
5008 {
5009     Pixel result = T_COLOR(TScreenOf(xw), TEXT_BG);
5010 
5011 #if OPT_ISO_COLORS
5012     if (color >= 0) {
5013 	if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_BG), {
5014 	    result = (Pixel) color;
5015 	}) if ((attr_flags & BG_COLOR) && (color < MAXCOLORS)) {
5016 	    result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
5017 	}
5018     }
5019 #else
5020     (void) attr_flags;
5021     (void) color;
5022 #endif
5023     return result;
5024 }
5025 
5026 Pixel
getXtermForeground(XtermWidget xw,unsigned attr_flags,int color)5027 getXtermForeground(XtermWidget xw, unsigned attr_flags, int color)
5028 {
5029     Pixel result = T_COLOR(TScreenOf(xw), TEXT_FG);
5030 
5031 #if OPT_ISO_COLORS
5032     if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_FG), {
5033 	result = (Pixel) color;
5034     })
5035 	if ((attr_flags & FG_COLOR) &&
5036 	    (color >= 0 && color < MAXCOLORS)) {
5037 	result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
5038     }
5039 #else
5040     (void) attr_flags;
5041     (void) color;
5042 #endif
5043 
5044 #if OPT_WIDE_ATTRS
5045 #define DIM_IT(n) work.n = (unsigned short) ((2 * (unsigned)work.n) / 3)
5046     if ((attr_flags & ATR_FAINT)) {
5047 	static Pixel last_in;
5048 	static Pixel last_out;
5049 	if ((result != last_in)
5050 	    && ((color >= 0)
5051 		|| (result != (Pixel) color))) {
5052 	    XColor work;
5053 	    last_in = result;
5054 	    work.pixel = result;
5055 	    if (QueryOneColor(xw, &work)) {
5056 		DIM_IT(red);
5057 		DIM_IT(green);
5058 		DIM_IT(blue);
5059 		if (allocateBestRGB(xw, &work)) {
5060 		    result = work.pixel;
5061 		}
5062 	    }
5063 	    last_out = result;
5064 	} else {
5065 	    result = last_out;
5066 	}
5067     }
5068 #endif
5069     return result;
5070 }
5071 
5072 /*
5073  * Returns a single base character for the given cell.
5074  */
5075 unsigned
getXtermCell(TScreen * screen,int row,int col)5076 getXtermCell(TScreen *screen, int row, int col)
5077 {
5078     CLineData *ld = getLineData(screen, row);
5079 
5080     return ((ld && (col < (int) ld->lineSize))
5081 	    ? ld->charData[col]
5082 	    : (unsigned) ' ');
5083 }
5084 
5085 /*
5086  * Sets a single base character for the given cell.
5087  */
5088 void
putXtermCell(TScreen * screen,int row,int col,int ch)5089 putXtermCell(TScreen *screen, int row, int col, int ch)
5090 {
5091     LineData *ld = getLineData(screen, row);
5092 
5093     if (ld && (col < (int) ld->lineSize)) {
5094 	ld->charData[col] = (CharData) ch;
5095 	if_OPT_WIDE_CHARS(screen, {
5096 	    size_t off;
5097 	    for_each_combData(off, ld) {
5098 		ld->combData[off][col] = 0;
5099 	    }
5100 	});
5101     }
5102 }
5103 
5104 #if OPT_WIDE_CHARS
5105 /*
5106  * Add a combining character for the given cell
5107  */
5108 void
addXtermCombining(TScreen * screen,int row,int col,unsigned ch)5109 addXtermCombining(TScreen *screen, int row, int col, unsigned ch)
5110 {
5111     if (ch != 0) {
5112 	LineData *ld = getLineData(screen, row);
5113 	size_t off;
5114 
5115 	TRACE(("addXtermCombining %d,%d U+%04X (%d)\n",
5116 	       row, col, ch, CharWidth(screen, ch)));
5117 
5118 	for_each_combData(off, ld) {
5119 	    if (!ld->combData[off][col]) {
5120 		ld->combData[off][col] = (CharData) ch;
5121 		break;
5122 	    }
5123 	}
5124     }
5125 }
5126 
5127 unsigned
getXtermCombining(TScreen * screen,int row,int col,int off)5128 getXtermCombining(TScreen *screen, int row, int col, int off)
5129 {
5130     CLineData *ld = getLineData(screen, row);
5131     return (ld->combSize ? ld->combData[off][col] : 0U);
5132 }
5133 #endif
5134 
5135 void
update_keyboard_type(void)5136 update_keyboard_type(void)
5137 {
5138     update_delete_del();
5139     update_tcap_fkeys();
5140     update_old_fkeys();
5141     update_hp_fkeys();
5142     update_sco_fkeys();
5143     update_sun_fkeys();
5144     update_sun_kbd();
5145 }
5146 
5147 void
set_keyboard_type(XtermWidget xw,xtermKeyboardType type,Bool set)5148 set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5149 {
5150     xtermKeyboardType save = xw->keyboard.type;
5151 
5152     TRACE(("set_keyboard_type(%s, %s) currently %s\n",
5153 	   visibleKeyboardType(type),
5154 	   BtoS(set),
5155 	   visibleKeyboardType(xw->keyboard.type)));
5156     if (set) {
5157 	xw->keyboard.type = type;
5158     } else {
5159 	xw->keyboard.type = keyboardIsDefault;
5160     }
5161 
5162     if (save != xw->keyboard.type) {
5163 	update_keyboard_type();
5164     }
5165 }
5166 
5167 void
toggle_keyboard_type(XtermWidget xw,xtermKeyboardType type)5168 toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
5169 {
5170     xtermKeyboardType save = xw->keyboard.type;
5171 
5172     TRACE(("toggle_keyboard_type(%s) currently %s\n",
5173 	   visibleKeyboardType(type),
5174 	   visibleKeyboardType(xw->keyboard.type)));
5175     if (xw->keyboard.type == type) {
5176 	xw->keyboard.type = keyboardIsDefault;
5177     } else {
5178 	xw->keyboard.type = type;
5179     }
5180 
5181     if (save != xw->keyboard.type) {
5182 	update_keyboard_type();
5183     }
5184 }
5185 
5186 const char *
visibleKeyboardType(xtermKeyboardType type)5187 visibleKeyboardType(xtermKeyboardType type)
5188 {
5189     const char *result = "?";
5190     switch (type) {
5191 	CASETYPE(keyboardIsLegacy);	/* bogus vt220 codes for F1-F4, etc. */
5192 	CASETYPE(keyboardIsDefault);
5193 	CASETYPE(keyboardIsHP);
5194 	CASETYPE(keyboardIsSCO);
5195 	CASETYPE(keyboardIsSun);
5196 	CASETYPE(keyboardIsTermcap);
5197 	CASETYPE(keyboardIsVT220);
5198     }
5199     return result;
5200 }
5201 
5202 static void
init_keyboard_type(XtermWidget xw,xtermKeyboardType type,Bool set)5203 init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5204 {
5205     TRACE(("init_keyboard_type(%s, %s) currently %s\n",
5206 	   visibleKeyboardType(type),
5207 	   BtoS(set),
5208 	   visibleKeyboardType(xw->keyboard.type)));
5209     if (set) {
5210 	/*
5211 	 * Check for conflicts, e.g., if someone asked for both Sun and HP
5212 	 * function keys.
5213 	 */
5214 	if (guard_keyboard_type) {
5215 	    xtermWarning("Conflicting keyboard type option (%s/%s)\n",
5216 			 visibleKeyboardType(xw->keyboard.type),
5217 			 visibleKeyboardType(type));
5218 	}
5219 	xw->keyboard.type = type;
5220 	guard_keyboard_type = True;
5221 	update_keyboard_type();
5222     }
5223 }
5224 
5225 /*
5226  * If the keyboardType resource is set, use that, overriding the individual
5227  * boolean resources for different keyboard types.
5228  */
5229 void
decode_keyboard_type(XtermWidget xw,XTERM_RESOURCE * rp)5230 decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
5231 {
5232 #define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
5233 #define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
5234     static struct {
5235 	const char *name;
5236 	xtermKeyboardType type;
5237 	unsigned offset;
5238     } table[] = {
5239 	DATA(NAME_OLD_KT, keyboardIsLegacy, oldKeyboard),
5240 #if OPT_HP_FUNC_KEYS
5241 	    DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
5242 #endif
5243 #if OPT_SCO_FUNC_KEYS
5244 	    DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
5245 #endif
5246 #if OPT_SUN_FUNC_KEYS
5247 	    DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
5248 #endif
5249 #if OPT_SUNPC_KBD
5250 	    DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
5251 #endif
5252 #if OPT_TCAP_FKEYS
5253 	    DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
5254 #endif
5255     };
5256     Cardinal n;
5257     TScreen *screen = TScreenOf(xw);
5258 
5259     TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
5260     if (!x_strcasecmp(rp->keyboardType, "unknown")) {
5261 	/*
5262 	 * Let the individual resources comprise the keyboard-type.
5263 	 */
5264 	for (n = 0; n < XtNumber(table); ++n)
5265 	    init_keyboard_type(xw, table[n].type, FLAG(n));
5266     } else if (!x_strcasecmp(rp->keyboardType, "default")) {
5267 	/*
5268 	 * Set the keyboard-type to the Sun/PC type, allowing modified
5269 	 * function keys, etc.
5270 	 */
5271 	for (n = 0; n < XtNumber(table); ++n)
5272 	    init_keyboard_type(xw, table[n].type, False);
5273     } else {
5274 	Bool found = False;
5275 
5276 	/*
5277 	 * Special case: oldXtermFKeys should have been like the others.
5278 	 */
5279 	if (!x_strcasecmp(rp->keyboardType, NAME_OLD_KT)) {
5280 	    TRACE(("special case, setting oldXtermFKeys\n"));
5281 	    screen->old_fkeys = True;
5282 	    screen->old_fkeys0 = True;
5283 	}
5284 
5285 	/*
5286 	 * Choose an individual keyboard type.
5287 	 */
5288 	for (n = 0; n < XtNumber(table); ++n) {
5289 	    if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
5290 		FLAG(n) = True;
5291 		found = True;
5292 	    } else {
5293 		FLAG(n) = False;
5294 	    }
5295 	    init_keyboard_type(xw, table[n].type, FLAG(n));
5296 	}
5297 	if (!found) {
5298 	    xtermWarning("KeyboardType resource \"%s\" not found\n",
5299 			 rp->keyboardType);
5300 	}
5301     }
5302 #undef DATA
5303 #undef FLAG
5304 }
5305 
5306 #if OPT_WIDE_CHARS
5307 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5308 /*
5309  * If xterm is running in a UTF-8 locale, it is still possible to encounter
5310  * old runtime configurations which yield incomplete or inaccurate data.
5311  */
5312 static Bool
systemWcwidthOk(int samplesize,int samplepass)5313 systemWcwidthOk(int samplesize, int samplepass)
5314 {
5315     wchar_t n;
5316     int oops = 0;
5317 
5318     for (n = 21; n <= 25; ++n) {
5319 	wchar_t code = (wchar_t) dec2ucs(NULL, (unsigned) n);
5320 	int system_code = wcwidth(code);
5321 	int intern_code = mk_wcwidth(code);
5322 
5323 	/*
5324 	 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
5325 	 * 0x2500) and most of the geometric shapes (a few are excluded, just
5326 	 * to make it more difficult to use).  Do a sanity check to avoid using
5327 	 * it.
5328 	 */
5329 	if ((system_code < 0 && intern_code >= 1)
5330 	    || (system_code >= 0 && intern_code != system_code)) {
5331 	    TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
5332 	    oops += (samplepass + 1);
5333 	    break;
5334 	}
5335     }
5336 
5337     for (n = 0; n < (wchar_t) samplesize; ++n) {
5338 	int system_code = wcwidth(n);
5339 	int intern_code = mk_wcwidth(n);
5340 
5341 	/*
5342 	 * When this check was originally implemented, there were few if any
5343 	 * libraries with full Unicode coverage.  Time passes, and it is
5344 	 * possible to make a full comparison of the BMP.  There are some
5345 	 * differences: mk_wcwidth() marks some codes as combining and some
5346 	 * as single-width, differing from GNU libc.
5347 	 */
5348 	if ((system_code < 0 && intern_code >= 1)
5349 	    || (system_code >= 0 && intern_code != system_code)) {
5350 	    TRACE((".. width(U+%04X) = %d, expected %d\n",
5351 		   (unsigned) n, system_code, intern_code));
5352 	    if (++oops > samplepass)
5353 		break;
5354 	}
5355     }
5356     TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
5357 	   oops, (int) n, samplepass));
5358     return (oops <= samplepass);
5359 }
5360 #endif /* HAVE_WCWIDTH */
5361 
5362 void
decode_wcwidth(XtermWidget xw)5363 decode_wcwidth(XtermWidget xw)
5364 {
5365     int mode = ((xw->misc.cjk_width ? 2 : 0)
5366 		+ (xw->misc.mk_width ? 1 : 0)
5367 		+ 1);
5368 
5369     switch (mode) {
5370     default:
5371 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5372 	if (xtermEnvUTF8() &&
5373 	    systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {
5374 	    my_wcwidth = wcwidth;
5375 	    TRACE(("using system wcwidth() function\n"));
5376 	    break;
5377 	}
5378 #endif
5379 	/* FALLTHRU */
5380     case 2:
5381 	my_wcwidth = &mk_wcwidth;
5382 	TRACE(("using MK wcwidth() function\n"));
5383 	break;
5384     case 3:
5385 	/* FALLTHRU */
5386     case 4:
5387 	my_wcwidth = &mk_wcwidth_cjk;
5388 	TRACE(("using MK-CJK wcwidth() function\n"));
5389 	break;
5390     }
5391 
5392     for (first_widechar = 128; first_widechar < 4500; ++first_widechar) {
5393 	if (my_wcwidth((wchar_t) first_widechar) > 1) {
5394 	    TRACE(("first_widechar %#x\n", first_widechar));
5395 	    break;
5396 	}
5397     }
5398 }
5399 #endif
5400 
5401 /*
5402  * Extend a (normally) boolean resource value by checking for additional values
5403  * which will be mapped into true/false.
5404  */
5405 int
extendedBoolean(const char * value,const FlagList * table,Cardinal limit)5406 extendedBoolean(const char *value, const FlagList * table, Cardinal limit)
5407 {
5408     int result = -1;
5409     long check;
5410     char *next;
5411     Cardinal n;
5412 
5413     if ((x_strcasecmp(value, "true") == 0)
5414 	|| (x_strcasecmp(value, "yes") == 0)
5415 	|| (x_strcasecmp(value, "on") == 0)) {
5416 	result = True;
5417     } else if ((x_strcasecmp(value, "false") == 0)
5418 	       || (x_strcasecmp(value, "no") == 0)
5419 	       || (x_strcasecmp(value, "off") == 0)) {
5420 	result = False;
5421     } else if ((check = strtol(value, &next, 0)) >= 0 && FullS2L(value, next)) {
5422 	if (check >= (long) limit)	/* i.e., past False=0, True=1 */
5423 	    check = True;
5424 	result = (int) check;
5425     } else {
5426 	for (n = 0; n < limit - 2; ++n) {
5427 	    if (table[n].name == NULL) {
5428 		break;
5429 	    } else if (x_strcasecmp(value, table[n].name) == 0) {
5430 		result = table[n].code;
5431 		break;
5432 	    }
5433 	}
5434     }
5435 
5436     if (result < 0) {
5437 	xtermWarning("Unrecognized keyword: %s\n", value);
5438 	result = False;
5439     }
5440 
5441     TRACE(("extendedBoolean(%s) = %d\n", value, result));
5442     return result;
5443 }
5444 
5445 /*
5446  * Something like round() from math library, but round() is less widely-used
5447  * than xterm.  Also, there are no negative numbers to complicate this.
5448  */
5449 int
dimRound(double value)5450 dimRound(double value)
5451 {
5452     int result = (int) value;
5453     if (result < value)
5454 	++result;
5455     return result;
5456 }
5457 
5458 /*
5459  * Find the geometry of the specified Xinerama screen
5460  */
5461 static void
find_xinerama_screen(Display * display,int screen,struct Xinerama_geometry * ret)5462 find_xinerama_screen(Display *display, int screen, struct Xinerama_geometry *ret)
5463 {
5464 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
5465     XineramaScreenInfo *screens;
5466     int nb_screens;
5467 
5468     if (screen == -1)		/* already inited */
5469 	return;
5470     screens = XineramaQueryScreens(display, &nb_screens);
5471     if (screen >= nb_screens) {
5472 	xtermWarning("Xinerama screen %d does not exist\n", screen);
5473 	return;
5474     }
5475     if (screen == -2) {
5476 	int ptr_x, ptr_y;
5477 	int dummy_int, i;
5478 	unsigned dummy_uint;
5479 	Window dummy_win;
5480 	if (nb_screens == 0)
5481 	    return;
5482 	XQueryPointer(display, DefaultRootWindow(display),
5483 		      &dummy_win, &dummy_win,
5484 		      &ptr_x, &ptr_y,
5485 		      &dummy_int, &dummy_int, &dummy_uint);
5486 	for (i = 0; i < nb_screens; i++) {
5487 	    if ((ptr_x - screens[i].x_org) < screens[i].width &&
5488 		(ptr_y - screens[i].y_org) < screens[i].height) {
5489 		screen = i;
5490 		break;
5491 	    }
5492 	}
5493 	if (screen < 0) {
5494 	    xtermWarning("Mouse not in any Xinerama screen, using 0\n");
5495 	    screen = 0;
5496 	}
5497     }
5498     ret->scr_x = screens[screen].x_org;
5499     ret->scr_y = screens[screen].y_org;
5500     ret->scr_w = screens[screen].width;
5501     ret->scr_h = screens[screen].height;
5502 #else /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5503     (void) display;
5504     (void) ret;
5505     if (screen > 0)
5506 	xtermWarning("Xinerama support not enabled\n");
5507 #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5508 }
5509 
5510 /*
5511  * Parse the screen code after the @ in a geometry string.
5512  */
5513 static void
parse_xinerama_screen(Display * display,const char * str,struct Xinerama_geometry * ret)5514 parse_xinerama_screen(Display *display, const char *str, struct Xinerama_geometry *ret)
5515 {
5516     int screen = -1;
5517     char *end;
5518 
5519     if (*str == 'g') {
5520 	screen = -1;
5521 	str++;
5522     } else if (*str == 'c') {
5523 	screen = -2;
5524 	str++;
5525     } else {
5526 	long s = strtol(str, &end, 0);
5527 	if (FullS2L(str, end) && ((int) s >= 0)) {
5528 	    screen = (int) s;
5529 	    str = end;
5530 	}
5531     }
5532     if (*str) {
5533 	xtermWarning("invalid Xinerama specification '%s'\n", str);
5534 	return;
5535     }
5536     if (screen == -1)		/* already done */
5537 	return;
5538     find_xinerama_screen(display, screen, ret);
5539 }
5540 
5541 /*
5542  * Parse a geometry string with extra Xinerama specification:
5543  * <w>x<h>+<x>+<y>@<screen>.
5544  */
5545 int
XParseXineramaGeometry(Display * display,char * parsestring,struct Xinerama_geometry * ret)5546 XParseXineramaGeometry(Display *display, char *parsestring, struct Xinerama_geometry *ret)
5547 {
5548     char *at, buf[128];
5549 
5550     ret->scr_x = 0;
5551     ret->scr_y = 0;
5552     ret->scr_w = DisplayWidth(display, DefaultScreen(display));
5553     ret->scr_h = DisplayHeight(display, DefaultScreen(display));
5554     at = strchr(parsestring, '@');
5555     if (at != NULL && (size_t) (at - parsestring) < sizeof(buf) - 1) {
5556 	memcpy(buf, parsestring, (size_t) (at - parsestring));
5557 	buf[at - parsestring] = 0;
5558 	parsestring = buf;
5559 	parse_xinerama_screen(display, at + 1, ret);
5560     }
5561     return ((strlen(parsestring) <= MAX_U_STRING)
5562 	    ? XParseGeometry(parsestring, &ret->x, &ret->y, &ret->w, &ret->h)
5563 	    : 0);
5564 }
5565 
5566 #if USE_DOUBLE_BUFFER
5567 Window
VDrawable(TScreen * screen)5568 VDrawable(TScreen *screen)
5569 {
5570     screen->needSwap = 1;
5571     return WhichVWin(screen)->drawable;
5572 }
5573 #endif
5574 
5575 #if OPT_RENDERFONT
5576 #ifndef discardRenderDraw
5577 void
discardRenderDraw(TScreen * screen)5578 discardRenderDraw(TScreen *screen)
5579 {
5580     if (
5581 #if USE_DOUBLE_BUFFER
5582 	   resource.buffered &&
5583 #endif
5584 	   screen->renderDraw) {
5585 	XftDrawDestroy(screen->renderDraw);
5586 	screen->renderDraw = NULL;
5587     }
5588 }
5589 #endif
5590 #endif /* OPT_RENDERFONT */
5591 
5592 char *
xtermSetLocale(int category,String after)5593 xtermSetLocale(int category, String after)
5594 {
5595     char *before = x_strdup(setlocale(category, 0));
5596 
5597     (void) setlocale(category, after);
5598     TRACE(("before setlocale :%s\n", NonNull(before)));
5599     TRACE(("updated locale   :%s\n", NonNull(setlocale(category, 0))));
5600     return before;
5601 }
5602 
5603 void
xtermResetLocale(int category,char * before)5604 xtermResetLocale(int category, char *before)
5605 {
5606     (void) setlocale(category, before);
5607     free(before);
5608     TRACE(("restored locale  :%s\n", NonNull(setlocale(category, 0))));
5609 }
5610