initial commit
[profile/ivi/xterm.git] / util.c
1 /* $XTermId: util.c,v 1.543 2011/02/09 10:11:44 tom Exp $ */
2
3 /*
4  * Copyright 1999-2010,2011 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 <ctype.h>
67 #include <assert.h>
68
69 #if OPT_WIDE_CHARS
70 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
71 #include <wchar.h>
72 #endif
73 #include <wcwidth.h>
74 #endif
75
76 static int handle_translated_exposure(XtermWidget xw,
77                                       int rect_x,
78                                       int rect_y,
79                                       int rect_width,
80                                       int rect_height);
81 static void ClearLeft(XtermWidget xw);
82 static void CopyWait(XtermWidget xw);
83 static void horizontal_copy_area(XtermWidget xw,
84                                  int firstchar,
85                                  int nchars,
86                                  int amount);
87 static void vertical_copy_area(XtermWidget xw,
88                                int firstline,
89                                int nlines,
90                                int amount);
91
92 #if OPT_WIDE_CHARS
93 unsigned first_widechar;
94 int (*my_wcwidth) (wchar_t);
95 #endif
96
97 #if OPT_WIDE_CHARS
98 /*
99  * We will modify the 'n' cells beginning at the current position.
100  * Some of those cells may be part of multi-column characters, including
101  * carryover from the left.  Find the limits of the multi-column characters
102  * that we should fill with blanks, return true if filling is needed.
103  */
104 int
105 DamagedCells(TScreen * screen, unsigned n, int *klp, int *krp, int row, int col)
106 {
107     LineData *ld = getLineData(screen, row);
108     int result = False;
109
110     assert(ld);
111     if (col < (int) ld->lineSize) {
112         int nn = (int) n;
113         int kl = col;
114         int kr = col + nn;
115
116         if (kr >= ld->lineSize) {
117             nn = (ld->lineSize - col - 1);
118             kr = col + nn;
119         }
120
121         if (nn > 0) {
122             assert(kl < ld->lineSize);
123             if (ld->charData[kl] == HIDDEN_CHAR) {
124                 while (kl > 0) {
125                     if (ld->charData[--kl] != HIDDEN_CHAR) {
126                         break;
127                     }
128                 }
129             } else {
130                 kl = col + 1;
131             }
132
133             assert(kr < (int) ld->lineSize);
134             if (ld->charData[kr] == HIDDEN_CHAR) {
135                 while (kr < screen->max_col) {
136                     assert((kr + 1) < (int) ld->lineSize);
137                     if (ld->charData[++kr] != HIDDEN_CHAR) {
138                         --kr;
139                         break;
140                     }
141                 }
142             } else {
143                 kr = col - 1;
144             }
145
146             if (klp)
147                 *klp = kl;
148             if (krp)
149                 *krp = kr;
150             result = (kr >= kl);
151         }
152     }
153
154     return result;
155 }
156
157 int
158 DamagedCurCells(TScreen * screen, unsigned n, int *klp, int *krp)
159 {
160     return DamagedCells(screen, n, klp, krp, screen->cur_row, screen->cur_col);
161 }
162 #endif /* OPT_WIDE_CHARS */
163
164 /*
165  * These routines are used for the jump scroll feature
166  */
167 void
168 FlushScroll(XtermWidget xw)
169 {
170     TScreen *screen = TScreenOf(xw);
171     int i;
172     int shift = INX2ROW(screen, 0);
173     int bot = screen->max_row - shift;
174     int refreshtop;
175     int refreshheight;
176     int scrolltop;
177     int scrollheight;
178
179     if (screen->cursor_state)
180         HideCursor();
181     if (screen->scroll_amt > 0) {
182         refreshheight = screen->refresh_amt;
183         scrollheight = screen->bot_marg - screen->top_marg -
184             refreshheight + 1;
185         if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
186             (i = screen->max_row - screen->scroll_amt + 1))
187             refreshtop = i;
188         if (screen->scrollWidget
189             && !screen->whichBuf
190             && screen->top_marg == 0) {
191             scrolltop = 0;
192             if ((scrollheight += shift) > i)
193                 scrollheight = i;
194             if ((i = screen->bot_marg - bot) > 0 &&
195                 (refreshheight -= i) < screen->scroll_amt)
196                 refreshheight = screen->scroll_amt;
197             if ((i = screen->savedlines) < screen->savelines) {
198                 if ((i += screen->scroll_amt) >
199                     screen->savelines)
200                     i = screen->savelines;
201                 screen->savedlines = i;
202                 ScrollBarDrawThumb(screen->scrollWidget);
203             }
204         } else {
205             scrolltop = screen->top_marg + shift;
206             if ((i = bot - (screen->bot_marg - screen->refresh_amt +
207                             screen->scroll_amt)) > 0) {
208                 if (bot < screen->bot_marg)
209                     refreshheight = screen->scroll_amt + i;
210             } else {
211                 scrollheight += i;
212                 refreshheight = screen->scroll_amt;
213                 if ((i = screen->top_marg + screen->scroll_amt -
214                      1 - bot) > 0) {
215                     refreshtop += i;
216                     refreshheight -= i;
217                 }
218             }
219         }
220     } else {
221         refreshheight = -screen->refresh_amt;
222         scrollheight = screen->bot_marg - screen->top_marg -
223             refreshheight + 1;
224         refreshtop = screen->top_marg + shift;
225         scrolltop = refreshtop + refreshheight;
226         if ((i = screen->bot_marg - bot) > 0)
227             scrollheight -= i;
228         if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
229             refreshheight -= i;
230     }
231     scrolling_copy_area(xw, scrolltop + screen->scroll_amt,
232                         scrollheight, screen->scroll_amt);
233     ScrollSelection(screen, -(screen->scroll_amt), False);
234     screen->scroll_amt = 0;
235     screen->refresh_amt = 0;
236     if (refreshheight > 0) {
237         ClearCurBackground(xw,
238                            (int) refreshtop * FontHeight(screen) + screen->border,
239                            (int) OriginX(screen),
240                            (unsigned) (refreshheight * FontHeight(screen)),
241                            (unsigned) Width(screen));
242         ScrnRefresh(xw, refreshtop, 0, refreshheight,
243                     MaxCols(screen), False);
244     }
245     return;
246 }
247
248 /*
249  * Returns true if there are lines off-screen due to scrolling which should
250  * include the current line.  If false, the line is visible and we should
251  * paint it now rather than waiting for the line to become visible.
252  */
253 int
254 AddToRefresh(XtermWidget xw)
255 {
256     TScreen *screen = TScreenOf(xw);
257     int amount = screen->refresh_amt;
258     int row = screen->cur_row;
259     int result;
260
261     if (amount == 0) {
262         result = 0;
263     } else if (amount > 0) {
264         int bottom;
265
266         if (row == (bottom = screen->bot_marg) - amount) {
267             screen->refresh_amt++;
268             result = 1;
269         } else {
270             result = (row >= bottom - amount + 1 && row <= bottom);
271         }
272     } else {
273         int top;
274
275         amount = -amount;
276         if (row == (top = screen->top_marg) + amount) {
277             screen->refresh_amt--;
278             result = 1;
279         } else {
280             result = (row <= top + amount - 1 && row >= top);
281         }
282     }
283
284     /*
285      * If this line is visible, and there are scrolled-off lines, flush out
286      * those which are now visible.
287      */
288     if (!result && screen->scroll_amt)
289         FlushScroll(xw);
290
291     return result;
292 }
293
294 /*
295  * Returns true if the current row is in the visible area (it should be for
296  * screen operations) and incidentally flush the scrolled-in lines which
297  * have newly become visible.
298  */
299 static Bool
300 AddToVisible(XtermWidget xw)
301 {
302     TScreen *screen = TScreenOf(xw);
303     Bool result = False;
304
305     if (INX2ROW(screen, screen->cur_row) <= screen->max_row) {
306         if (!AddToRefresh(xw)) {
307             result = True;
308         }
309     }
310     return result;
311 }
312
313 /*
314  * If we're scrolling, leave the selection intact if possible.
315  * If it will bump into one of the extremes of the saved-lines, truncate that.
316  * If the selection is not contained within the scrolled region, clear it.
317  */
318 static void
319 adjustHiliteOnFwdScroll(XtermWidget xw, int amount, Bool all_lines)
320 {
321     TScreen *screen = TScreenOf(xw);
322     int lo_row = (all_lines
323                   ? (screen->bot_marg - screen->savelines)
324                   : screen->top_marg);
325     int hi_row = screen->bot_marg;
326
327     TRACE2(("adjustSelection FWD %s by %d (%s)\n",
328             screen->whichBuf ? "alternate" : "normal",
329             amount,
330             all_lines ? "all" : "visible"));
331     TRACE2(("  before highlite %d.%d .. %d.%d\n",
332             screen->startH.row,
333             screen->startH.col,
334             screen->endH.row,
335             screen->endH.col));
336     TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
337     TRACE2(("  limits  %d..%d\n", lo_row, hi_row));
338
339     if (screen->startH.row >= lo_row
340         && screen->startH.row - amount < lo_row) {
341         /* truncate the selection because its start would move out of region */
342         if (lo_row + amount <= screen->endH.row) {
343             TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
344                     screen->startH.row,
345                     screen->startH.col,
346                     lo_row + amount,
347                     0));
348             screen->startH.row = lo_row + amount;
349             screen->startH.col = 0;
350         } else {
351             TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
352                     screen->startH.row,
353                     screen->startH.col,
354                     screen->endH.row,
355                     screen->endH.col,
356                     -amount,
357                     lo_row,
358                     hi_row));
359             ScrnDisownSelection(xw);
360         }
361     } else if (screen->startH.row <= hi_row && screen->endH.row > hi_row) {
362         ScrnDisownSelection(xw);
363     } else if (screen->startH.row < lo_row && screen->endH.row > lo_row) {
364         ScrnDisownSelection(xw);
365     }
366
367     TRACE2(("  after highlite %d.%d .. %d.%d\n",
368             screen->startH.row,
369             screen->startH.col,
370             screen->endH.row,
371             screen->endH.col));
372 }
373
374 /*
375  * This is the same as adjustHiliteOnFwdScroll(), but reversed.  In this case,
376  * only the visible lines are affected.
377  */
378 static void
379 adjustHiliteOnBakScroll(XtermWidget xw, int amount)
380 {
381     TScreen *screen = TScreenOf(xw);
382     int lo_row = screen->top_marg;
383     int hi_row = screen->bot_marg;
384
385     TRACE2(("adjustSelection BAK %s by %d (%s)\n",
386             screen->whichBuf ? "alternate" : "normal",
387             amount,
388             "visible"));
389     TRACE2(("  before highlite %d.%d .. %d.%d\n",
390             screen->startH.row,
391             screen->startH.col,
392             screen->endH.row,
393             screen->endH.col));
394     TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
395
396     if (screen->endH.row >= hi_row
397         && screen->endH.row + amount > hi_row) {
398         /* truncate the selection because its start would move out of region */
399         if (hi_row - amount >= screen->startH.row) {
400             TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
401                     screen->startH.row,
402                     screen->startH.col,
403                     hi_row - amount,
404                     0));
405             screen->endH.row = hi_row - amount;
406             screen->endH.col = 0;
407         } else {
408             TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
409                     screen->startH.row,
410                     screen->startH.col,
411                     screen->endH.row,
412                     screen->endH.col,
413                     amount,
414                     lo_row,
415                     hi_row));
416             ScrnDisownSelection(xw);
417         }
418     } else if (screen->endH.row >= lo_row && screen->startH.row < lo_row) {
419         ScrnDisownSelection(xw);
420     } else if (screen->endH.row > hi_row && screen->startH.row > hi_row) {
421         ScrnDisownSelection(xw);
422     }
423
424     TRACE2(("  after highlite %d.%d .. %d.%d\n",
425             screen->startH.row,
426             screen->startH.col,
427             screen->endH.row,
428             screen->endH.col));
429 }
430
431 /*
432  * scrolls the screen by amount lines, erases bottom, doesn't alter
433  * cursor position (i.e. cursor moves down amount relative to text).
434  * All done within the scrolling region, of course.
435  * requires: amount > 0
436  */
437 void
438 xtermScroll(XtermWidget xw, int amount)
439 {
440     TScreen *screen = TScreenOf(xw);
441     int i = screen->bot_marg - screen->top_marg + 1;
442     int shift;
443     int bot;
444     int refreshtop = 0;
445     int refreshheight;
446     int scrolltop;
447     int scrollheight;
448     Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
449                                           && !screen->whichBuf
450                                           && screen->top_marg == 0);
451
452     TRACE(("xtermScroll count=%d\n", amount));
453
454     screen->cursor_busy += 1;
455     screen->cursor_moved = True;
456
457     if (screen->cursor_state)
458         HideCursor();
459
460     if (amount > i)
461         amount = i;
462
463 #if OPT_SCROLL_LOCK
464     if (screen->allowScrollLock && screen->scroll_lock) {
465         refreshheight = 0;
466         screen->scroll_amt = 0;
467         screen->refresh_amt = 0;
468         if (--(screen->topline) < -screen->savelines) {
469             screen->topline = -screen->savelines;
470             screen->scroll_dirty = True;
471         }
472         if (++(screen->savedlines) > screen->savelines) {
473             screen->savedlines = screen->savelines;
474         }
475     } else
476 #endif
477     {
478         if (ScrnHaveSelection(screen))
479             adjustHiliteOnFwdScroll(xw, amount, scroll_all_lines);
480
481         if (screen->jumpscroll) {
482             if (screen->scroll_amt > 0) {
483                 if (!screen->fastscroll) {
484                     if (screen->refresh_amt + amount > i)
485                         FlushScroll(xw);
486                 }
487                 screen->scroll_amt += amount;
488                 screen->refresh_amt += amount;
489             } else {
490                 if (!screen->fastscroll) {
491                     if (screen->scroll_amt < 0)
492                         FlushScroll(xw);
493                 }
494                 screen->scroll_amt = amount;
495                 screen->refresh_amt = amount;
496             }
497             refreshheight = 0;
498         } else {
499             ScrollSelection(screen, -(amount), False);
500             if (amount == i) {
501                 ClearScreen(xw);
502                 screen->cursor_busy -= 1;
503                 return;
504             }
505
506             shift = INX2ROW(screen, 0);
507             bot = screen->max_row - shift;
508             scrollheight = i - amount;
509             refreshheight = amount;
510
511             if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
512                 (i = screen->max_row - refreshheight + 1))
513                 refreshtop = i;
514
515             if (scroll_all_lines) {
516                 scrolltop = 0;
517                 if ((scrollheight += shift) > i)
518                     scrollheight = i;
519                 if ((i = screen->savedlines) < screen->savelines) {
520                     if ((i += amount) > screen->savelines)
521                         i = screen->savelines;
522                     screen->savedlines = i;
523                     ScrollBarDrawThumb(screen->scrollWidget);
524                 }
525             } else {
526                 scrolltop = screen->top_marg + shift;
527                 if ((i = screen->bot_marg - bot) > 0) {
528                     scrollheight -= i;
529                     if ((i = screen->top_marg + amount - 1 - bot) >= 0) {
530                         refreshtop += i;
531                         refreshheight -= i;
532                     }
533                 }
534             }
535
536             if (screen->multiscroll && amount == 1 &&
537                 screen->topline == 0 && screen->top_marg == 0 &&
538                 screen->bot_marg == screen->max_row) {
539                 if (screen->incopy < 0 && screen->scrolls == 0)
540                     CopyWait(xw);
541                 screen->scrolls++;
542             }
543
544             scrolling_copy_area(xw, scrolltop + amount, scrollheight, amount);
545
546             if (refreshheight > 0) {
547                 ClearCurBackground(xw,
548                                    (int) refreshtop * FontHeight(screen) + screen->border,
549                                    (int) OriginX(screen),
550                                    (unsigned) (refreshheight * FontHeight(screen)),
551                                    (unsigned) Width(screen));
552                 if (refreshheight > shift)
553                     refreshheight = shift;
554             }
555         }
556     }
557
558     if (amount > 0) {
559         if (scroll_all_lines) {
560             ScrnDeleteLine(xw,
561                            screen->saveBuf_index,
562                            screen->bot_marg + screen->savelines,
563                            0,
564                            (unsigned) amount);
565         } else {
566             ScrnDeleteLine(xw,
567                            screen->visbuf,
568                            screen->bot_marg,
569                            screen->top_marg,
570                            (unsigned) amount);
571         }
572     }
573
574     if (refreshheight > 0) {
575         ScrnRefresh(xw, refreshtop, 0, refreshheight,
576                     MaxCols(screen), False);
577     }
578
579     screen->cursor_busy -= 1;
580     return;
581 }
582
583 /*
584  * Reverse scrolls the screen by amount lines, erases top, doesn't alter
585  * cursor position (i.e. cursor moves up amount relative to text).
586  * All done within the scrolling region, of course.
587  * Requires: amount > 0
588  */
589 void
590 RevScroll(XtermWidget xw, int amount)
591 {
592     TScreen *screen = TScreenOf(xw);
593     int i = screen->bot_marg - screen->top_marg + 1;
594     int shift;
595     int bot;
596     int refreshtop;
597     int refreshheight;
598     int scrolltop;
599     int scrollheight;
600
601     TRACE(("RevScroll count=%d\n", amount));
602
603     screen->cursor_busy += 1;
604     screen->cursor_moved = True;
605
606     if (screen->cursor_state)
607         HideCursor();
608
609     if (amount > i)
610         amount = i;
611
612     if (ScrnHaveSelection(screen))
613         adjustHiliteOnBakScroll(xw, amount);
614
615     if (screen->jumpscroll) {
616         if (screen->scroll_amt < 0) {
617             if (-screen->refresh_amt + amount > i)
618                 FlushScroll(xw);
619             screen->scroll_amt -= amount;
620             screen->refresh_amt -= amount;
621         } else {
622             if (screen->scroll_amt > 0)
623                 FlushScroll(xw);
624             screen->scroll_amt = -amount;
625             screen->refresh_amt = -amount;
626         }
627     } else {
628         shift = INX2ROW(screen, 0);
629         bot = screen->max_row - shift;
630         refreshheight = amount;
631         scrollheight = screen->bot_marg - screen->top_marg -
632             refreshheight + 1;
633         refreshtop = screen->top_marg + shift;
634         scrolltop = refreshtop + refreshheight;
635         if ((i = screen->bot_marg - bot) > 0)
636             scrollheight -= i;
637         if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
638             refreshheight -= i;
639
640         if (screen->multiscroll && amount == 1 &&
641             screen->topline == 0 && screen->top_marg == 0 &&
642             screen->bot_marg == screen->max_row) {
643             if (screen->incopy < 0 && screen->scrolls == 0)
644                 CopyWait(xw);
645             screen->scrolls++;
646         }
647
648         scrolling_copy_area(xw, scrolltop - amount, scrollheight, -amount);
649
650         if (refreshheight > 0) {
651             ClearCurBackground(xw,
652                                (int) refreshtop * FontHeight(screen) + screen->border,
653                                (int) OriginX(screen),
654                                (unsigned) (refreshheight * FontHeight(screen)),
655                                (unsigned) Width(screen));
656         }
657     }
658     if (amount > 0) {
659         ScrnInsertLine(xw,
660                        screen->visbuf,
661                        screen->bot_marg,
662                        screen->top_marg,
663                        (unsigned) amount);
664     }
665     screen->cursor_busy -= 1;
666     return;
667 }
668
669 /*
670  * write a string str of length len onto the screen at
671  * the current cursor position.  update cursor position.
672  */
673 void
674 WriteText(XtermWidget xw, IChar * str, Cardinal len)
675 {
676     TScreen *screen = TScreenOf(xw);
677     LineData *ld = 0;
678     int fg;
679     unsigned test;
680     unsigned flags = xw->flags;
681     CellColor fg_bg = makeColorPair(xw->cur_foreground, xw->cur_background);
682     unsigned cells = visual_width(str, len);
683     GC currentGC;
684
685     TRACE(("WriteText %d (%2d,%2d) %3d:%s\n",
686            screen->topline,
687            screen->cur_row,
688            screen->cur_col,
689            len, visibleIChar(str, len)));
690
691     if (cells + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) {
692         cells = (unsigned) (MaxCols(screen) - screen->cur_col);
693     }
694
695     if (ScrnHaveSelection(screen)
696         && ScrnIsLineInSelection(screen, INX2ROW(screen, screen->cur_row))) {
697         ScrnDisownSelection(xw);
698     }
699
700     /* if we are in insert-mode, reserve space for the new cells */
701     if (flags & INSERT) {
702         InsertChar(xw, cells);
703     }
704
705     if (AddToVisible(xw)
706         && ((ld = getLineData(screen, screen->cur_row))) != 0) {
707         if (screen->cursor_state)
708             HideCursor();
709
710         /*
711          * If we overwrite part of a multi-column character, fill the rest
712          * of it with blanks.
713          */
714         if_OPT_WIDE_CHARS(screen, {
715             int kl;
716             int kr;
717             if (DamagedCurCells(screen, cells, &kl, &kr))
718                 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
719         });
720
721         if (flags & INVISIBLE) {
722             Cardinal n;
723             for (n = 0; n < cells; ++n)
724                 str[n] = ' ';
725         }
726
727         TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n",
728                LineCharSet(screen, ld),
729                screen->cur_col,
730                screen->cur_row));
731
732         test = flags;
733 #if OPT_ISO_COLORS
734         if (screen->colorAttrMode) {
735             fg = MapToColorMode(xw->cur_foreground, screen, flags);
736         } else {
737             fg = xw->cur_foreground;
738         }
739         checkVeryBoldColors(test, fg);
740 #endif
741
742         /* make sure that the correct GC is current */
743         currentGC = updatedXtermGC(xw, flags, fg_bg, False);
744
745         drawXtermText(xw, test & DRAWX_MASK, currentGC,
746                       LineCursorX(screen, ld, screen->cur_col),
747                       CursorY(screen, screen->cur_row),
748                       LineCharSet(screen, ld),
749                       str, len, 0);
750
751         resetXtermGC(xw, flags, False);
752     }
753
754     ScrnWriteText(xw, str, flags, fg_bg, len);
755     CursorForward(screen, (int) cells);
756 #if OPT_ZICONBEEP
757     /* Flag icon name with "***"  on window output when iconified.
758      */
759     if (resource.zIconBeep && mapstate == IsUnmapped && !screen->zIconBeep_flagged) {
760         static char *icon_name;
761         static Arg args[] =
762         {
763             {XtNiconName, (XtArgVal) & icon_name}
764         };
765
766         icon_name = NULL;
767         XtGetValues(toplevel, args, XtNumber(args));
768
769         if (icon_name != NULL) {
770             screen->zIconBeep_flagged = True;
771             ChangeIconName(xw, icon_name);
772         }
773         xtermBell(xw, XkbBI_Info, 0);
774     }
775     mapstate = -1;
776 #endif /* OPT_ZICONBEEP */
777     return;
778 }
779
780 /*
781  * If cursor not in scrolling region, returns.  Else,
782  * inserts n blank lines at the cursor's position.  Lines above the
783  * bottom margin are lost.
784  */
785 void
786 InsertLine(XtermWidget xw, int n)
787 {
788     TScreen *screen = TScreenOf(xw);
789     int i;
790     int shift;
791     int bot;
792     int refreshtop;
793     int refreshheight;
794     int scrolltop;
795     int scrollheight;
796
797     if (!ScrnIsLineInMargins(screen, screen->cur_row))
798         return;
799
800     TRACE(("InsertLine count=%d\n", n));
801
802     if (screen->cursor_state)
803         HideCursor();
804
805     if (ScrnHaveSelection(screen)
806         && ScrnAreLinesInSelection(screen,
807                                    INX2ROW(screen, screen->top_marg),
808                                    INX2ROW(screen, screen->cur_row - 1))
809         && ScrnAreLinesInSelection(screen,
810                                    INX2ROW(screen, screen->cur_row),
811                                    INX2ROW(screen, screen->bot_marg))) {
812         ScrnDisownSelection(xw);
813     }
814
815     screen->do_wrap = False;
816     if (n > (i = screen->bot_marg - screen->cur_row + 1))
817         n = i;
818     if (screen->jumpscroll) {
819         if (screen->scroll_amt <= 0 &&
820             screen->cur_row <= -screen->refresh_amt) {
821             if (-screen->refresh_amt + n > MaxRows(screen))
822                 FlushScroll(xw);
823             screen->scroll_amt -= n;
824             screen->refresh_amt -= n;
825         } else {
826             if (screen->scroll_amt)
827                 FlushScroll(xw);
828         }
829     }
830     if (!screen->scroll_amt) {
831         shift = INX2ROW(screen, 0);
832         bot = screen->max_row - shift;
833         refreshheight = n;
834         scrollheight = screen->bot_marg - screen->cur_row - refreshheight + 1;
835         refreshtop = screen->cur_row + shift;
836         scrolltop = refreshtop + refreshheight;
837         if ((i = screen->bot_marg - bot) > 0)
838             scrollheight -= i;
839         if ((i = screen->cur_row + refreshheight - 1 - bot) > 0)
840             refreshheight -= i;
841         vertical_copy_area(xw, scrolltop - n, scrollheight, -n);
842         if (refreshheight > 0) {
843             ClearCurBackground(xw,
844                                (int) refreshtop * FontHeight(screen) + screen->border,
845                                (int) OriginX(screen),
846                                (unsigned) (refreshheight * FontHeight(screen)),
847                                (unsigned) Width(screen));
848         }
849     }
850     if (n > 0) {
851         ScrnInsertLine(xw,
852                        screen->visbuf,
853                        screen->bot_marg,
854                        screen->cur_row,
855                        (unsigned) n);
856     }
857 }
858
859 /*
860  * If cursor not in scrolling region, returns.  Else, deletes n lines
861  * at the cursor's position, lines added at bottom margin are blank.
862  */
863 void
864 DeleteLine(XtermWidget xw, int n)
865 {
866     TScreen *screen = TScreenOf(xw);
867     int i;
868     int shift;
869     int bot;
870     int refreshtop;
871     int refreshheight;
872     int scrolltop;
873     int scrollheight;
874     Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
875                                           && !screen->whichBuf
876                                           && screen->cur_row == 0);
877
878     if (!ScrnIsLineInMargins(screen, screen->cur_row))
879         return;
880
881     TRACE(("DeleteLine count=%d\n", n));
882
883     if (screen->cursor_state)
884         HideCursor();
885
886     if (n > (i = screen->bot_marg - screen->cur_row + 1)) {
887         n = i;
888     }
889     if (ScrnHaveSelection(screen)
890         && ScrnAreLinesInSelection(screen,
891                                    INX2ROW(screen, screen->cur_row),
892                                    INX2ROW(screen, screen->cur_row + n - 1))) {
893         ScrnDisownSelection(xw);
894     }
895
896     screen->do_wrap = False;
897     if (screen->jumpscroll) {
898         if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) {
899             if (screen->refresh_amt + n > MaxRows(screen))
900                 FlushScroll(xw);
901             screen->scroll_amt += n;
902             screen->refresh_amt += n;
903         } else {
904             if (screen->scroll_amt)
905                 FlushScroll(xw);
906         }
907     }
908
909     /* adjust screen->buf */
910     if (n > 0) {
911         if (scroll_all_lines)
912             ScrnDeleteLine(xw,
913                            screen->saveBuf_index,
914                            screen->bot_marg + screen->savelines,
915                            0,
916                            (unsigned) n);
917         else
918             ScrnDeleteLine(xw,
919                            screen->visbuf,
920                            screen->bot_marg,
921                            screen->cur_row,
922                            (unsigned) n);
923     }
924
925     /* repaint the screen, as needed */
926     if (!screen->scroll_amt) {
927         shift = INX2ROW(screen, 0);
928         bot = screen->max_row - shift;
929         scrollheight = i - n;
930         refreshheight = n;
931         if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
932             (i = screen->max_row - refreshheight + 1))
933             refreshtop = i;
934         if (scroll_all_lines) {
935             scrolltop = 0;
936             if ((scrollheight += shift) > i)
937                 scrollheight = i;
938             if ((i = screen->savedlines) < screen->savelines) {
939                 if ((i += n) > screen->savelines)
940                     i = screen->savelines;
941                 screen->savedlines = i;
942                 ScrollBarDrawThumb(screen->scrollWidget);
943             }
944         } else {
945             scrolltop = screen->cur_row + shift;
946             if ((i = screen->bot_marg - bot) > 0) {
947                 scrollheight -= i;
948                 if ((i = screen->cur_row + n - 1 - bot) >= 0) {
949                     refreshheight -= i;
950                 }
951             }
952         }
953         vertical_copy_area(xw, scrolltop + n, scrollheight, n);
954         if (shift > 0 && refreshheight > 0) {
955             int rows = refreshheight;
956             if (rows > shift)
957                 rows = shift;
958             ScrnUpdate(xw, refreshtop, 0, rows, MaxCols(screen), True);
959             refreshtop += shift;
960             refreshheight -= shift;
961         }
962         if (refreshheight > 0) {
963             ClearCurBackground(xw,
964                                (int) refreshtop * FontHeight(screen) + screen->border,
965                                (int) OriginX(screen),
966                                (unsigned) (refreshheight * FontHeight(screen)),
967                                (unsigned) Width(screen));
968         }
969     }
970 }
971
972 /*
973  * Insert n blanks at the cursor's position, no wraparound
974  */
975 void
976 InsertChar(XtermWidget xw, unsigned n)
977 {
978     TScreen *screen = TScreenOf(xw);
979     LineData *ld;
980     unsigned limit;
981     int row = INX2ROW(screen, screen->cur_row);
982
983     if (screen->cursor_state)
984         HideCursor();
985
986     TRACE(("InsertChar count=%d\n", n));
987
988     if (ScrnHaveSelection(screen)
989         && ScrnIsLineInSelection(screen, row)) {
990         ScrnDisownSelection(xw);
991     }
992     screen->do_wrap = False;
993
994     assert(screen->cur_col <= screen->max_col);
995     limit = (unsigned) (MaxCols(screen) - screen->cur_col);
996
997     if (n > limit)
998         n = limit;
999
1000     assert(n != 0);
1001     if (AddToVisible(xw)
1002         && (ld = getLineData(screen, screen->cur_row)) != 0) {
1003         int col = MaxCols(screen) - (int) n;
1004
1005         /*
1006          * If we shift part of a multi-column character, fill the rest
1007          * of it with blanks.  Do similar repair for the text which will
1008          * be shifted into the right-margin.
1009          */
1010         if_OPT_WIDE_CHARS(screen, {
1011             int kl;
1012             int kr = screen->cur_col;
1013             if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) {
1014                 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1015             }
1016             kr = screen->max_col - (int) n + 1;
1017             if (DamagedCells(screen, n, &kl, (int *) 0,
1018                              screen->cur_row,
1019                              kr) && kr > kl) {
1020                 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1021             }
1022         });
1023
1024 #if OPT_DEC_CHRSET
1025         if (CSET_DOUBLE(GetLineDblCS(ld))) {
1026             col = MaxCols(screen) / 2 - (int) n;
1027         }
1028 #endif
1029         /*
1030          * prevent InsertChar from shifting the end of a line over
1031          * if it is being appended to
1032          */
1033         if (non_blank_line(screen, screen->cur_row,
1034                            screen->cur_col, MaxCols(screen))) {
1035             horizontal_copy_area(xw, screen->cur_col,
1036                                  col - screen->cur_col,
1037                                  (int) n);
1038         }
1039
1040         ClearCurBackground(xw,
1041                            CursorY(screen, screen->cur_row),
1042                            LineCursorX(screen, ld, screen->cur_col),
1043                            (unsigned) FontHeight(screen),
1044                            n * (unsigned) LineFontWidth(screen, ld));
1045     }
1046     /* adjust screen->buf */
1047     ScrnInsertChar(xw, n);
1048 }
1049
1050 /*
1051  * Deletes n chars at the cursor's position, no wraparound.
1052  */
1053 void
1054 DeleteChar(XtermWidget xw, unsigned n)
1055 {
1056     TScreen *screen = TScreenOf(xw);
1057     LineData *ld;
1058     unsigned limit;
1059     int row = INX2ROW(screen, screen->cur_row);
1060
1061     if (screen->cursor_state)
1062         HideCursor();
1063
1064     TRACE(("DeleteChar count=%d\n", n));
1065
1066     if (ScrnHaveSelection(screen)
1067         && ScrnIsLineInSelection(screen, row)) {
1068         ScrnDisownSelection(xw);
1069     }
1070     screen->do_wrap = False;
1071
1072     assert(screen->cur_col <= screen->max_col);
1073     limit = (unsigned) (MaxCols(screen) - screen->cur_col);
1074
1075     if (n > limit)
1076         n = limit;
1077
1078     assert(n != 0);
1079     if (AddToVisible(xw)
1080         && (ld = getLineData(screen, screen->cur_row)) != 0) {
1081         int col = MaxCols(screen) - (int) n;
1082
1083         /*
1084          * If we delete part of a multi-column character, fill the rest
1085          * of it with blanks.
1086          */
1087         if_OPT_WIDE_CHARS(screen, {
1088             int kl;
1089             int kr;
1090             if (DamagedCurCells(screen, n, &kl, &kr))
1091                 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1092         });
1093
1094 #if OPT_DEC_CHRSET
1095         if (CSET_DOUBLE(GetLineDblCS(ld))) {
1096             col = MaxCols(screen) / 2 - (int) n;
1097         }
1098 #endif
1099         horizontal_copy_area(xw,
1100                              (screen->cur_col + (int) n),
1101                              col - screen->cur_col,
1102                              -((int) n));
1103
1104         ClearCurBackground(xw,
1105                            CursorY(screen, screen->cur_row),
1106                            LineCursorX(screen, ld, col),
1107                            (unsigned) FontHeight(screen),
1108                            n * (unsigned) LineFontWidth(screen, ld));
1109     }
1110     if (n != 0) {
1111         /* adjust screen->buf */
1112         ScrnDeleteChar(xw, n);
1113     }
1114 }
1115
1116 /*
1117  * Clear from cursor position to beginning of display, inclusive.
1118  */
1119 static void
1120 ClearAbove(XtermWidget xw)
1121 {
1122     TScreen *screen = TScreenOf(xw);
1123
1124     if (screen->protected_mode != OFF_PROTECT) {
1125         int row;
1126         unsigned len = (unsigned) MaxCols(screen);
1127
1128         assert(screen->max_col >= 0);
1129         for (row = 0; row <= screen->max_row; row++)
1130             ClearInLine(xw, row, 0, len);
1131     } else {
1132         int top, height;
1133
1134         if (screen->cursor_state)
1135             HideCursor();
1136         if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1137             if (screen->scroll_amt)
1138                 FlushScroll(xw);
1139             if ((height = screen->cur_row + top) > screen->max_row)
1140                 height = screen->max_row + 1;
1141             if ((height -= top) > 0) {
1142                 ClearCurBackground(xw,
1143                                    top * FontHeight(screen) + screen->border,
1144                                    OriginX(screen),
1145                                    (unsigned) (height * FontHeight(screen)),
1146                                    (unsigned) (Width(screen)));
1147             }
1148         }
1149         ClearBufRows(xw, 0, screen->cur_row - 1);
1150     }
1151
1152     ClearLeft(xw);
1153 }
1154
1155 /*
1156  * Clear from cursor position to end of display, inclusive.
1157  */
1158 static void
1159 ClearBelow(XtermWidget xw)
1160 {
1161     TScreen *screen = TScreenOf(xw);
1162
1163     ClearRight(xw, -1);
1164
1165     if (screen->protected_mode != OFF_PROTECT) {
1166         int row;
1167         unsigned len = (unsigned) MaxCols(screen);
1168
1169         assert(screen->max_col >= 0);
1170         for (row = screen->cur_row + 1; row <= screen->max_row; row++)
1171             ClearInLine(xw, row, 0, len);
1172     } else {
1173         int top;
1174
1175         if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) {
1176             if (screen->scroll_amt)
1177                 FlushScroll(xw);
1178             if (++top <= screen->max_row) {
1179                 ClearCurBackground(xw,
1180                                    top * FontHeight(screen) + screen->border,
1181                                    OriginX(screen),
1182                                    (unsigned) ((screen->max_row - top + 1)
1183                                                * FontHeight(screen)),
1184                                    (unsigned) (Width(screen)));
1185             }
1186         }
1187         ClearBufRows(xw, screen->cur_row + 1, screen->max_row);
1188     }
1189 }
1190
1191 /*
1192  * Clear the given row, for the given range of columns, returning 1 if no
1193  * protected characters were found, 0 otherwise.
1194  */
1195 static int
1196 ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len)
1197 {
1198     TScreen *screen = TScreenOf(xw);
1199     LineData *ld;
1200     int rc = 1;
1201
1202     TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n",
1203            row, col, len,
1204            screen->startH.row,
1205            screen->startH.col));
1206
1207     if (ScrnHaveSelection(screen)
1208         && ScrnIsLineInSelection(screen, row)) {
1209         ScrnDisownSelection(xw);
1210     }
1211
1212     if (col + (int) len >= MaxCols(screen)) {
1213         len = (unsigned) (MaxCols(screen) - col);
1214     }
1215
1216     /* If we've marked protected text on the screen, we'll have to
1217      * check each time we do an erase.
1218      */
1219     if (screen->protected_mode != OFF_PROTECT) {
1220         unsigned n;
1221         Char *attrs = getLineData(screen, row)->attribs + col;
1222         int saved_mode = screen->protected_mode;
1223         Bool done;
1224
1225         /* disable this branch during recursion */
1226         screen->protected_mode = OFF_PROTECT;
1227
1228         do {
1229             done = True;
1230             for (n = 0; n < len; n++) {
1231                 if (attrs[n] & PROTECTED) {
1232                     rc = 0;     /* found a protected segment */
1233                     if (n != 0) {
1234                         ClearInLine(xw, row, col, n);
1235                     }
1236                     while ((n < len)
1237                            && (attrs[n] & PROTECTED)) {
1238                         n++;
1239                     }
1240                     done = False;
1241                     break;
1242                 }
1243             }
1244             /* setup for another segment, past the protected text */
1245             if (!done) {
1246                 attrs += n;
1247                 col += (int) n;
1248                 len -= n;
1249             }
1250         } while (!done);
1251
1252         screen->protected_mode = saved_mode;
1253         if ((int) len <= 0) {
1254             return 0;
1255         }
1256     }
1257     /* fall through to the final non-protected segment */
1258
1259     if (screen->cursor_state)
1260         HideCursor();
1261     screen->do_wrap = False;
1262
1263     if (AddToVisible(xw)
1264         && (ld = getLineData(screen, row)) != 0) {
1265
1266         ClearCurBackground(xw,
1267                            CursorY(screen, row),
1268                            LineCursorX(screen, ld, col),
1269                            (unsigned) FontHeight(screen),
1270                            len * (unsigned) LineFontWidth(screen, ld));
1271     }
1272
1273     if (len != 0) {
1274         ClearCells(xw, flags, len, row, col);
1275     }
1276
1277     return rc;
1278 }
1279
1280 int
1281 ClearInLine(XtermWidget xw, int row, int col, unsigned len)
1282 {
1283     TScreen *screen = TScreenOf(xw);
1284     int flags = 0;
1285
1286     /*
1287      * If we're clearing to the end of the line, we won't count this as
1288      * "drawn" characters.  We'll only do cut/paste on "drawn" characters,
1289      * so this has the effect of suppressing trailing blanks from a
1290      * selection.
1291      */
1292     if (col + (int) len < MaxCols(screen)) {
1293         flags |= CHARDRAWN;
1294     }
1295     return ClearInLine2(xw, flags, row, col, len);
1296 }
1297
1298 /*
1299  * Clear the next n characters on the cursor's line, including the cursor's
1300  * position.
1301  */
1302 void
1303 ClearRight(XtermWidget xw, int n)
1304 {
1305     TScreen *screen = TScreenOf(xw);
1306     LineData *ld;
1307     unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col);
1308
1309     assert(screen->max_col >= 0);
1310     assert(screen->max_col >= screen->cur_col);
1311
1312     if (n < 0)                  /* the remainder of the line */
1313         n = MaxCols(screen);
1314     if (n == 0)                 /* default for 'ECH' */
1315         n = 1;
1316
1317     if (len > (unsigned) n)
1318         len = (unsigned) n;
1319
1320     ld = getLineData(screen, screen->cur_row);
1321     if (AddToVisible(xw)) {
1322         if_OPT_WIDE_CHARS(screen, {
1323             int col = screen->cur_col;
1324             int row = screen->cur_row;
1325             int kl;
1326             int kr;
1327             int xx;
1328             if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) {
1329                 xx = col;
1330                 if (kl < xx) {
1331                     ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl));
1332                 }
1333                 xx = col + (int) len - 1;
1334                 if (kr > xx) {
1335                     ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx));
1336                 }
1337             }
1338         });
1339         (void) ClearInLine(xw, screen->cur_row, screen->cur_col, len);
1340     } else {
1341         ScrnClearCells(xw, screen->cur_row, screen->cur_col, len);
1342     }
1343
1344     /* with the right part cleared, we can't be wrapping */
1345     LineClrWrapped(ld);
1346     if (screen->show_wrap_marks) {
1347         ShowWrapMarks(xw, screen->cur_row, ld);
1348     }
1349     screen->do_wrap = False;
1350 }
1351
1352 /*
1353  * Clear first part of cursor's line, inclusive.
1354  */
1355 static void
1356 ClearLeft(XtermWidget xw)
1357 {
1358     TScreen *screen = TScreenOf(xw);
1359     unsigned len = (unsigned) screen->cur_col + 1;
1360
1361     assert(screen->cur_col >= 0);
1362     if (AddToVisible(xw)) {
1363         if_OPT_WIDE_CHARS(screen, {
1364             int row = screen->cur_row;
1365             int kl;
1366             int kr;
1367             if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
1368                 ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
1369             }
1370         });
1371         (void) ClearInLine(xw, screen->cur_row, 0, len);
1372     } else {
1373         ScrnClearCells(xw, screen->cur_row, 0, len);
1374     }
1375 }
1376
1377 /*
1378  * Erase the cursor's line.
1379  */
1380 static void
1381 ClearLine(XtermWidget xw)
1382 {
1383     TScreen *screen = TScreenOf(xw);
1384     unsigned len = (unsigned) MaxCols(screen);
1385
1386     assert(screen->max_col >= 0);
1387     (void) ClearInLine(xw, screen->cur_row, 0, len);
1388 }
1389
1390 void
1391 ClearScreen(XtermWidget xw)
1392 {
1393     TScreen *screen = TScreenOf(xw);
1394     int top;
1395
1396     if (screen->cursor_state)
1397         HideCursor();
1398
1399     ScrnDisownSelection(xw);
1400     screen->do_wrap = False;
1401     if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1402         if (screen->scroll_amt)
1403             FlushScroll(xw);
1404         ClearCurBackground(xw,
1405                            top * FontHeight(screen) + screen->border,
1406                            OriginX(screen),
1407                            (unsigned) ((screen->max_row - top + 1)
1408                                        * FontHeight(screen)),
1409                            (unsigned) Width(screen));
1410     }
1411     ClearBufRows(xw, 0, screen->max_row);
1412 }
1413
1414 /*
1415  * If we've written protected text DEC-style, and are issuing a non-DEC
1416  * erase, temporarily reset the protected_mode flag so that the erase will
1417  * ignore the protected flags.
1418  */
1419 void
1420 do_erase_line(XtermWidget xw, int param, int mode)
1421 {
1422     TScreen *screen = TScreenOf(xw);
1423     int saved_mode = screen->protected_mode;
1424
1425     if (saved_mode == DEC_PROTECT
1426         && saved_mode != mode) {
1427         screen->protected_mode = OFF_PROTECT;
1428     }
1429
1430     switch (param) {
1431     case -1:                    /* DEFAULT */
1432     case 0:
1433         ClearRight(xw, -1);
1434         break;
1435     case 1:
1436         ClearLeft(xw);
1437         break;
1438     case 2:
1439         ClearLine(xw);
1440         break;
1441     }
1442     screen->protected_mode = saved_mode;
1443 }
1444
1445 /*
1446  * Just like 'do_erase_line()', except that this intercepts ED controls.  If we
1447  * clear the whole screen, we'll get the return-value from ClearInLine, and
1448  * find if there were any protected characters left.  If not, reset the
1449  * protected mode flag in the screen data (it's slower).
1450  */
1451 void
1452 do_erase_display(XtermWidget xw, int param, int mode)
1453 {
1454     TScreen *screen = TScreenOf(xw);
1455     int saved_mode = screen->protected_mode;
1456
1457     if (saved_mode == DEC_PROTECT
1458         && saved_mode != mode)
1459         screen->protected_mode = OFF_PROTECT;
1460
1461     switch (param) {
1462     case -1:                    /* DEFAULT */
1463     case 0:
1464         if (screen->cur_row == 0
1465             && screen->cur_col == 0) {
1466             screen->protected_mode = saved_mode;
1467             do_erase_display(xw, 2, mode);
1468             saved_mode = screen->protected_mode;
1469         } else
1470             ClearBelow(xw);
1471         break;
1472
1473     case 1:
1474         if (screen->cur_row == screen->max_row
1475             && screen->cur_col == screen->max_col) {
1476             screen->protected_mode = saved_mode;
1477             do_erase_display(xw, 2, mode);
1478             saved_mode = screen->protected_mode;
1479         } else
1480             ClearAbove(xw);
1481         break;
1482
1483     case 2:
1484         /*
1485          * We use 'ClearScreen()' throughout the remainder of the
1486          * program for places where we don't care if the characters are
1487          * protected or not.  So we modify the logic around this call
1488          * on 'ClearScreen()' to handle protected characters.
1489          */
1490         if (screen->protected_mode != OFF_PROTECT) {
1491             int row;
1492             int rc = 1;
1493             unsigned len = (unsigned) MaxCols(screen);
1494
1495             assert(screen->max_col >= 0);
1496             for (row = 0; row <= screen->max_row; row++)
1497                 rc &= ClearInLine(xw, row, 0, len);
1498             if (rc != 0)
1499                 saved_mode = OFF_PROTECT;
1500         } else {
1501             ClearScreen(xw);
1502         }
1503         break;
1504
1505     case 3:
1506         /* xterm addition - erase saved lines. */
1507         screen->savedlines = 0;
1508         ScrollBarDrawThumb(screen->scrollWidget);
1509         break;
1510     }
1511     screen->protected_mode = saved_mode;
1512 }
1513
1514 static void
1515 CopyWait(XtermWidget xw)
1516 {
1517     TScreen *screen = TScreenOf(xw);
1518     XEvent reply;
1519     XEvent *rep = &reply;
1520
1521     for (;;) {
1522         XWindowEvent(screen->display, VWindow(screen),
1523                      ExposureMask, &reply);
1524         switch (reply.type) {
1525         case Expose:
1526             HandleExposure(xw, &reply);
1527             break;
1528         case NoExpose:
1529         case GraphicsExpose:
1530             if (screen->incopy <= 0) {
1531                 screen->incopy = 1;
1532                 if (screen->scrolls > 0)
1533                     screen->scrolls--;
1534             }
1535             if (reply.type == GraphicsExpose)
1536                 HandleExposure(xw, &reply);
1537
1538             if ((reply.type == NoExpose) ||
1539                 ((XExposeEvent *) rep)->count == 0) {
1540                 if (screen->incopy <= 0 && screen->scrolls > 0)
1541                     screen->scrolls--;
1542                 if (screen->scrolls == 0) {
1543                     screen->incopy = 0;
1544                     return;
1545                 }
1546                 screen->incopy = -1;
1547             }
1548             break;
1549         }
1550     }
1551 }
1552
1553 /*
1554  * used by vertical_copy_area and and horizontal_copy_area
1555  */
1556 static void
1557 copy_area(XtermWidget xw,
1558           int src_x,
1559           int src_y,
1560           unsigned width,
1561           unsigned height,
1562           int dest_x,
1563           int dest_y)
1564 {
1565     TScreen *screen = TScreenOf(xw);
1566
1567     if (width != 0 && height != 0) {
1568         /* wait for previous CopyArea to complete unless
1569            multiscroll is enabled and active */
1570         if (screen->incopy && screen->scrolls == 0)
1571             CopyWait(xw);
1572         screen->incopy = -1;
1573
1574         /* save for translating Expose events */
1575         screen->copy_src_x = src_x;
1576         screen->copy_src_y = src_y;
1577         screen->copy_width = width;
1578         screen->copy_height = height;
1579         screen->copy_dest_x = dest_x;
1580         screen->copy_dest_y = dest_y;
1581
1582         XCopyArea(screen->display,
1583                   VWindow(screen), VWindow(screen),
1584                   NormalGC(xw, screen),
1585                   src_x, src_y, width, height, dest_x, dest_y);
1586     }
1587 }
1588
1589 /*
1590  * use when inserting or deleting characters on the current line
1591  */
1592 static void
1593 horizontal_copy_area(XtermWidget xw,
1594                      int firstchar,     /* char pos on screen to start copying at */
1595                      int nchars,
1596                      int amount)        /* number of characters to move right */
1597 {
1598     TScreen *screen = TScreenOf(xw);
1599     LineData *ld;
1600
1601     if ((ld = getLineData(screen, screen->cur_row)) != 0) {
1602         int src_x = LineCursorX(screen, ld, firstchar);
1603         int src_y = CursorY(screen, screen->cur_row);
1604
1605         copy_area(xw, src_x, src_y,
1606                   (unsigned) (nchars * LineFontWidth(screen, ld)),
1607                   (unsigned) FontHeight(screen),
1608                   src_x + amount * LineFontWidth(screen, ld), src_y);
1609     }
1610 }
1611
1612 /*
1613  * use when inserting or deleting lines from the screen
1614  */
1615 static void
1616 vertical_copy_area(XtermWidget xw,
1617                    int firstline,       /* line on screen to start copying at */
1618                    int nlines,
1619                    int amount)  /* number of lines to move up (neg=down) */
1620 {
1621     TScreen *screen = TScreenOf(xw);
1622
1623     if (nlines > 0) {
1624         int src_x = OriginX(screen);
1625         int src_y = firstline * FontHeight(screen) + screen->border;
1626
1627         copy_area(xw, src_x, src_y,
1628                   (unsigned) Width(screen),
1629                   (unsigned) (nlines * FontHeight(screen)),
1630                   src_x, src_y - amount * FontHeight(screen));
1631         if (screen->show_wrap_marks) {
1632             LineData *ld;
1633             int row;
1634             for (row = firstline; row < firstline + nlines; ++row) {
1635                 if ((ld = getLineData(screen, row)) != 0) {
1636                     ShowWrapMarks(xw, row, ld);
1637                 }
1638             }
1639         }
1640     }
1641 }
1642
1643 /*
1644  * use when scrolling the entire screen
1645  */
1646 void
1647 scrolling_copy_area(XtermWidget xw,
1648                     int firstline,      /* line on screen to start copying at */
1649                     int nlines,
1650                     int amount) /* number of lines to move up (neg=down) */
1651 {
1652
1653     if (nlines > 0) {
1654         vertical_copy_area(xw, firstline, nlines, amount);
1655     }
1656 }
1657
1658 /*
1659  * Handler for Expose events on the VT widget.
1660  * Returns 1 iff the area where the cursor was got refreshed.
1661  */
1662 int
1663 HandleExposure(XtermWidget xw, XEvent * event)
1664 {
1665     TScreen *screen = TScreenOf(xw);
1666     XExposeEvent *reply = (XExposeEvent *) event;
1667
1668 #ifndef NO_ACTIVE_ICON
1669     if (reply->window == screen->iconVwin.window) {
1670         WhichVWin(screen) = &screen->iconVwin;
1671         TRACE(("HandleExposure - icon"));
1672     } else {
1673         WhichVWin(screen) = &screen->fullVwin;
1674         TRACE(("HandleExposure - normal"));
1675     }
1676     TRACE((" event %d,%d %dx%d\n",
1677            reply->y,
1678            reply->x,
1679            reply->height,
1680            reply->width));
1681 #endif /* NO_ACTIVE_ICON */
1682
1683     /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
1684     if (!screen->incopy || event->type != Expose)
1685         return handle_translated_exposure(xw, reply->x, reply->y,
1686                                           reply->width,
1687                                           reply->height);
1688     else {
1689         /* compute intersection of area being copied with
1690            area being exposed. */
1691         int both_x1 = Max(screen->copy_src_x, reply->x);
1692         int both_y1 = Max(screen->copy_src_y, reply->y);
1693         int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
1694                           (reply->x + (int) reply->width));
1695         int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
1696                           (reply->y + (int) reply->height));
1697         int value = 0;
1698
1699         /* was anything copied affected? */
1700         if (both_x2 > both_x1 && both_y2 > both_y1) {
1701             /* do the copied area */
1702             value = handle_translated_exposure
1703                 (xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
1704                  reply->y + screen->copy_dest_y - screen->copy_src_y,
1705                  reply->width, reply->height);
1706         }
1707         /* was anything not copied affected? */
1708         if (reply->x < both_x1 || reply->y < both_y1
1709             || reply->x + reply->width > both_x2
1710             || reply->y + reply->height > both_y2)
1711             value = handle_translated_exposure(xw, reply->x, reply->y,
1712                                                reply->width, reply->height);
1713
1714         return value;
1715     }
1716 }
1717
1718 static void
1719 set_background(XtermWidget xw, int color GCC_UNUSED)
1720 {
1721     TScreen *screen = TScreenOf(xw);
1722     Pixel c = getXtermBackground(xw, xw->flags, color);
1723
1724     TRACE(("set_background(%d) %#lx\n", color, c));
1725     XSetWindowBackground(screen->display, VShellWindow(xw), c);
1726     XSetWindowBackground(screen->display, VWindow(screen), c);
1727 }
1728
1729 /*
1730  * Called by the ExposeHandler to do the actual repaint after the coordinates
1731  * have been translated to allow for any CopyArea in progress.
1732  * The rectangle passed in is pixel coordinates.
1733  */
1734 static int
1735 handle_translated_exposure(XtermWidget xw,
1736                            int rect_x,
1737                            int rect_y,
1738                            int rect_width,
1739                            int rect_height)
1740 {
1741     TScreen *screen = TScreenOf(xw);
1742     int toprow, leftcol, nrows, ncols;
1743     int x0, x1;
1744     int y0, y1;
1745     int result = 0;
1746
1747     TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
1748            rect_y, rect_x, rect_height, rect_width));
1749
1750     x0 = (rect_x - OriginX(screen));
1751     x1 = (x0 + rect_width);
1752
1753     y0 = (rect_y - OriginY(screen));
1754     y1 = (y0 + rect_height);
1755
1756     if ((x0 < 0 ||
1757          y0 < 0 ||
1758          x1 > Width(screen) ||
1759          y1 > Height(screen))) {
1760         set_background(xw, -1);
1761         XClearArea(screen->display, VWindow(screen),
1762                    rect_x,
1763                    rect_y,
1764                    (unsigned) rect_width,
1765                    (unsigned) rect_height, False);
1766     }
1767     toprow = y0 / FontHeight(screen);
1768     if (toprow < 0)
1769         toprow = 0;
1770
1771     leftcol = x0 / FontWidth(screen);
1772     if (leftcol < 0)
1773         leftcol = 0;
1774
1775     nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
1776     ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
1777     toprow -= screen->scrolls;
1778     if (toprow < 0) {
1779         nrows += toprow;
1780         toprow = 0;
1781     }
1782     if (toprow + nrows > MaxRows(screen))
1783         nrows = MaxRows(screen) - toprow;
1784     if (leftcol + ncols > MaxCols(screen))
1785         ncols = MaxCols(screen) - leftcol;
1786
1787     if (nrows > 0 && ncols > 0) {
1788         ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
1789         first_map_occurred();
1790         if (screen->cur_row >= toprow &&
1791             screen->cur_row < toprow + nrows &&
1792             screen->cur_col >= leftcol &&
1793             screen->cur_col < leftcol + ncols) {
1794             result = 1;
1795         }
1796
1797     }
1798     TRACE(("...handle_translated_exposure %d\n", result));
1799     return (result);
1800 }
1801
1802 /***====================================================================***/
1803
1804 void
1805 GetColors(XtermWidget xw, ScrnColors * pColors)
1806 {
1807     TScreen *screen = TScreenOf(xw);
1808     int n;
1809
1810     pColors->which = 0;
1811     for (n = 0; n < NCOLORS; ++n) {
1812         SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
1813     }
1814 }
1815
1816 void
1817 ChangeColors(XtermWidget xw, ScrnColors * pNew)
1818 {
1819     Bool repaint = False;
1820     TScreen *screen = TScreenOf(xw);
1821     VTwin *win = WhichVWin(screen);
1822
1823     TRACE(("ChangeColors\n"));
1824
1825     if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
1826         T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
1827         TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
1828         /* no repaint needed */
1829     } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
1830                (COLOR_DEFINED(pNew, TEXT_FG))) {
1831         if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) {
1832             T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
1833             TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
1834             if (screen->Vshow)
1835                 repaint = True;
1836         }
1837     }
1838
1839     if (COLOR_DEFINED(pNew, TEXT_FG)) {
1840         Pixel fg = COLOR_VALUE(pNew, TEXT_FG);
1841         T_COLOR(screen, TEXT_FG) = fg;
1842         TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG)));
1843         if (screen->Vshow) {
1844             setCgsFore(xw, win, gcNorm, fg);
1845             setCgsBack(xw, win, gcNormReverse, fg);
1846             setCgsFore(xw, win, gcBold, fg);
1847             setCgsBack(xw, win, gcBoldReverse, fg);
1848             repaint = True;
1849         }
1850     }
1851
1852     if (COLOR_DEFINED(pNew, TEXT_BG)) {
1853         Pixel bg = COLOR_VALUE(pNew, TEXT_BG);
1854         T_COLOR(screen, TEXT_BG) = bg;
1855         TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG)));
1856         if (screen->Vshow) {
1857             setCgsBack(xw, win, gcNorm, bg);
1858             setCgsFore(xw, win, gcNormReverse, bg);
1859             setCgsBack(xw, win, gcBold, bg);
1860             setCgsFore(xw, win, gcBoldReverse, bg);
1861             set_background(xw, -1);
1862             repaint = True;
1863         }
1864     }
1865 #if OPT_HIGHLIGHT_COLOR
1866     if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
1867         if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) {
1868             T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
1869             TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
1870             if (screen->Vshow)
1871                 repaint = True;
1872         }
1873     }
1874     if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
1875         if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) {
1876             T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
1877             TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
1878             if (screen->Vshow)
1879                 repaint = True;
1880         }
1881     }
1882 #endif
1883
1884     if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
1885         if (COLOR_DEFINED(pNew, MOUSE_FG)) {
1886             T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
1887             TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
1888         }
1889         if (COLOR_DEFINED(pNew, MOUSE_BG)) {
1890             T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
1891             TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
1892         }
1893
1894         if (screen->Vshow) {
1895             recolor_cursor(screen,
1896                            screen->pointer_cursor,
1897                            T_COLOR(screen, MOUSE_FG),
1898                            T_COLOR(screen, MOUSE_BG));
1899             XDefineCursor(screen->display, VWindow(screen),
1900                           screen->pointer_cursor);
1901         }
1902 #if OPT_TEK4014
1903         if (TEK4014_SHOWN(xw)) {
1904             TekScreen *tekscr = TekScreenOf(tekWidget);
1905             Window tekwin = TWindow(tekscr);
1906             if (tekwin) {
1907                 recolor_cursor(screen,
1908                                tekscr->arrow,
1909                                T_COLOR(screen, MOUSE_FG),
1910                                T_COLOR(screen, MOUSE_BG));
1911                 XDefineCursor(screen->display, tekwin, tekscr->arrow);
1912             }
1913         }
1914 #endif
1915         /* no repaint needed */
1916     }
1917
1918     if (COLOR_DEFINED(pNew, TEXT_FG) ||
1919         COLOR_DEFINED(pNew, TEXT_BG) ||
1920         COLOR_DEFINED(pNew, TEXT_CURSOR)) {
1921         if (set_cursor_gcs(xw) && screen->Vshow) {
1922             repaint = True;
1923         }
1924     }
1925 #if OPT_TEK4014
1926     if (COLOR_DEFINED(pNew, TEK_FG) ||
1927         COLOR_DEFINED(pNew, TEK_BG)) {
1928         ChangeTekColors(tekWidget, screen, pNew);
1929         if (TEK4014_SHOWN(xw)) {
1930             TekRepaint(tekWidget);
1931         }
1932     } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
1933         ChangeTekColors(tekWidget, screen, pNew);
1934     }
1935 #endif
1936     if (repaint)
1937         xtermRepaint(xw);
1938 }
1939
1940 void
1941 xtermClear(XtermWidget xw)
1942 {
1943     TScreen *screen = TScreenOf(xw);
1944
1945     TRACE(("xtermClear\n"));
1946     XClearWindow(screen->display, VWindow(screen));
1947 }
1948
1949 void
1950 xtermRepaint(XtermWidget xw)
1951 {
1952     TScreen *screen = TScreenOf(xw);
1953
1954     TRACE(("xtermRepaint\n"));
1955     xtermClear(xw);
1956     ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), True);
1957 }
1958
1959 /***====================================================================***/
1960
1961 Boolean
1962 isDefaultForeground(const char *name)
1963 {
1964     return (Boolean) ! x_strcasecmp(name, XtDefaultForeground);
1965 }
1966
1967 Boolean
1968 isDefaultBackground(const char *name)
1969 {
1970     return (Boolean) ! x_strcasecmp(name, XtDefaultBackground);
1971 }
1972
1973 #if OPT_WIDE_CHARS
1974 /*
1975  * Check for Unicode BIDI control characters, which may be miscategorized via
1976  * wcwidth() and iswprint() as zero-width printable characters.
1977  */
1978 Boolean
1979 isWideControl(unsigned ch)
1980 {
1981     Boolean result;
1982
1983     switch (ch) {
1984     case 0x200E:
1985     case 0x200F:
1986     case 0x202A:
1987     case 0x202B:
1988     case 0x202C:
1989     case 0x202D:
1990     case 0x202E:
1991         result = True;
1992         break;
1993     default:
1994         result = False;
1995         break;
1996     }
1997     return result;
1998 }
1999 #endif
2000
2001 /***====================================================================***/
2002
2003 typedef struct {
2004     Pixel fg;
2005     Pixel bg;
2006 } ToSwap;
2007
2008 #if OPT_HIGHLIGHT_COLOR
2009 #define hc_param ,Bool hilite_color
2010 #define hc_value ,screen->hilite_color
2011 #else
2012 #define hc_param                /* nothing */
2013 #define hc_value                /* nothing */
2014 #endif
2015
2016 /*
2017  * Use this to swap the foreground/background color values in the resource
2018  * data, and to build up a list of the pairs which must be swapped in the
2019  * GC cache.
2020  */
2021 static void
2022 swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
2023 {
2024     ColorRes tmp;
2025     int n;
2026     Boolean found = False;
2027
2028 #if OPT_COLOR_RES
2029     Pixel fg_color = fg->value;
2030     Pixel bg_color = bg->value;
2031 #else
2032     Pixel fg_color = *fg;
2033     Pixel bg_color = *bg;
2034 #endif
2035
2036 #if OPT_HIGHLIGHT_COLOR
2037     if ((fg_color != bg_color) || !hilite_color)
2038 #endif
2039     {
2040         EXCHANGE(*fg, *bg, tmp);
2041         for (n = 0; n < *count; ++n) {
2042             if ((list[n].fg == fg_color && list[n].bg == bg_color)
2043                 || (list[n].fg == bg_color && list[n].bg == fg_color)) {
2044                 found = True;
2045                 break;
2046             }
2047         }
2048         if (!found) {
2049             list[*count].fg = fg_color;
2050             list[*count].bg = bg_color;
2051             *count = *count + 1;
2052             TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
2053                    fg_color, bg_color, *count));
2054         }
2055     }
2056 }
2057
2058 static void
2059 reallySwapColors(XtermWidget xw, ToSwap * list, int count)
2060 {
2061     int j, k;
2062
2063     TRACE(("reallySwapColors\n"));
2064     for (j = 0; j < count; ++j) {
2065         for_each_text_gc(k) {
2066             redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
2067         }
2068     }
2069 }
2070
2071 static void
2072 swapVTwinGCs(XtermWidget xw, VTwin * win)
2073 {
2074     swapCgs(xw, win, gcNorm, gcNormReverse);
2075     swapCgs(xw, win, gcBold, gcBoldReverse);
2076 }
2077
2078 void
2079 ReverseVideo(XtermWidget xw)
2080 {
2081     TScreen *screen = TScreenOf(xw);
2082     ToSwap listToSwap[5];
2083     int numToSwap = 0;
2084
2085     TRACE(("ReverseVideo\n"));
2086
2087     /*
2088      * Swap SGR foreground and background colors.  By convention, these are
2089      * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
2090      * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
2091      * #7, respectively.
2092      *
2093      * We don't swap colors that happen to match the screen's foreground
2094      * and background because that tends to produce bizarre effects.
2095      */
2096 #define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
2097 #define swapAColor(a,b) swapAnyColor(Acolors, a, b)
2098     if_OPT_ISO_COLORS(screen, {
2099         swapAColor(0, 7);
2100         swapAColor(8, 15);
2101     });
2102
2103     if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG))
2104         T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
2105
2106 #define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
2107     swapTColor(TEXT_FG, TEXT_BG);
2108     swapTColor(MOUSE_FG, MOUSE_BG);
2109
2110     reallySwapColors(xw, listToSwap, numToSwap);
2111
2112     swapVTwinGCs(xw, &(screen->fullVwin));
2113 #ifndef NO_ACTIVE_ICON
2114     swapVTwinGCs(xw, &(screen->iconVwin));
2115 #endif /* NO_ACTIVE_ICON */
2116
2117     xw->misc.re_verse = (Boolean) ! xw->misc.re_verse;
2118
2119     if (XtIsRealized((Widget) xw)) {
2120         xtermDisplayCursor(xw);
2121     }
2122 #if OPT_TEK4014
2123     if (TEK4014_SHOWN(xw)) {
2124         TekScreen *tekscr = TekScreenOf(tekWidget);
2125         Window tekwin = TWindow(tekscr);
2126         recolor_cursor(screen,
2127                        tekscr->arrow,
2128                        T_COLOR(screen, MOUSE_FG),
2129                        T_COLOR(screen, MOUSE_BG));
2130         XDefineCursor(screen->display, tekwin, tekscr->arrow);
2131     }
2132 #endif
2133
2134     if (screen->scrollWidget)
2135         ScrollBarReverseVideo(screen->scrollWidget);
2136
2137     if (XtIsRealized((Widget) xw)) {
2138         set_background(xw, -1);
2139     }
2140 #if OPT_TEK4014
2141     TekReverseVideo(tekWidget);
2142 #endif
2143     if (XtIsRealized((Widget) xw)) {
2144         xtermRepaint(xw);
2145     }
2146 #if OPT_TEK4014
2147     if (TEK4014_SHOWN(xw)) {
2148         TekRepaint(tekWidget);
2149     }
2150 #endif
2151     ReverseOldColors();
2152     set_cursor_gcs(xw);
2153     update_reversevideo();
2154     TRACE(("...ReverseVideo\n"));
2155 }
2156
2157 void
2158 recolor_cursor(TScreen * screen,
2159                Cursor cursor,   /* X cursor ID to set */
2160                unsigned long fg,        /* pixel indexes to look up */
2161                unsigned long bg)        /* pixel indexes to look up */
2162 {
2163     Display *dpy = screen->display;
2164     XColor colordefs[2];        /* 0 is foreground, 1 is background */
2165
2166     colordefs[0].pixel = fg;
2167     colordefs[1].pixel = bg;
2168     XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
2169                  colordefs, 2);
2170     XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
2171     return;
2172 }
2173
2174 #if OPT_RENDERFONT
2175 static XftColor *
2176 getXftColor(XtermWidget xw, Pixel pixel)
2177 {
2178 #define CACHE_SIZE  4
2179     static struct {
2180         XftColor color;
2181         int use;
2182     } cache[CACHE_SIZE];
2183     static int use;
2184     int i;
2185     int oldest, oldestuse;
2186     XColor color;
2187
2188     oldestuse = 0x7fffffff;
2189     oldest = 0;
2190     for (i = 0; i < CACHE_SIZE; i++) {
2191         if (cache[i].use) {
2192             if (cache[i].color.pixel == pixel) {
2193                 cache[i].use = ++use;
2194                 return &cache[i].color;
2195             }
2196         }
2197         if (cache[i].use < oldestuse) {
2198             oldestuse = cache[i].use;
2199             oldest = i;
2200         }
2201     }
2202     i = oldest;
2203     color.pixel = pixel;
2204     XQueryColor(TScreenOf(xw)->display, xw->core.colormap, &color);
2205     cache[i].color.color.red = color.red;
2206     cache[i].color.color.green = color.green;
2207     cache[i].color.color.blue = color.blue;
2208     cache[i].color.color.alpha = 0xffff;
2209     cache[i].color.pixel = pixel;
2210     cache[i].use = ++use;
2211     return &cache[i].color;
2212 }
2213
2214 /*
2215  * The cell-width is related to, but not the same as the wide-character width.
2216  * We will only get useful values from wcwidth() for codes above 255.
2217  * Otherwise, interpret according to internal data.
2218  */
2219 #if OPT_RENDERWIDE
2220
2221 #if OPT_C1_PRINT
2222 #define XtermCellWidth(xw, ch) \
2223         (((ch) == 0 || (ch) == 127) \
2224           ? 0 \
2225           : (((ch) < 256) \
2226               ? (((ch) >= 128 && (ch) < 160) \
2227                   ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
2228                   : 1) \
2229               : my_wcwidth(ch)))
2230 #else
2231 #define XtermCellWidth(xw, ch) \
2232         (((ch) == 0 || (ch) == 127) \
2233           ? 0 \
2234           : (((ch) < 256) \
2235               ? 1 \
2236               : my_wcwidth(ch)))
2237 #endif
2238
2239 #endif /* OPT_RENDERWIDE */
2240
2241 #define XFT_FONT(name) screen->name.font
2242
2243 #if OPT_ISO_COLORS
2244 #define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
2245 #else
2246 #define UseBoldFont(screen) 1
2247 #endif
2248 /*
2249  * fontconfig/Xft combination prior to 2.2 has a problem with
2250  * CJK truetype 'double-width' (bi-width/monospace) fonts leading
2251  * to the 's p a c e d o u t' rendering. Consequently, we can't
2252  * rely on XftDrawString8/16  when one of  those fonts is used.
2253  * Instead, we need to roll out our own using XftDrawCharSpec.
2254  * A patch in the same spirit (but in a rather different form)
2255  * was applied to gnome vte and gtk2 port of vim.
2256  * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
2257  */
2258 static int
2259 xtermXftDrawString(XtermWidget xw,
2260                    unsigned flags GCC_UNUSED,
2261                    XftColor * color,
2262                    XftFont * font,
2263                    int x,
2264                    int y,
2265                    IChar * text,
2266                    Cardinal len,
2267                    Bool really)
2268 {
2269     TScreen *screen = TScreenOf(xw);
2270     int ncells = 0;
2271
2272     if (len != 0) {
2273 #if OPT_RENDERWIDE
2274         XftCharSpec *sbuf;
2275         XftFont *wfont;
2276         Cardinal src, dst;
2277         XftFont *lastFont = 0;
2278         XftFont *currFont = 0;
2279         Cardinal start = 0;
2280         int charWidth;
2281         int fontnum = screen->menu_font_number;
2282         int fwidth = FontWidth(screen);
2283
2284 #if OPT_ISO_COLORS
2285         if ((flags & UNDERLINE)
2286             && !screen->colorULMode
2287             && screen->italicULMode
2288             && XFT_FONT(renderWideItal[fontnum])) {
2289             wfont = XFT_FONT(renderWideItal[fontnum]);
2290         } else
2291 #endif
2292             if ((flags & BOLDATTR(screen))
2293                 && UseBoldFont(screen)
2294                 && XFT_FONT(renderWideBold[fontnum])) {
2295             wfont = XFT_FONT(renderWideBold[fontnum]);
2296         } else {
2297             wfont = XFT_FONT(renderWideNorm[fontnum]);
2298         }
2299
2300         BumpTypedBuffer(XftCharSpec, len);
2301         sbuf = BfBuf(XftCharSpec);
2302
2303         for (src = dst = 0; src < len; src++) {
2304             FcChar32 wc = *text++;
2305
2306             charWidth = XtermCellWidth(xw, (wchar_t) wc);
2307             if (charWidth < 0)
2308                 continue;
2309
2310             sbuf[dst].ucs4 = wc;
2311             sbuf[dst].x = (short) (x + fwidth * ncells);
2312             sbuf[dst].y = (short) (y);
2313
2314             currFont = (charWidth == 2 && wfont != 0) ? wfont : font;
2315             ncells += charWidth;
2316
2317             if (lastFont != currFont) {
2318                 if ((lastFont != 0) && really) {
2319                     XftDrawCharSpec(screen->renderDraw,
2320                                     color,
2321                                     lastFont,
2322                                     sbuf + start,
2323                                     (int) (dst - start));
2324                 }
2325                 start = dst;
2326                 lastFont = currFont;
2327             }
2328             ++dst;
2329         }
2330         if ((dst != start) && really) {
2331             XftDrawCharSpec(screen->renderDraw,
2332                             color,
2333                             lastFont,
2334                             sbuf + start,
2335                             (int) (dst - start));
2336         }
2337 #else /* !OPT_RENDERWIDE */
2338         if (really) {
2339             XftChar8 *buffer;
2340             int dst;
2341
2342             BumpTypedBuffer(XftChar8, len);
2343             buffer = BfBuf(XftChar8);
2344
2345             for (dst = 0; dst < (int) len; ++dst)
2346                 buffer[dst] = CharOf(text[dst]);
2347
2348             XftDrawString8(screen->renderDraw,
2349                            color,
2350                            font,
2351                            x, y, buffer, (int) len);
2352         }
2353         ncells = (int) len;
2354 #endif
2355     }
2356     return ncells;
2357 }
2358 #define xtermXftWidth(xw, flags, color, font, x, y, chars, len) \
2359    xtermXftDrawString(xw, flags, color, font, x, y, chars, len, False)
2360 #endif /* OPT_RENDERFONT */
2361
2362 #if OPT_WIDE_CHARS
2363 /*
2364  * Map characters commonly "fixed" by groff back to their ASCII equivalents.
2365  * Also map other useful equivalents.
2366  */
2367 unsigned
2368 AsciiEquivs(unsigned ch)
2369 {
2370     switch (ch) {
2371     case 0x2010:                /* groff "-" */
2372     case 0x2011:
2373     case 0x2012:
2374     case 0x2013:
2375     case 0x2014:
2376     case 0x2015:
2377     case 0x2212:                /* groff "\-" */
2378         ch = '-';
2379         break;
2380     case 0x2018:                /* groff "`" */
2381         ch = '`';
2382         break;
2383     case 0x2019:                /* groff ' */
2384         ch = '\'';
2385         break;
2386     case 0x201C:                /* groff lq */
2387     case 0x201D:                /* groff rq */
2388         ch = '"';
2389         break;
2390     case 0x2329:                /* groff ".URL" */
2391         ch = '<';
2392         break;
2393     case 0x232a:                /* groff ".URL" */
2394         ch = '>';
2395         break;
2396     default:
2397         if (ch >= 0xff01 && ch <= 0xff5e) {
2398             /* "Fullwidth" codes (actually double-width) */
2399             ch -= 0xff00;
2400             ch += ANSI_SPA;
2401             break;
2402         }
2403     }
2404     return ch;
2405 }
2406
2407 /*
2408  * Actually this should be called "groff_workaround()" - for the places where
2409  * groff stomps on compatibility.  Still, if enough people get used to it,
2410  * this might someday become a quasi-standard.
2411  */
2412 static int
2413 ucs_workaround(XtermWidget xw,
2414                unsigned ch,
2415                unsigned flags,
2416                GC gc,
2417                int x,
2418                int y,
2419                int chrset,
2420                int on_wide)
2421 {
2422     TScreen *screen = TScreenOf(xw);
2423     int fixed = False;
2424
2425     if (screen->wide_chars && screen->utf8_mode && ch > 256) {
2426         IChar eqv = (IChar) AsciiEquivs(ch);
2427
2428         if (eqv != (IChar) ch) {
2429             int width = my_wcwidth((int) ch);
2430
2431             do {
2432                 drawXtermText(xw,
2433                               flags,
2434                               gc,
2435                               x,
2436                               y,
2437                               chrset,
2438                               &eqv,
2439                               1,
2440                               on_wide);
2441                 x += FontWidth(screen);
2442                 eqv = '?';
2443             } while (width-- > 1);
2444
2445             fixed = True;
2446         } else if (ch == HIDDEN_CHAR) {
2447             fixed = True;
2448         }
2449     }
2450     return fixed;
2451 }
2452 #endif
2453
2454 /*
2455  * Use this when the characters will not fill the cell area properly.  Fill the
2456  * area where we'll write the characters, otherwise we'll get gaps between
2457  * them, e.g., in the original background color.
2458  *
2459  * The cursor is a special case, because the XFillRectangle call only uses the
2460  * foreground, while we've set the cursor color in the background.  So we need
2461  * a special GC for that.
2462  */
2463 static void
2464 xtermFillCells(XtermWidget xw,
2465                unsigned flags,
2466                GC gc,
2467                int x,
2468                int y,
2469                Cardinal len)
2470 {
2471     TScreen *screen = TScreenOf(xw);
2472     VTwin *currentWin = WhichVWin(screen);
2473
2474     if (!(flags & NOBACKGROUND)) {
2475         CgsEnum srcId = getCgsId(xw, currentWin, gc);
2476         CgsEnum dstId = gcMAX;
2477         Pixel fg = getCgsFore(xw, currentWin, gc);
2478         Pixel bg = getCgsBack(xw, currentWin, gc);
2479
2480         switch (srcId) {
2481         case gcVTcursNormal:
2482         case gcVTcursReverse:
2483             dstId = gcVTcursOutline;
2484             break;
2485         case gcVTcursFilled:
2486         case gcVTcursOutline:
2487             /* FIXME */
2488             break;
2489         case gcNorm:
2490             dstId = gcNormReverse;
2491             break;
2492         case gcNormReverse:
2493             dstId = gcNorm;
2494             break;
2495         case gcBold:
2496             dstId = gcBoldReverse;
2497             break;
2498         case gcBoldReverse:
2499             dstId = gcBold;
2500             break;
2501 #if OPT_BOX_CHARS
2502         case gcLine:
2503         case gcDots:
2504             /* FIXME */
2505             break;
2506 #endif
2507 #if OPT_DEC_CHRSET
2508         case gcCNorm:
2509         case gcCBold:
2510             /* FIXME */
2511             break;
2512 #endif
2513 #if OPT_WIDE_CHARS
2514         case gcWide:
2515             dstId = gcWideReverse;
2516             break;
2517         case gcWBold:
2518             dstId = gcBoldReverse;
2519             break;
2520         case gcWideReverse:
2521         case gcWBoldReverse:
2522             /* FIXME */
2523             break;
2524 #endif
2525 #if OPT_TEK4014
2526         case gcTKcurs:
2527             /* FIXME */
2528             break;
2529 #endif
2530         case gcMAX:
2531             break;
2532         }
2533
2534         if (dstId != gcMAX) {
2535             setCgsFore(xw, currentWin, dstId, bg);
2536             setCgsBack(xw, currentWin, dstId, fg);
2537
2538             XFillRectangle(screen->display, VWindow(screen),
2539                            getCgsGC(xw, currentWin, dstId),
2540                            x, y,
2541                            len * (Cardinal) FontWidth(screen),
2542                            (unsigned) FontHeight(screen));
2543         }
2544     }
2545 }
2546
2547 #if OPT_TRACE
2548 static void
2549 xtermSetClipRectangles(Display * dpy,
2550                        GC gc,
2551                        int x,
2552                        int y,
2553                        XRectangle * rp,
2554                        Cardinal nr,
2555                        int order)
2556 {
2557 #if 0
2558     TScreen *screen = TScreenOf(term);
2559     Drawable draw = VWindow(screen);
2560
2561     XSetClipMask(dpy, gc, None);
2562     XDrawRectangle(screen->display, draw, gc,
2563                    x + rp->x - 1,
2564                    y + rp->y - 1,
2565                    rp->width,
2566                    rp->height);
2567 #endif
2568
2569     XSetClipRectangles(dpy, gc,
2570                        x, y, rp, (int) nr, order);
2571     TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
2572            y, x,
2573            rp->y, rp->x, rp->height, rp->width));
2574 }
2575
2576 #else
2577 #define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
2578             XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
2579 #endif
2580
2581 #if OPT_CLIP_BOLD
2582 /*
2583  * This special case is a couple of percent slower, but avoids a lot of pixel
2584  * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
2585  */
2586 #define beginClipping(screen,gc,pwidth,plength) \
2587             if (screen->use_clipping && (pwidth > 2)) { \
2588                 XRectangle clip; \
2589                 int clip_x = x; \
2590                 int clip_y = y - FontHeight(screen) + FontDescent(screen); \
2591                 clip.x = 0; \
2592                 clip.y = 0; \
2593                 clip.height = (unsigned short) FontHeight(screen); \
2594                 clip.width = (unsigned short) (pwidth * plength); \
2595                 xtermSetClipRectangles(screen->display, gc, \
2596                                        clip_x, clip_y, \
2597                                        &clip, 1, Unsorted); \
2598             }
2599 #define endClipping(screen,gc) \
2600             XSetClipMask(screen->display, gc, None)
2601 #else
2602 #define beginClipping(screen,gc,pwidth,plength)         /* nothing */
2603 #define endClipping(screen,gc)  /* nothing */
2604 #endif /* OPT_CLIP_BOLD */
2605
2606 #if OPT_CLIP_BOLD && OPT_RENDERFONT && defined(HAVE_XFTDRAWSETCLIP) && defined(HAVE_XFTDRAWSETCLIPRECTANGLES)
2607 #define beginXftClipping(screen,px,py,plength) \
2608             if (screen->use_clipping && (FontWidth(screen) > 2)) { \
2609                 XRectangle clip; \
2610                 int clip_x = px; \
2611                 int clip_y = py - FontHeight(screen) + FontDescent(screen); \
2612                 clip.x = 0; \
2613                 clip.y = 0; \
2614                 clip.height = (unsigned short) (FontHeight(screen)); \
2615                 clip.width = (unsigned short) (FontWidth(screen) * plength); \
2616                 XftDrawSetClipRectangles (screen->renderDraw, \
2617                                           clip_x, clip_y, \
2618                                           &clip, 1); \
2619             }
2620 #define endXftClipping(screen) \
2621             XftDrawSetClip (screen->renderDraw, 0)
2622 #else
2623 #define beginXftClipping(screen,px,py,plength)  /* nothing */
2624 #define endXftClipping(screen)  /* nothing */
2625 #endif /* OPT_CLIP_BOLD */
2626
2627 #if OPT_RENDERFONT
2628 static int
2629 drawClippedXftString(XtermWidget xw,
2630                      unsigned flags,
2631                      XftFont * font,
2632                      XftColor * fg_color,
2633                      int x,
2634                      int y,
2635                      IChar * text,
2636                      Cardinal len)
2637 {
2638     int ncells = xtermXftWidth(xw, flags,
2639                                fg_color,
2640                                font, x, y,
2641                                text,
2642                                len);
2643     TScreen *screen = TScreenOf(xw);
2644
2645     beginXftClipping(screen, x, y, ncells);
2646     xtermXftDrawString(xw, flags,
2647                        fg_color,
2648                        font, x, y,
2649                        text,
2650                        len,
2651                        True);
2652     endXftClipping(screen);
2653     return ncells;
2654 }
2655 #endif
2656
2657 #ifndef NO_ACTIVE_ICON
2658 #define WhichVFontData(screen,name) \
2659                 (IsIcon(screen) ? &((screen)->fnt_icon) \
2660                                 : &((screen)->name))
2661 #else
2662 #define WhichVFontData(screen,name) \
2663                                 (&((screen)->name))
2664 #endif
2665
2666 /*
2667  * Draws text with the specified combination of bold/underline.  The return
2668  * value is the updated x position.
2669  */
2670 int
2671 drawXtermText(XtermWidget xw,
2672               unsigned flags,
2673               GC gc,
2674               int x,
2675               int y,
2676               int chrset,
2677               IChar * text,
2678               Cardinal len,
2679               int on_wide)
2680 {
2681     TScreen *screen = TScreenOf(xw);
2682     Cardinal real_length = len;
2683     Cardinal underline_len = 0;
2684     /* Intended width of the font to draw (as opposed to the actual width of
2685        the X font, and the width of the default font) */
2686     int font_width = ((flags & DOUBLEWFONT) ? 2 : 1) * screen->fnt_wide;
2687     Bool did_ul = False;
2688
2689 #if OPT_WIDE_CHARS
2690     if (text == 0)
2691         return 0;
2692 #endif
2693 #if OPT_DEC_CHRSET
2694     if (CSET_DOUBLE(chrset)) {
2695         /* We could try drawing double-size characters in the icon, but
2696          * given that the icon font is usually nil or nil2, there
2697          * doesn't seem to be much point.
2698          */
2699         int inx = 0;
2700         GC gc2 = ((!IsIcon(screen) && screen->font_doublesize)
2701                   ? xterm_DoubleGC(xw, (unsigned) chrset, flags, gc, &inx)
2702                   : 0);
2703
2704         TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
2705                screen->cursor_state == OFF ? ' ' : '*',
2706                y, x, chrset, len,
2707                visibleIChars(text, len)));
2708
2709         if (gc2 != 0) {         /* draw actual double-sized characters */
2710             XFontStruct *fs = screen->double_fonts[inx].fs;
2711
2712 #if OPT_RENDERFONT
2713             if (!UsingRenderFont(xw))
2714 #endif
2715             {
2716                 XRectangle rect, *rp = &rect;
2717                 Cardinal nr = 1;
2718
2719                 font_width *= 2;
2720                 flags |= DOUBLEWFONT;
2721
2722                 rect.x = 0;
2723                 rect.y = 0;
2724                 rect.width = (unsigned short) ((int) len * font_width);
2725                 rect.height = (unsigned short) (FontHeight(screen));
2726
2727                 TRACE(("drawing %s\n", visibleChrsetName((unsigned) chrset)));
2728                 switch (chrset) {
2729                 case CSET_DHL_TOP:
2730                     rect.y = (short) -(fs->ascent / 2);
2731                     y -= rect.y;
2732                     flags |= DOUBLEHFONT;
2733                     break;
2734                 case CSET_DHL_BOT:
2735                     rect.y = (short) (rect.height - (fs->ascent / 2));
2736                     y -= rect.y;
2737                     flags |= DOUBLEHFONT;
2738                     break;
2739                 default:
2740                     nr = 0;
2741                     break;
2742                 }
2743
2744                 if (nr) {
2745                     xtermSetClipRectangles(screen->display, gc2,
2746                                            x, y, rp, nr, YXBanded);
2747                 } else {
2748                     XSetClipMask(screen->display, gc2, None);
2749                 }
2750             }
2751
2752             /* Call ourselves recursively with the new gc */
2753
2754             /*
2755              * If we're trying to use proportional font, or if the
2756              * font server didn't give us what we asked for wrt
2757              * width, position each character independently.
2758              */
2759             if (screen->fnt_prop
2760                 || (fs->min_bounds.width != fs->max_bounds.width)
2761                 || (fs->min_bounds.width != 2 * FontWidth(screen))) {
2762                 /* It is hard to fall-through to the main
2763                    branch: in a lot of places the check
2764                    for the cached font info is for
2765                    normal/bold fonts only. */
2766                 while (len--) {
2767                     x = drawXtermText(xw, flags, gc2,
2768                                       x, y, 0,
2769                                       text++,
2770                                       1, on_wide);
2771                     x += FontWidth(screen);
2772                 }
2773             } else {
2774                 x = drawXtermText(xw, flags, gc2,
2775                                   x, y, 0,
2776                                   text,
2777                                   len, on_wide);
2778                 x += (int) len *FontWidth(screen);
2779             }
2780
2781             TRACE(("drawtext [%4d,%4d]\n", y, x));
2782         } else {                /* simulate double-sized characters */
2783             unsigned need = 2 * len;
2784             IChar *temp = TypeMallocN(IChar, need);
2785             unsigned n = 0;
2786
2787             while (len--) {
2788                 temp[n++] = *text++;
2789                 temp[n++] = ' ';
2790             }
2791             x = drawXtermText(xw,
2792                               flags,
2793                               gc,
2794                               x, y,
2795                               0,
2796                               temp,
2797                               n,
2798                               on_wide);
2799             free(temp);
2800         }
2801         return x;
2802     }
2803 #endif
2804 #if OPT_RENDERFONT
2805     if (UsingRenderFont(xw)) {
2806         VTwin *currentWin = WhichVWin(screen);
2807         Display *dpy = screen->display;
2808         XftFont *font;
2809         XGCValues values;
2810         int fontnum = screen->menu_font_number;
2811         int ncells;
2812
2813         if (!screen->renderDraw) {
2814             int scr;
2815             Drawable draw = VWindow(screen);
2816             Visual *visual;
2817
2818             scr = DefaultScreen(dpy);
2819             visual = DefaultVisual(dpy, scr);
2820             screen->renderDraw = XftDrawCreate(dpy, draw, visual,
2821                                                DefaultColormap(dpy, scr));
2822         }
2823 #if OPT_ISO_COLORS
2824         if ((flags & UNDERLINE)
2825             && !screen->colorULMode
2826             && screen->italicULMode
2827             && XFT_FONT(renderFontItal[fontnum])) {
2828             font = XFT_FONT(renderFontItal[fontnum]);
2829             did_ul = True;
2830         } else
2831 #endif
2832             if ((flags & BOLDATTR(screen))
2833                 && UseBoldFont(screen)
2834                 && XFT_FONT(renderFontBold[fontnum])) {
2835             font = XFT_FONT(renderFontBold[fontnum]);
2836         } else {
2837             font = XFT_FONT(renderFontNorm[fontnum]);
2838         }
2839         values.foreground = getCgsFore(xw, currentWin, gc);
2840         values.background = getCgsBack(xw, currentWin, gc);
2841
2842         if (!(flags & NOBACKGROUND)) {
2843             XftColor *bg_color = getXftColor(xw, values.background);
2844             ncells = xtermXftWidth(xw, flags,
2845                                    bg_color,
2846                                    font, x, y,
2847                                    text,
2848                                    len);
2849             XftDrawRect(screen->renderDraw,
2850                         bg_color,
2851                         x, y,
2852                         (unsigned) (ncells * FontWidth(screen)),
2853                         (unsigned) FontHeight(screen));
2854         }
2855
2856         y += font->ascent;
2857 #if OPT_BOX_CHARS
2858         {
2859             /* adding code to substitute simulated line-drawing characters */
2860             int last, first = 0;
2861             Dimension old_wide, old_high = 0;
2862             int curX = x;
2863
2864             for (last = 0; last < (int) len; last++) {
2865                 Boolean replace = False;
2866                 Boolean missing = False;
2867                 unsigned ch = (unsigned) text[last];
2868                 int nc;
2869 #if OPT_WIDE_CHARS
2870
2871                 if (xtermIsDecGraphic(ch)) {
2872                     /*
2873                      * Xft generally does not have the line-drawing characters
2874                      * in cells 1-31.  Assume this (we cannot inspect the
2875                      * picture easily...), and attempt to fill in from real
2876                      * line-drawing character in the font at the Unicode
2877                      * position.  Failing that, use our own box-characters.
2878                      */
2879                     if (screen->force_box_chars
2880                         || xtermXftMissing(xw, font, dec2ucs(ch))) {
2881                         missing = 1;
2882                     } else {
2883                         ch = dec2ucs(ch);
2884                         replace = True;
2885                     }
2886                 } else if (ch >= 256) {
2887                     /*
2888                      * If we're reading UTF-8 from the client, we may have a
2889                      * line-drawing character.  Translate it back to our
2890                      * box-code if Xft tells us that the glyph is missing.
2891                      */
2892                     if_OPT_WIDE_CHARS(screen, {
2893                         unsigned part = ucs2dec(ch);
2894                         if (xtermIsDecGraphic(part) &&
2895                             (screen->force_box_chars
2896                              || xtermXftMissing(xw, font, ch))) {
2897                             ch = part;
2898                             missing = True;
2899                         }
2900                     });
2901                 }
2902 #else
2903                 if (xtermIsDecGraphic(ch)) {
2904                     /*
2905                      * Xft generally does not have the line-drawing characters
2906                      * in cells 1-31.  Check for this, and attempt to fill in
2907                      * from real line-drawing character in the font at the
2908                      * Unicode position.  Failing that, use our own
2909                      * box-characters.
2910                      */
2911                     if (xtermXftMissing(xw, font, ch)) {
2912                         missing = 1;
2913                     }
2914                 }
2915 #endif
2916
2917                 /*
2918                  * If we now have one of our box-codes, draw it directly.
2919                  */
2920                 if (missing || replace) {
2921                     /* line drawing character time */
2922                     if (last > first) {
2923                         nc = drawClippedXftString(xw,
2924                                                   flags,
2925                                                   font,
2926                                                   getXftColor(xw, values.foreground),
2927                                                   curX,
2928                                                   y,
2929                                                   text + first,
2930                                                   (Cardinal) (last - first));
2931                         curX += nc * FontWidth(screen);
2932                         underline_len += (Cardinal) nc;
2933                     }
2934                     if (missing) {
2935                         old_wide = screen->fnt_wide;
2936                         old_high = screen->fnt_high;
2937                         screen->fnt_wide = (Dimension) FontWidth(screen);
2938                         screen->fnt_high = (Dimension) FontHeight(screen);
2939                         xtermDrawBoxChar(xw, ch, flags, gc,
2940                                          curX, y - FontAscent(screen), 1);
2941                         curX += FontWidth(screen);
2942                         underline_len += 1;
2943                         screen->fnt_wide = old_wide;
2944                         screen->fnt_high = old_high;
2945                     } else {
2946                         IChar ch2 = (IChar) ch;
2947                         nc = drawClippedXftString(xw,
2948                                                   flags,
2949                                                   font,
2950                                                   getXftColor(xw, values.foreground),
2951                                                   curX,
2952                                                   y,
2953                                                   &ch2,
2954                                                   1);
2955                         curX += nc * FontWidth(screen);
2956                         underline_len += (Cardinal) nc;
2957                     }
2958                     first = last + 1;
2959                 }
2960             }
2961             if (last > first) {
2962                 underline_len += (Cardinal)
2963                     drawClippedXftString(xw,
2964                                          flags,
2965                                          font,
2966                                          getXftColor(xw, values.foreground),
2967                                          curX,
2968                                          y,
2969                                          text + first,
2970                                          (Cardinal) (last - first));
2971             }
2972         }
2973 #else
2974         {
2975             underline_len += (Cardinal)
2976                 drawClippedXftString(xw,
2977                                      flags,
2978                                      font,
2979                                      getXftColor(xw, values.foreground),
2980                                      x,
2981                                      y,
2982                                      text,
2983                                      len);
2984         }
2985 #endif /* OPT_BOX_CHARS */
2986
2987         if ((flags & UNDERLINE) && screen->underline && !did_ul) {
2988             if (FontDescent(screen) > 1)
2989                 y++;
2990             XDrawLine(screen->display, VWindow(screen), gc,
2991                       x, y,
2992                       x + (int) underline_len * FontWidth(screen) - 1,
2993                       y);
2994         }
2995         return x + (int) len *FontWidth(screen);
2996     }
2997 #endif /* OPT_RENDERFONT */
2998     /*
2999      * If we're asked to display a proportional font, do this with a fixed
3000      * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
3001      * as a dumb terminal vs its use as in fullscreen programs such as vi.
3002      * Hint: do not try to use a proportional font in the icon.
3003      */
3004     if (!IsIcon(screen) && !(flags & CHARBYCHAR) && screen->fnt_prop) {
3005         int adj, width;
3006         XTermFonts *font = ((flags & BOLDATTR(screen))
3007                             ? WhichVFontData(screen, fnts[fBold])
3008                             : WhichVFontData(screen, fnts[fNorm]));
3009
3010         while (len--) {
3011             int cells = WideCells(*text);
3012 #if OPT_BOX_CHARS
3013 #if OPT_WIDE_CHARS
3014             if (*text == HIDDEN_CHAR) {
3015                 ++text;
3016                 continue;
3017             } else
3018 #endif
3019             if (IsXtermMissingChar(screen, *text, font)) {
3020                 adj = 0;
3021             } else
3022 #endif
3023             {
3024                 if_WIDE_OR_NARROW(screen, {
3025                     XChar2b temp[1];
3026                     temp[0].byte2 = LO_BYTE(*text);
3027                     temp[0].byte1 = HI_BYTE(*text);
3028                     width = XTextWidth16(font->fs, temp, 1);
3029                 }
3030                 , {
3031                     char temp[1];
3032                     temp[0] = (char) LO_BYTE(*text);
3033                     width = XTextWidth(font->fs, temp, 1);
3034                 });
3035                 adj = (FontWidth(screen) - width) / 2;
3036                 if (adj < 0)
3037                     adj = 0;
3038             }
3039             xtermFillCells(xw, flags, gc, x, y, (Cardinal) cells);
3040             x = drawXtermText(xw,
3041                               flags | NOBACKGROUND | CHARBYCHAR,
3042                               gc, x + adj, y, chrset,
3043                               text++, 1, on_wide) - adj;
3044         }
3045         return x;
3046     }
3047 #if OPT_BOX_CHARS
3048     /* If the font is incomplete, draw some substitutions */
3049     if (!IsIcon(screen)
3050         && !(flags & NOTRANSLATION)
3051         && (!screen->fnt_boxes || screen->force_box_chars)) {
3052         /* Fill in missing box-characters.
3053            Find regions without missing characters, and draw
3054            them calling ourselves recursively.  Draw missing
3055            characters via xtermDrawBoxChar(). */
3056         XTermFonts *font = ((flags & BOLDATTR(screen))
3057                             ? WhichVFontData(screen, fnts[fBold])
3058                             : WhichVFontData(screen, fnts[fNorm]));
3059         int last, first = 0;
3060         Bool drewBoxes = False;
3061
3062         for (last = 0; last < (int) len; last++) {
3063             unsigned ch = (unsigned) text[last];
3064             Bool isMissing;
3065             int ch_width;
3066 #if OPT_WIDE_CHARS
3067
3068             if (ch == HIDDEN_CHAR) {
3069                 if (last > first) {
3070                     x = drawXtermText(xw, flags | NOTRANSLATION, gc,
3071                                       x, y,
3072                                       chrset, text + first,
3073                                       (unsigned) (last - first), on_wide);
3074                 }
3075                 first = last + 1;
3076                 drewBoxes = True;
3077                 continue;
3078             }
3079             ch_width = my_wcwidth((int) ch);
3080             isMissing =
3081                 IsXtermMissingChar(screen, ch,
3082                                    ((on_wide || ch_width > 1)
3083                                     && okFont(NormalWFont(screen)))
3084                                    ? WhichVFontData(screen, fnts[fWide])
3085                                    : font);
3086 #else
3087             isMissing = IsXtermMissingChar(screen, ch, font);
3088             ch_width = 1;
3089 #endif
3090             /*
3091              * If the character is not missing, but we're in wide-character
3092              * mode and the character happens to be a wide-character that
3093              * corresponds to the line-drawing set, allow the forceBoxChars
3094              * resource (or menu entry) to force it to display using our
3095              * tables.
3096              */
3097             if_OPT_WIDE_CHARS(screen, {
3098                 if (!isMissing
3099                     && ch > 255
3100                     && ucs2dec(ch) < 32
3101                     && TScreenOf(xw)->force_box_chars) {
3102                     ch = ucs2dec(ch);
3103                     isMissing = True;
3104                 }
3105             });
3106
3107             if (isMissing) {
3108                 if (last > first) {
3109                     x = drawXtermText(xw, flags | NOTRANSLATION, gc,
3110                                       x, y,
3111                                       chrset, text + first,
3112                                       (unsigned) (last - first), on_wide);
3113                 }
3114 #if OPT_WIDE_CHARS
3115                 if (ucs_workaround(xw, ch, flags, gc,
3116                                    x, y,
3117                                    chrset, on_wide)) {
3118                     /*
3119                      * if true, we drew at least one cell whether or not it is
3120                      * printable
3121                      */
3122                     if (ch_width <= 0)
3123                         ch_width = 1;
3124                 } else
3125 #endif
3126                 {
3127                     if (ch_width <= 0)
3128                         ch_width = 1;
3129                     xtermDrawBoxChar(xw, ch, flags, gc,
3130                                      x, y,
3131                                      ch_width);
3132                 }
3133                 x += (ch_width * FontWidth(screen));
3134                 first = last + 1;
3135                 drewBoxes = True;
3136             }
3137         }
3138         if (last <= first) {
3139             return x;
3140         }
3141         text += first;
3142         len = (Cardinal) (last - first);
3143         flags |= NOTRANSLATION;
3144         if (drewBoxes) {
3145             return drawXtermText(xw,
3146                                  flags,
3147                                  gc,
3148                                  x,
3149                                  y,
3150                                  chrset,
3151                                  text,
3152                                  len,
3153                                  on_wide);
3154         }
3155     }
3156 #endif /* OPT_BOX_CHARS */
3157     /*
3158      * Behave as if the font has (maybe Unicode-replacements for) drawing
3159      * characters in the range 1-31 (either we were not asked to ignore them,
3160      * or the caller made sure that there is none).
3161      */
3162     TRACE(("drawtext%c[%4d,%4d] (%d) %d:%s\n",
3163            screen->cursor_state == OFF ? ' ' : '*',
3164            y, x, chrset, len,
3165            visibleIChars(text, len)));
3166     y += FontAscent(screen);
3167
3168 #if OPT_WIDE_CHARS
3169
3170     if (screen->wide_chars || screen->unicode_font) {
3171         XChar2b *buffer;
3172         Bool needWide = False;
3173         int ascent_adjust = 0;
3174         int src, dst;
3175
3176         BumpTypedBuffer(XChar2b, len);
3177         buffer = BfBuf(XChar2b);
3178
3179         for (src = dst = 0; src < (int) len; src++) {
3180             IChar ch = text[src];
3181
3182             if (ch == HIDDEN_CHAR)
3183                 continue;
3184
3185             if (!needWide
3186                 && !IsIcon(screen)
3187                 && ((on_wide || my_wcwidth((int) ch) > 1)
3188                     && okFont(NormalWFont(screen)))) {
3189                 needWide = True;
3190             }
3191
3192             /*
3193              * bitmap-fonts are limited to 16-bits.
3194              */
3195 #if OPT_WIDER_ICHAR
3196             if (ch > 0xffff) {
3197                 ch = UCS_REPL;
3198             }
3199 #endif
3200             buffer[dst].byte2 = LO_BYTE(ch);
3201             buffer[dst].byte1 = HI_BYTE(ch);
3202 #if OPT_MINI_LUIT
3203 #define UCS2SBUF(value) buffer[dst].byte2 = LO_BYTE(value);\
3204                         buffer[dst].byte1 = HI_BYTE(value)
3205
3206 #define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); }
3207
3208             if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) {
3209
3210                 /* see http://www.cs.tut.fi/~jkorpela/latin9.html */
3211                 /* *INDENT-OFF* */
3212                 if Map2Sbuf(0xa4, 0x20ac)
3213                 else if Map2Sbuf(0xa6, 0x0160)
3214                 else if Map2Sbuf(0xa8, 0x0161)
3215                 else if Map2Sbuf(0xb4, 0x017d)
3216                 else if Map2Sbuf(0xb8, 0x017e)
3217                 else if Map2Sbuf(0xbc, 0x0152)
3218                 else if Map2Sbuf(0xbd, 0x0153)
3219                 else if Map2Sbuf(0xbe, 0x0178)
3220                 /* *INDENT-ON* */
3221
3222             }
3223             if (screen->unicode_font
3224                 && (text[src] == ANSI_DEL ||
3225                     text[src] < ANSI_SPA)) {
3226                 unsigned ni = dec2ucs((unsigned) ((text[src] == ANSI_DEL)
3227                                                   ? 0
3228                                                   : text[src]));
3229                 UCS2SBUF(ni);
3230             }
3231 #endif /* OPT_MINI_LUIT */
3232             ++dst;
3233         }
3234         /* FIXME This is probably wrong. But it works. */
3235         underline_len = len;
3236
3237         /* Set the drawing font */
3238         if (!(flags & (DOUBLEHFONT | DOUBLEWFONT))) {
3239             VTwin *currentWin = WhichVWin(screen);
3240             VTFontEnum fntId;
3241             CgsEnum cgsId;
3242             Pixel fg = getCgsFore(xw, currentWin, gc);
3243             Pixel bg = getCgsBack(xw, currentWin, gc);
3244
3245             if (needWide
3246                 && (okFont(NormalWFont(screen)) || okFont(BoldWFont(screen)))) {
3247                 if ((flags & BOLDATTR(screen)) != 0
3248                     && okFont(BoldWFont(screen))) {
3249                     fntId = fWBold;
3250                     cgsId = gcWBold;
3251                 } else {
3252                     fntId = fWide;
3253                     cgsId = gcWide;
3254                 }
3255             } else if ((flags & BOLDATTR(screen)) != 0
3256                        && okFont(BoldFont(screen))) {
3257                 fntId = fBold;
3258                 cgsId = gcBold;
3259             } else {
3260                 fntId = fNorm;
3261                 cgsId = gcNorm;
3262             }
3263
3264             setCgsFore(xw, currentWin, cgsId, fg);
3265             setCgsBack(xw, currentWin, cgsId, bg);
3266             gc = getCgsGC(xw, currentWin, cgsId);
3267
3268             if (fntId != fNorm) {
3269                 XFontStruct *thisFp = WhichVFont(screen, fnts[fntId].fs);
3270                 ascent_adjust = (thisFp->ascent
3271                                  - NormalFont(screen)->ascent);
3272                 if (thisFp->max_bounds.width ==
3273                     NormalFont(screen)->max_bounds.width * 2) {
3274                     underline_len = real_length = (Cardinal) (dst * 2);
3275                 } else if (cgsId == gcWide || cgsId == gcWBold) {
3276                     underline_len = real_length = (Cardinal) (dst * 2);
3277                     xtermFillCells(xw,
3278                                    flags,
3279                                    gc,
3280                                    x,
3281                                    y - thisFp->ascent,
3282                                    real_length);
3283                 }
3284             }
3285         }
3286
3287         if (flags & NOBACKGROUND) {
3288             XDrawString16(screen->display,
3289                           VWindow(screen), gc,
3290                           x, y + ascent_adjust,
3291                           buffer, dst);
3292         } else {
3293             XDrawImageString16(screen->display,
3294                                VWindow(screen), gc,
3295                                x, y + ascent_adjust,
3296                                buffer, dst);
3297         }
3298
3299         if ((flags & BOLDATTR(screen)) && screen->enbolden) {
3300             beginClipping(screen, gc, (Cardinal) font_width, len);
3301             XDrawString16(screen->display, VWindow(screen), gc,
3302                           x + 1,
3303                           y + ascent_adjust,
3304                           buffer, dst);
3305             endClipping(screen, gc);
3306         }
3307
3308     } else
3309 #endif /* OPT_WIDE_CHARS */
3310     {
3311         int length = (int) len; /* X should have used unsigned */
3312 #if OPT_WIDE_CHARS
3313         char *buffer;
3314         int dst;
3315
3316         BumpTypedBuffer(char, len);
3317         buffer = BfBuf(char);
3318
3319         for (dst = 0; dst < length; ++dst)
3320             buffer[dst] = (char) LO_BYTE(text[dst]);
3321 #else
3322         char *buffer = (char *) text;
3323 #endif
3324
3325         if (flags & NOBACKGROUND) {
3326             XDrawString(screen->display, VWindow(screen), gc,
3327                         x, y, buffer, length);
3328         } else {
3329             XDrawImageString(screen->display, VWindow(screen), gc,
3330                              x, y, buffer, length);
3331         }
3332         underline_len = (Cardinal) length;
3333         if ((flags & BOLDATTR(screen)) && screen->enbolden) {
3334             beginClipping(screen, gc, font_width, length);
3335             XDrawString(screen->display, VWindow(screen), gc,
3336                         x + 1, y, buffer, length);
3337             endClipping(screen, gc);
3338         }
3339     }
3340
3341     if ((flags & UNDERLINE) && screen->underline && !did_ul) {
3342         if (FontDescent(screen) > 1)
3343             y++;
3344         XDrawLine(screen->display, VWindow(screen), gc,
3345                   x, y, (x + (int) underline_len * font_width - 1), y);
3346     }
3347
3348     return x + (int) real_length *FontWidth(screen);
3349 }
3350
3351 #if OPT_WIDE_CHARS
3352 /*
3353  * Allocate buffer - workaround for wide-character interfaces.
3354  */
3355 void
3356 allocXtermChars(ScrnPtr * buffer, Cardinal length)
3357 {
3358     if (*buffer == 0) {
3359         *buffer = (ScrnPtr) XtMalloc(length);
3360     } else {
3361         *buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
3362     }
3363 }
3364 #endif
3365
3366 /* set up size hints for window manager; min 1 char by 1 char */
3367 void
3368 xtermSizeHints(XtermWidget xw, int scrollbarWidth)
3369 {
3370     TScreen *screen = TScreenOf(xw);
3371
3372     TRACE(("xtermSizeHints\n"));
3373     TRACE(("   border    %d\n", xw->core.border_width));
3374     TRACE(("   scrollbar %d\n", scrollbarWidth));
3375
3376     xw->hints.base_width = 2 * screen->border + scrollbarWidth;
3377     xw->hints.base_height = 2 * screen->border;
3378
3379 #if OPT_TOOLBAR
3380     TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
3381
3382     xw->hints.base_height += ToolbarHeight(xw);
3383     xw->hints.base_height += BorderWidth(xw) * 2;
3384     xw->hints.base_width += BorderWidth(xw) * 2;
3385 #endif
3386
3387     xw->hints.width_inc = FontWidth(screen);
3388     xw->hints.height_inc = FontHeight(screen);
3389     xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
3390     xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
3391
3392     xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
3393     xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
3394
3395     xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
3396
3397     TRACE_HINTS(&(xw->hints));
3398 }
3399
3400 void
3401 getXtermSizeHints(XtermWidget xw)
3402 {
3403     TScreen *screen = TScreenOf(xw);
3404     long supp;
3405
3406     if (!XGetWMNormalHints(screen->display, VShellWindow(xw),
3407                            &xw->hints, &supp))
3408         memset(&xw->hints, 0, sizeof(xw->hints));
3409     TRACE_HINTS(&(xw->hints));
3410 }
3411
3412 /*
3413  * Returns a GC, selected according to the font (reverse/bold/normal) that is
3414  * required for the current position (implied).  The GC is updated with the
3415  * current screen foreground and background colors.
3416  */
3417 GC
3418 updatedXtermGC(XtermWidget xw, unsigned flags, unsigned fg_bg, Bool hilite)
3419 {
3420     TScreen *screen = TScreenOf(xw);
3421     VTwin *win = WhichVWin(screen);
3422     CgsEnum cgsId = gcMAX;
3423     unsigned my_fg = extract_fg(xw, fg_bg, flags);
3424     unsigned my_bg = extract_bg(xw, fg_bg, flags);
3425     Pixel fg_pix = getXtermForeground(xw, flags, my_fg);
3426     Pixel bg_pix = getXtermBackground(xw, flags, my_bg);
3427     Pixel xx_pix;
3428 #if OPT_HIGHLIGHT_COLOR
3429     Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
3430     Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
3431     Boolean always = screen->hilite_color;
3432     Boolean use_selbg = (Boolean) (always ||
3433                                    isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
3434     Boolean use_selfg = (Boolean) (always &&
3435                                    isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
3436 #endif
3437
3438     (void) fg_bg;
3439     (void) my_bg;
3440     (void) my_fg;
3441
3442     /*
3443      * Discard video attributes overridden by colorXXXMode's.
3444      */
3445     checkVeryBoldColors(flags, my_fg);
3446
3447     if (ReverseOrHilite(screen, flags, hilite)) {
3448         if (flags & BOLDATTR(screen)) {
3449             cgsId = gcBoldReverse;
3450         } else {
3451             cgsId = gcNormReverse;
3452         }
3453
3454 #if OPT_HIGHLIGHT_COLOR
3455         if (!screen->hilite_color) {
3456             if (selbg_pix != T_COLOR(screen, TEXT_FG)
3457                 && selbg_pix != fg_pix
3458                 && selbg_pix != bg_pix
3459                 && selbg_pix != xw->dft_foreground) {
3460                 bg_pix = fg_pix;
3461                 fg_pix = selbg_pix;
3462             }
3463         }
3464 #endif
3465         EXCHANGE(fg_pix, bg_pix, xx_pix);
3466 #if OPT_HIGHLIGHT_COLOR
3467         if (screen->hilite_color) {
3468             if (screen->hilite_reverse) {
3469                 if (use_selbg) {
3470                     if (use_selfg)
3471                         bg_pix = fg_pix;
3472                     else
3473                         fg_pix = bg_pix;
3474                 }
3475                 if (use_selbg)
3476                     bg_pix = selbg_pix;
3477                 if (use_selfg)
3478                     fg_pix = selfg_pix;
3479             }
3480         }
3481 #endif
3482     } else {
3483         if (flags & BOLDATTR(screen)) {
3484             cgsId = gcBold;
3485         } else {
3486             cgsId = gcNorm;
3487         }
3488     }
3489 #if OPT_HIGHLIGHT_COLOR
3490     if (!screen->hilite_color || !screen->hilite_reverse) {
3491         if (hilite && !screen->hilite_reverse) {
3492             if (use_selbg)
3493                 bg_pix = selbg_pix;
3494             if (use_selfg)
3495                 fg_pix = selfg_pix;
3496         }
3497     }
3498 #endif
3499
3500 #if OPT_BLINK_TEXT
3501     if ((screen->blink_state == ON) && (!screen->blink_as_bold) && (flags & BLINK)) {
3502         fg_pix = bg_pix;
3503     }
3504 #endif
3505
3506     setCgsFore(xw, win, cgsId, fg_pix);
3507     setCgsBack(xw, win, cgsId, bg_pix);
3508     return getCgsGC(xw, win, cgsId);
3509 }
3510
3511 /*
3512  * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
3513  * to the values that would be set in SGR_Foreground and SGR_Background. This
3514  * duplicates some logic, but only modifies 1/4 as many GC's.
3515  */
3516 void
3517 resetXtermGC(XtermWidget xw, unsigned flags, Bool hilite)
3518 {
3519     TScreen *screen = TScreenOf(xw);
3520     VTwin *win = WhichVWin(screen);
3521     CgsEnum cgsId = gcMAX;
3522     Pixel fg_pix = getXtermForeground(xw, flags, xw->cur_foreground);
3523     Pixel bg_pix = getXtermBackground(xw, flags, xw->cur_background);
3524
3525     checkVeryBoldColors(flags, xw->cur_foreground);
3526
3527     if (ReverseOrHilite(screen, flags, hilite)) {
3528         if (flags & BOLDATTR(screen)) {
3529             cgsId = gcBoldReverse;
3530         } else {
3531             cgsId = gcNormReverse;
3532         }
3533
3534         setCgsFore(xw, win, cgsId, bg_pix);
3535         setCgsBack(xw, win, cgsId, fg_pix);
3536
3537     } else {
3538         if (flags & BOLDATTR(screen)) {
3539             cgsId = gcBold;
3540         } else {
3541             cgsId = gcNorm;
3542         }
3543
3544         setCgsFore(xw, win, cgsId, fg_pix);
3545         setCgsBack(xw, win, cgsId, bg_pix);
3546     }
3547 }
3548
3549 #if OPT_ISO_COLORS
3550 /*
3551  * Extract the foreground-color index from a color pair.
3552  * If we've got BOLD or UNDERLINE color-mode active, those will be used.
3553  */
3554 unsigned
3555 extract_fg(XtermWidget xw, unsigned color, unsigned flags)
3556 {
3557     unsigned fg = ExtractForeground(color);
3558
3559     if (TScreenOf(xw)->colorAttrMode
3560         || (fg == ExtractBackground(color))) {
3561         fg = MapToColorMode(fg, TScreenOf(xw), flags);
3562     }
3563     return fg;
3564 }
3565
3566 /*
3567  * Extract the background-color index from a color pair.
3568  * If we've got INVERSE color-mode active, that will be used.
3569  */
3570 unsigned
3571 extract_bg(XtermWidget xw, unsigned color, unsigned flags)
3572 {
3573     unsigned bg = ExtractBackground(color);
3574
3575     if (TScreenOf(xw)->colorAttrMode
3576         || (bg == ExtractForeground(color))) {
3577         if (TScreenOf(xw)->colorRVMode && (flags & INVERSE))
3578             bg = COLOR_RV;
3579     }
3580     return bg;
3581 }
3582
3583 /*
3584  * Combine the current foreground and background into a single 8-bit number.
3585  * Note that we're storing the SGR foreground, since cur_foreground may be set
3586  * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
3587  * bits.
3588  *
3589  * This assumes that fg/bg are equal when we override with one of the special
3590  * attribute colors.
3591  */
3592 CellColor
3593 makeColorPair(int fg, int bg)
3594 {
3595     unsigned my_bg = (bg >= 0) && (bg < NUM_ANSI_COLORS) ? (unsigned) bg : 0;
3596     unsigned my_fg = (fg >= 0) && (fg < NUM_ANSI_COLORS) ? (unsigned) fg : my_bg;
3597
3598     return (CellColor) (my_fg | (my_bg << COLOR_BITS));
3599 }
3600
3601 /*
3602  * Using the "current" SGR background, clear a rectangle.
3603  */
3604 void
3605 ClearCurBackground(XtermWidget xw,
3606                    int top,
3607                    int left,
3608                    unsigned height,
3609                    unsigned width)
3610 {
3611     TScreen *screen = TScreenOf(xw);
3612
3613     TRACE(("ClearCurBackground(%d,%d,%d,%d) %d\n",
3614            top, left, height, width, xw->cur_background));
3615
3616     if (VWindow(screen)) {
3617         set_background(xw, xw->cur_background);
3618
3619         XClearArea(screen->display, VWindow(screen),
3620                    left, top, width, height, False);
3621
3622         set_background(xw, -1);
3623     }
3624 }
3625 #endif /* OPT_ISO_COLORS */
3626
3627 /*
3628  * Returns a single base character for the given cell.
3629  */
3630 unsigned
3631 getXtermCell(TScreen * screen, int row, int col)
3632 {
3633     LineData *ld = getLineData(screen, row);
3634
3635     assert(ld && (col < (int) ld->lineSize));
3636     return ((ld && (col < (int) ld->lineSize))
3637             ? ld->charData[col]
3638             : (unsigned) ' ');
3639 }
3640
3641 /*
3642  * Sets a single base character for the given cell.
3643  */
3644 void
3645 putXtermCell(TScreen * screen, int row, int col, int ch)
3646 {
3647     LineData *ld = getLineData(screen, row);
3648
3649     assert(ld && (col < (int) ld->lineSize));
3650     if (ld && (col < (int) ld->lineSize)) {
3651         ld->charData[col] = (CharData) ch;
3652         if_OPT_WIDE_CHARS(screen, {
3653             size_t off;
3654             for_each_combData(off, ld) {
3655                 ld->combData[off][col] = 0;
3656             }
3657         });
3658     }
3659 }
3660
3661 #if OPT_WIDE_CHARS
3662 /*
3663  * Add a combining character for the given cell
3664  */
3665 void
3666 addXtermCombining(TScreen * screen, int row, int col, unsigned ch)
3667 {
3668     if (ch != 0) {
3669         LineData *ld = getLineData(screen, row);
3670         size_t off;
3671
3672         TRACE(("addXtermCombining %d,%d %#x (%d)\n",
3673                row, col, ch, my_wcwidth((wchar_t) ch)));
3674
3675         for_each_combData(off, ld) {
3676             if (!ld->combData[off][col]) {
3677                 ld->combData[off][col] = (CharData) ch;
3678                 break;
3679             }
3680         }
3681     }
3682 }
3683 #endif
3684
3685 #ifdef HAVE_CONFIG_H
3686 #ifdef USE_MY_MEMMOVE
3687 void *
3688 my_memmove(void *s1, void *s2, size_t n)
3689 {
3690     if (n != 0) {
3691         char *p1 = (char *) s1;
3692         char *p2 = (char *) s2;
3693
3694         if ((p1 + n > p2) && (p2 + n > p1)) {
3695             static char *bfr;
3696             static size_t length;
3697             size_t j;
3698             if (length < n) {
3699                 length = (n * 3) / 2;
3700                 bfr = ((bfr != 0)
3701                        ? TypeRealloc(char, length, bfr)
3702                        : TypeMallocN(char, length));
3703                 if (bfr == NULL)
3704                     SysError(ERROR_MMALLOC);
3705             }
3706             for (j = 0; j < n; j++)
3707                 bfr[j] = p2[j];
3708             p2 = bfr;
3709         }
3710         while (n-- != 0)
3711             p1[n] = p2[n];
3712     }
3713     return s1;
3714 }
3715 #endif /* USE_MY_MEMMOVE */
3716
3717 #ifndef HAVE_STRERROR
3718 char *
3719 my_strerror(int n)
3720 {
3721     extern char *sys_errlist[];
3722     extern int sys_nerr;
3723     if (n > 0 && n < sys_nerr)
3724         return sys_errlist[n];
3725     return "?";
3726 }
3727 #endif
3728 #endif
3729
3730 void
3731 update_keyboard_type(void)
3732 {
3733     update_delete_del();
3734     update_tcap_fkeys();
3735     update_old_fkeys();
3736     update_hp_fkeys();
3737     update_sco_fkeys();
3738     update_sun_fkeys();
3739     update_sun_kbd();
3740 }
3741
3742 void
3743 set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
3744 {
3745     xtermKeyboardType save = xw->keyboard.type;
3746
3747     TRACE(("set_keyboard_type(%s, %s) currently %s\n",
3748            visibleKeyboardType(type),
3749            BtoS(set),
3750            visibleKeyboardType(xw->keyboard.type)));
3751     if (set) {
3752         xw->keyboard.type = type;
3753     } else {
3754         xw->keyboard.type = keyboardIsDefault;
3755     }
3756
3757     if (save != xw->keyboard.type) {
3758         update_keyboard_type();
3759     }
3760 }
3761
3762 void
3763 toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
3764 {
3765     xtermKeyboardType save = xw->keyboard.type;
3766
3767     TRACE(("toggle_keyboard_type(%s) currently %s\n",
3768            visibleKeyboardType(type),
3769            visibleKeyboardType(xw->keyboard.type)));
3770     if (xw->keyboard.type == type) {
3771         xw->keyboard.type = keyboardIsDefault;
3772     } else {
3773         xw->keyboard.type = type;
3774     }
3775
3776     if (save != xw->keyboard.type) {
3777         update_keyboard_type();
3778     }
3779 }
3780
3781 void
3782 init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
3783 {
3784     static Bool wasSet = False;
3785
3786     TRACE(("init_keyboard_type(%s, %s) currently %s\n",
3787            visibleKeyboardType(type),
3788            BtoS(set),
3789            visibleKeyboardType(xw->keyboard.type)));
3790     if (set) {
3791         if (wasSet) {
3792             fprintf(stderr, "Conflicting keyboard type option (%u/%u)\n",
3793                     xw->keyboard.type, type);
3794         }
3795         xw->keyboard.type = type;
3796         wasSet = True;
3797         update_keyboard_type();
3798     }
3799 }
3800
3801 /*
3802  * If the keyboardType resource is set, use that, overriding the individual
3803  * boolean resources for different keyboard types.
3804  */
3805 void
3806 decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
3807 {
3808 #define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
3809 #define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
3810     static struct {
3811         const char *name;
3812         xtermKeyboardType type;
3813         unsigned offset;
3814     } table[] = {
3815 #if OPT_HP_FUNC_KEYS
3816         DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
3817 #endif
3818 #if OPT_SCO_FUNC_KEYS
3819             DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
3820 #endif
3821 #if OPT_SUN_FUNC_KEYS
3822             DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
3823 #endif
3824 #if OPT_SUNPC_KBD
3825             DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
3826 #endif
3827 #if OPT_TCAP_FKEYS
3828             DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
3829 #endif
3830     };
3831     Cardinal n;
3832
3833     TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
3834     if (!x_strcasecmp(rp->keyboardType, "unknown")) {
3835         /*
3836          * Let the individual resources comprise the keyboard-type.
3837          */
3838         for (n = 0; n < XtNumber(table); ++n)
3839             init_keyboard_type(xw, table[n].type, FLAG(n));
3840     } else if (!x_strcasecmp(rp->keyboardType, "default")) {
3841         /*
3842          * Set the keyboard-type to the Sun/PC type, allowing modified
3843          * function keys, etc.
3844          */
3845         for (n = 0; n < XtNumber(table); ++n)
3846             init_keyboard_type(xw, table[n].type, False);
3847     } else {
3848         Bool found = False;
3849
3850         /*
3851          * Choose an individual keyboard type.
3852          */
3853         for (n = 0; n < XtNumber(table); ++n) {
3854             if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
3855                 FLAG(n) = True;
3856                 found = True;
3857             } else {
3858                 FLAG(n) = False;
3859             }
3860             init_keyboard_type(xw, table[n].type, FLAG(n));
3861         }
3862         if (!found) {
3863             fprintf(stderr,
3864                     "KeyboardType resource \"%s\" not found\n",
3865                     rp->keyboardType);
3866         }
3867     }
3868 #undef DATA
3869 #undef FLAG
3870 }
3871
3872 #if OPT_WIDE_CHARS
3873 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
3874 /*
3875  * If xterm is running in a UTF-8 locale, it is still possible to encounter
3876  * old runtime configurations which yield incomplete or inaccurate data.
3877  */
3878 static Bool
3879 systemWcwidthOk(int samplesize, int samplepass)
3880 {
3881     wchar_t n;
3882     int oops = 0;
3883
3884     for (n = 21; n <= 25; ++n) {
3885         int code = (int) dec2ucs((unsigned) n);
3886         int system_code = wcwidth(code);
3887         int intern_code = mk_wcwidth(code);
3888
3889         /*
3890          * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
3891          * 0x2500) and most of the geometric shapes (a few are excluded, just
3892          * to make it more difficult to use).  Do a sanity check to avoid using
3893          * it.
3894          */
3895         if ((system_code < 0 && intern_code >= 1)
3896             || (system_code >= 0 && intern_code != system_code)) {
3897             TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
3898             oops += (samplepass + 1);
3899             break;
3900         }
3901     }
3902
3903     for (n = 0; n < (wchar_t) samplesize; ++n) {
3904         int system_code = wcwidth(n);
3905         int intern_code = mk_wcwidth(n);
3906
3907         /*
3908          * Since mk_wcwidth() is designed to check for nonspacing characters,
3909          * and has rough range-checks for double-width characters, it will
3910          * generally not detect cases where a code has not been assigned.
3911          *
3912          * Some experimentation with GNU libc suggests that up to 1/4 of the
3913          * codes would differ, simply because the runtime library would have a
3914          * table listing the unassigned codes, and return -1 for those.  If
3915          * mk_wcwidth() has no information about a code, it returns 1.  On the
3916          * other hand, if the runtime returns a positive number, the two should
3917          * agree.
3918          *
3919          * The "up to" is measured for 4k, 8k, 16k of data.  With only 1k, the
3920          * number of differences was only 77.  However, that is only one
3921          * system, and this is only a sanity check to avoid using broken
3922          * libraries.
3923          */
3924         if ((system_code < 0 && intern_code >= 1)
3925             || (system_code >= 0 && intern_code != system_code)) {
3926             ++oops;
3927         }
3928     }
3929     TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
3930            oops, samplesize, samplepass));
3931     return (oops <= samplepass);
3932 }
3933 #endif /* HAVE_WCWIDTH */
3934
3935 void
3936 decode_wcwidth(XtermWidget xw)
3937 {
3938     int mode = ((xw->misc.cjk_width ? 2 : 0)
3939                 + (xw->misc.mk_width ? 1 : 0)
3940                 + 1);
3941
3942     switch (mode) {
3943     default:
3944 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
3945         if (xtermEnvUTF8() &&
3946             systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {
3947             my_wcwidth = wcwidth;
3948             TRACE(("using system wcwidth() function\n"));
3949             break;
3950         }
3951         /* FALLTHRU */
3952 #endif
3953     case 2:
3954         my_wcwidth = &mk_wcwidth;
3955         TRACE(("using MK wcwidth() function\n"));
3956         break;
3957     case 3:
3958     case 4:
3959         my_wcwidth = &mk_wcwidth_cjk;
3960         TRACE(("using MK-CJK wcwidth() function\n"));
3961         break;
3962     }
3963
3964     for (first_widechar = 128; first_widechar < 4500; ++first_widechar) {
3965         if (my_wcwidth((int) first_widechar) > 1) {
3966             TRACE(("first_widechar %#x\n", first_widechar));
3967             break;
3968         }
3969     }
3970 }
3971 #endif