*** empty log message ***
[dana/urxvt.git] / src / screen.C
1 /*--------------------------------*-C-*--------------------------------------*
2  * File:        screen.C
3  *---------------------------------------------------------------------------*
4  *
5  * Copyright (c) 1997-2001 Geoff Wing <gcw@pobox.com>
6  * Copyright (c) 2003-2004 Marc Lehmann <pcg@goof.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *--------------------------------------------------------------------------*/
22
23 /*
24  * This file handles _all_ screen updates and selections
25  */
26
27 #include "../config.h"          /* NECESSARY */
28 #define INTERN_SCREEN
29 #include "rxvt.h"               /* NECESSARY */
30
31 #include <X11/Xmd.h>            /* get the typedef for CARD32 */
32
33 #include <inttypes.h>
34 #include <wchar.h>
35
36 #include "salloc.C" // HACK, should be a seperate compile!
37
38 inline void fill_text (text_t *start, text_t value, int len)
39 {
40   while (len--)
41     *start++ = value;
42 }
43
44 /* ------------------------------------------------------------------------- */
45 #define PROP_SIZE               16384
46 #define TABSIZE                 8       /* default tab size */
47
48 /* ------------------------------------------------------------------------- *
49  *             GENERAL SCREEN AND SELECTION UPDATE ROUTINES                  *
50  * ------------------------------------------------------------------------- */
51 #define ZERO_SCROLLBACK()                                              \
52     if (options & Opt_scrollTtyOutput)                                 \
53         TermWin.view_start = 0
54 #define CLEAR_SELECTION()                                              \
55     selection.beg.row = selection.beg.col                              \
56         = selection.end.row = selection.end.col = 0
57 #define CLEAR_ALL_SELECTION()                                          \
58     selection.beg.row = selection.beg.col                              \
59         = selection.mark.row = selection.mark.col                      \
60         = selection.end.row = selection.end.col = 0
61
62 #define ROW_AND_COL_IS_AFTER(A, B, C, D)                               \
63     (((A) > (C)) || (((A) == (C)) && ((B) > (D))))
64 #define ROW_AND_COL_IS_BEFORE(A, B, C, D)                              \
65     (((A) < (C)) || (((A) == (C)) && ((B) < (D))))
66 #define ROW_AND_COL_IN_ROW_AFTER(A, B, C, D)                           \
67     (((A) == (C)) && ((B) > (D)))
68 #define ROW_AND_COL_IN_ROW_AT_OR_AFTER(A, B, C, D)                     \
69     (((A) == (C)) && ((B) >= (D)))
70 #define ROW_AND_COL_IN_ROW_BEFORE(A, B, C, D)                          \
71     (((A) == (C)) && ((B) < (D)))
72 #define ROW_AND_COL_IN_ROW_AT_OR_BEFORE(A, B, C, D)                    \
73     (((A) == (C)) && ((B) <= (D)))
74
75 /* these must be row_col_t */
76 #define ROWCOL_IS_AFTER(X, Y)                                          \
77     ROW_AND_COL_IS_AFTER ((X).row, (X).col, (Y).row, (Y).col)
78 #define ROWCOL_IS_BEFORE(X, Y)                                         \
79     ROW_AND_COL_IS_BEFORE ((X).row, (X).col, (Y).row, (Y).col)
80 #define ROWCOL_IN_ROW_AFTER(X, Y)                                      \
81     ROW_AND_COL_IN_ROW_AFTER ((X).row, (X).col, (Y).row, (Y).col)
82 #define ROWCOL_IN_ROW_BEFORE(X, Y)                                     \
83     ROW_AND_COL_IN_ROW_BEFORE ((X).row, (X).col, (Y).row, (Y).col)
84 #define ROWCOL_IN_ROW_AT_OR_AFTER(X, Y)                                \
85     ROW_AND_COL_IN_ROW_AT_OR_AFTER ((X).row, (X).col, (Y).row, (Y).col)
86 #define ROWCOL_IN_ROW_AT_OR_BEFORE(X, Y)                               \
87     ROW_AND_COL_IN_ROW_AT_OR_BEFORE ((X).row, (X).col, (Y).row, (Y).col)
88
89 /*
90  * CLEAR_ROWS : clear <num> rows starting from row <row>
91  * CLEAR_CHARS: clear <num> chars starting from pixel position <x,y>
92  * ERASE_ROWS : set <num> rows starting from row <row> to the foreground colour
93  */
94 #define drawBuffer      TermWin.vt
95
96 #define CLEAR_ROWS(row, num)                                           \
97     if (TermWin.mapped)                                                \
98         XClearArea (display->display, drawBuffer, 0,                   \
99                     Row2Pixel (row), (unsigned int)TermWin.width,      \
100                     (unsigned int)Height2Pixel (num), False)
101
102 #define CLEAR_CHARS(x, y, num)                                         \
103     if (TermWin.mapped)                                                \
104         XClearArea (display->display, drawBuffer, x, y,                \
105                     (unsigned int)Width2Pixel (num),                   \
106                     (unsigned int)Height2Pixel (1), False)
107
108 #define ERASE_ROWS(row, num)                                           \
109     XFillRectangle (display->display, drawBuffer, TermWin.gc,          \
110                     0, Row2Pixel (row),                                \
111                     (unsigned int)TermWin.width,                       \
112                     (unsigned int)Height2Pixel (num))
113
114 /* ------------------------------------------------------------------------- *
115  *                        SCREEN `COMMON' ROUTINES                           *
116  * ------------------------------------------------------------------------- */
117 /* Fill part/all of a line with blanks. */
118 void
119 rxvt_term::scr_blank_line (text_t *et, rend_t *er, unsigned int width, rend_t efs)
120 {
121   efs &= ~RS_baseattrMask;
122   efs = SET_FONT (efs, TermWin.ascii_map [' ' - 0x20]);
123
124   while (width--)
125     {
126       *et++ = ' ';
127       *er++ = efs;
128     }
129 }
130
131 /* ------------------------------------------------------------------------- */
132 /* Fill a full line with blanks - make sure it is allocated first */
133 void
134 rxvt_term::scr_blank_screen_mem (text_t **tp, rend_t **rp, unsigned int row, rend_t efs)
135 {
136 #ifdef DEBUG_STRICT
137   assert ((tp[row] && rp[row]) || (tp[row] == NULL && rp[row] == NULL));
138 #endif
139   if (tp[row] == NULL)
140     {
141       tp[row] = (text_t *)talloc->alloc ();
142       rp[row] = (rend_t *)ralloc->alloc ();
143     }
144
145   scr_blank_line (tp[row], rp[row], TermWin.ncol, efs);
146 }
147
148 /* ------------------------------------------------------------------------- *
149  *                          SCREEN INITIALISATION                            *
150  * ------------------------------------------------------------------------- */
151 void
152 rxvt_term::scr_reset ()
153 {
154   unsigned int ncol, nrow, total_rows, prev_total_rows;
155   unsigned int p, q;
156   int k;
157   rend_t setrstyle;
158
159 #if ENABLE_OVERLAY
160   scr_overlay_off ();
161 #endif
162
163   TermWin.view_start = 0;
164   num_scr = 0;
165
166   if (TermWin.ncol == 0)
167     TermWin.ncol = 80;
168
169   if (TermWin.nrow == 0)
170     TermWin.nrow = 24;
171
172   ncol = TermWin.ncol;
173   nrow = TermWin.nrow;
174
175   if (ncol == prev_ncol && nrow == prev_nrow)
176     return;
177
178   // we need at least two lines for wrapping to work correctly
179   if (nrow + TermWin.saveLines < 2)
180     {
181       TermWin.saveLines++;
182       prev_nrow--;
183       TermWin.nscrolled++;
184     }
185
186   want_refresh = 1;
187
188   prev_total_rows = prev_nrow + TermWin.saveLines;
189   total_rows = nrow + TermWin.saveLines;
190
191   screen.tscroll = 0;
192   screen.bscroll = nrow - 1;
193
194   if (!talloc)
195     {
196       talloc = new rxvt_salloc (ncol * sizeof (text_t));
197       ralloc = new rxvt_salloc (ncol * sizeof (rend_t));
198     }
199
200   if (!screen.text)
201     {
202       /*
203        * first time called so just malloc everything: don't rely on realloc
204        * Note: this is still needed so that all the scrollback lines are NULL
205        */
206       screen.text = (text_t **)rxvt_calloc (total_rows, sizeof (text_t *));
207       buf_text = (text_t **)rxvt_calloc (total_rows, sizeof (text_t *));
208       drawn_text = (text_t **)rxvt_calloc (nrow, sizeof (text_t *));
209       swap.text = (text_t **)rxvt_calloc (nrow, sizeof (text_t *));
210
211       screen.tlen = (int16_t *)rxvt_calloc (total_rows, sizeof (int16_t));
212       swap.tlen = (int16_t *)rxvt_calloc (nrow, sizeof (int16_t));
213
214       screen.rend = (rend_t **)rxvt_calloc (total_rows, sizeof (rend_t *));
215       buf_rend = (rend_t **)rxvt_calloc (total_rows, sizeof (rend_t *));
216       drawn_rend = (rend_t **)rxvt_calloc (nrow, sizeof (rend_t *));
217       swap.rend = (rend_t **)rxvt_calloc (nrow, sizeof (rend_t *));
218
219       for (p = 0; p < nrow; p++)
220         {
221           q = p + TermWin.saveLines;
222           scr_blank_screen_mem (screen.text, screen.rend, q, DEFAULT_RSTYLE);
223           scr_blank_screen_mem (swap.text, swap.rend, p, DEFAULT_RSTYLE);
224           screen.tlen[q] = swap.tlen[p] = 0;
225           scr_blank_screen_mem (drawn_text, drawn_rend, p, DEFAULT_RSTYLE);
226         }
227
228       memset (charsets, 'B', sizeof (charsets));
229       TermWin.nscrolled = 0;       /* no saved lines */
230       rstyle = DEFAULT_RSTYLE;
231       screen.flags = Screen_DefaultFlags;
232       screen.cur.row = screen.cur.col = 0;
233       screen.charset = 0;
234       current_screen = PRIMARY;
235       scr_cursor (SAVE);
236
237 #if NSCREENS
238       swap.flags = Screen_DefaultFlags;
239       swap.cur.row = swap.cur.col = 0;
240       swap.charset = 0;
241       current_screen = SECONDARY;
242       scr_cursor (SAVE);
243       current_screen = PRIMARY;
244 #endif
245
246       selection.text = NULL;
247       selection.len = 0;
248       selection.op = SELECTION_CLEAR;
249       selection.screen = PRIMARY;
250       selection.clicks = 0;
251       CLEAR_ALL_SELECTION ();
252       rvideo = 0;
253     }
254   else
255     {
256       /*
257        * add or delete rows as appropriate
258        */
259       setrstyle = DEFAULT_RSTYLE;
260
261       if (nrow < prev_nrow)
262         {
263           /* delete rows */
264           k = min (TermWin.nscrolled, prev_nrow - nrow);
265           scr_scroll_text (0, (int)prev_nrow - 1, k, 1);
266
267           for (p = nrow; p < prev_nrow; p++)
268             {
269               q = p + TermWin.saveLines;
270
271               if (screen.text[q])
272                 {
273 #ifdef DEBUG_STRICT
274                   assert (screen.rend[q]);
275 #endif
276                   talloc->free (screen.text[q]);
277                   ralloc->free (screen.rend[q]);
278                 }
279
280               if (swap.text[p])
281                 {
282 #ifdef DEBUG_STRICT
283                   assert (swap.rend[p]);
284 #endif
285                   talloc->free (swap.text[p]);
286                   ralloc->free (swap.rend[p]);
287                 }
288
289 #ifdef DEBUG_STRICT
290               assert (drawn_text[p] && drawn_rend[p]);
291 #endif
292               talloc->free (drawn_text[p]);
293               ralloc->free (drawn_rend[p]);
294             }
295
296           /* we have fewer rows so fix up cursor position */
297           MIN_IT (screen.cur.row, (int32_t)nrow - 1);
298           MIN_IT (swap.cur.row, (int32_t)nrow - 1);
299
300           scr_reset_realloc (); /* realloc _last_ */
301         }
302       else if (nrow > prev_nrow)
303         {
304           /* add rows */
305           scr_reset_realloc (); /* realloc _first_ */
306
307           TermWin.ncol = prev_ncol; // save b/c scr_blank_screen_mem uses this
308
309           k = min (TermWin.nscrolled, nrow - prev_nrow);
310
311           for (p = prev_total_rows; p < total_rows; p++)
312             {
313               screen.tlen[p] = 0;
314               screen.text[p] = NULL;
315               screen.rend[p] = NULL;
316             }
317
318           for (p = prev_total_rows; p < total_rows - k; p++)
319             scr_blank_screen_mem (screen.text, screen.rend, p, setrstyle);
320
321           for (p = prev_nrow; p < nrow; p++)
322             {
323               swap.tlen[p] = 0;
324               swap.text[p] = NULL;
325               swap.rend[p] = NULL;
326               drawn_text[p] = NULL;
327               drawn_rend[p] = NULL;
328               scr_blank_screen_mem (swap.text,  swap.rend,  p, setrstyle);
329               scr_blank_screen_mem (drawn_text, drawn_rend, p, setrstyle);
330             }
331
332           if (k > 0)
333             {
334               scr_scroll_text (0, (int)nrow - 1, -k, 1);
335               screen.cur.row += k;
336               screen.s_cur.row += k;
337               TermWin.nscrolled -= k;
338             }
339 #ifdef DEBUG_STRICT
340           assert (screen.cur.row < TermWin.nrow);
341           assert (swap.cur.row < TermWin.nrow);
342 #else                           /* drive with your eyes closed */
343
344           MIN_IT (screen.cur.row, nrow - 1);
345           MIN_IT (swap.cur.row, nrow - 1);
346 #endif
347           TermWin.ncol =  ncol; // save b/c scr_blank_screen_mem uses this
348         }
349
350       /* resize columns */
351       if (ncol != prev_ncol)
352         {
353           rxvt_salloc *ta = new rxvt_salloc (ncol * sizeof (text_t));
354           rxvt_salloc *ra = new rxvt_salloc (ncol * sizeof (rend_t));
355
356           for (p = 0; p < total_rows; p++)
357             {
358               if (screen.text[p])
359                 {
360                   screen.text[p] = (text_t *)ta->alloc (screen.text[p], prev_ncol * sizeof (text_t));
361                   screen.rend[p] = (rend_t *)ra->alloc (screen.rend[p], prev_ncol * sizeof (rend_t));
362
363                   MIN_IT (screen.tlen[p], (int16_t)ncol);
364
365                   if (ncol > prev_ncol)
366                     scr_blank_line (&screen.text[p][prev_ncol],
367                                     &screen.rend[p][prev_ncol],
368                                     ncol - prev_ncol, setrstyle);
369                 }
370             }
371
372           for (p = 0; p < nrow; p++)
373             {
374               drawn_text[p] = (text_t *)ta->alloc (drawn_text[p], prev_ncol * sizeof (text_t));
375               drawn_rend[p] = (rend_t *)ra->alloc (drawn_rend[p], prev_ncol * sizeof (rend_t));
376
377               if (ncol > prev_ncol)
378                 scr_blank_line (&drawn_text[p][prev_ncol],
379                                 &drawn_rend[p][prev_ncol],
380                                 ncol - prev_ncol, setrstyle);
381
382               if (swap.text[p])
383                 {
384                   swap.text[p] = (text_t *)ta->alloc (swap.text[p], prev_ncol * sizeof (text_t));
385                   swap.rend[p] = (rend_t *)ra->alloc (swap.rend[p], prev_ncol * sizeof (rend_t));
386
387                   MIN_IT (swap.tlen[p], (int16_t)ncol);
388
389                   if (ncol > prev_ncol)
390                     scr_blank_line (&swap.text[p][prev_ncol],
391                                     &swap.rend[p][prev_ncol],
392                                     ncol - prev_ncol, setrstyle);
393                 }
394
395             }
396
397           MIN_IT (screen.cur.col, (int16_t)ncol - 1);
398           MIN_IT (swap.cur.col, (int16_t)ncol - 1);
399
400           delete talloc; talloc = ta;
401           delete ralloc; ralloc = ra;
402         }
403
404       if (tabs)
405         free (tabs);
406     }
407
408   prev_nrow = nrow;
409   prev_ncol = ncol;
410
411   tabs = (char *)rxvt_malloc (ncol * sizeof (char));
412
413   for (p = 0; p < ncol; p++)
414     tabs[p] = (p % TABSIZE == 0) ? 1 : 0;
415
416   tt_winch ();
417 }
418
419 void
420 rxvt_term::scr_reset_realloc ()
421 {
422   uint16_t total_rows, nrow;
423
424   nrow = TermWin.nrow;
425   total_rows = nrow + TermWin.saveLines;
426   /* *INDENT-OFF* */
427   screen.text = (text_t **)rxvt_realloc (screen.text, total_rows * sizeof (text_t *));
428   buf_text    = (text_t **)rxvt_realloc (buf_text   , total_rows * sizeof (text_t *));
429   drawn_text  = (text_t **)rxvt_realloc (drawn_text , nrow       * sizeof (text_t *));
430   swap.text   = (text_t **)rxvt_realloc (swap.text  , nrow       * sizeof (text_t *));
431
432   screen.tlen = (int16_t *)rxvt_realloc (screen.tlen, total_rows * sizeof (int16_t));
433   swap.tlen   = (int16_t *)rxvt_realloc (swap.tlen  , total_rows * sizeof (int16_t));
434
435   screen.rend = (rend_t **)rxvt_realloc (screen.rend, total_rows * sizeof (rend_t *));
436   buf_rend    = (rend_t **)rxvt_realloc (buf_rend   , total_rows * sizeof (rend_t *));
437   drawn_rend  = (rend_t **)rxvt_realloc (drawn_rend , nrow       * sizeof (rend_t *));
438   swap.rend   = (rend_t **)rxvt_realloc (swap.rend  , nrow       * sizeof (rend_t *));
439   /* *INDENT-ON* */
440 }
441
442 /* ------------------------------------------------------------------------- */
443 /*
444  * Free everything.  That way malloc debugging can find leakage.
445  */
446 void
447 rxvt_term::scr_release ()
448 {
449   uint16_t total_rows;
450   int i;
451
452   total_rows = TermWin.nrow + TermWin.saveLines;
453
454   delete talloc; talloc = 0;
455   delete ralloc; ralloc = 0;
456
457   free (screen.text);
458   free (screen.tlen);
459   free (screen.rend);
460   free (drawn_text);
461   free (drawn_rend);
462   free (swap.text);
463   free (swap.tlen);
464   free (swap.rend);
465   free (buf_text);
466   free (buf_rend);
467   free (tabs);
468
469   /* NULL these so if anything tries to use them, we'll know about it */
470   screen.text = drawn_text = swap.text = NULL;
471   screen.rend = drawn_rend = swap.rend = NULL;
472   screen.tlen = swap.tlen = NULL;
473   buf_text = NULL;
474   buf_rend = NULL;
475   tabs = NULL;
476 }
477
478 /* ------------------------------------------------------------------------- */
479 /*
480  * Hard reset
481  */
482 void
483 rxvt_term::scr_poweron ()
484 {
485   scr_release ();
486   prev_nrow = prev_ncol = 0;
487   scr_reset ();
488
489   scr_clear (true);
490   scr_refresh (SLOW_REFRESH);
491 }
492
493 /* ------------------------------------------------------------------------- *
494  *                         PROCESS SCREEN COMMANDS                           *
495  * ------------------------------------------------------------------------- */
496 /*
497  * Save and Restore cursor
498  * XTERM_SEQ: Save cursor   : ESC 7
499  * XTERM_SEQ: Restore cursor: ESC 8
500  */
501 void
502 rxvt_term::scr_cursor (int mode)
503 {
504   screen_t *s;
505
506 #if NSCREENS && !defined(NO_SECONDARY_SCREEN_CURSOR)
507   if (current_screen == SECONDARY)
508     s = &swap;
509   else
510 #endif
511     s = &screen;
512
513   switch (mode)
514     {
515       case SAVE:
516         s->s_cur.row = s->cur.row;
517         s->s_cur.col = s->cur.col;
518         s->s_rstyle = rstyle;
519         s->s_charset = s->charset;
520         s->s_charset_char = charsets[s->charset];
521         break;
522
523       case RESTORE:
524         want_refresh = 1;
525         s->cur.row = s->s_cur.row;
526         s->cur.col = s->s_cur.col;
527         s->flags &= ~Screen_WrapNext;
528         rstyle = s->s_rstyle;
529         s->charset = s->s_charset;
530         charsets[s->charset] = s->s_charset_char;
531         set_font_style ();
532         break;
533     }
534
535   /* boundary check in case screen size changed between SAVE and RESTORE */
536   MIN_IT (s->cur.row, TermWin.nrow - 1);
537   MIN_IT (s->cur.col, TermWin.ncol - 1);
538 #ifdef DEBUG_STRICT
539   assert (s->cur.row >= 0);
540   assert (s->cur.col >= 0);
541 #else                           /* drive with your eyes closed */
542   MAX_IT (s->cur.row, 0);
543   MAX_IT (s->cur.col, 0);
544 #endif
545 }
546
547 /* ------------------------------------------------------------------------- */
548 /*
549  * Swap between primary and secondary screens
550  * XTERM_SEQ: Primary screen  : ESC [ ? 4 7 h
551  * XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
552  */
553 int
554 rxvt_term::scr_change_screen (int scrn)
555 {
556   int i;
557 #if NSCREENS
558   int offset;
559 #endif
560
561   want_refresh = 1;
562
563   TermWin.view_start = 0;
564
565   if (current_screen == scrn)
566     return scrn;
567
568   selection_check (2);        /* check for boundary cross */
569
570   SWAP_IT (current_screen, scrn, int);
571 #if NSCREENS
572   if (options & Opt_secondaryScreen)
573     {
574       num_scr = 0;
575       offset = TermWin.saveLines;
576       for (i = prev_nrow; i--;)
577         {
578           SWAP_IT (screen.text[i + offset], swap.text[i], text_t *);
579           SWAP_IT (screen.tlen[i + offset], swap.tlen[i], int16_t);
580           SWAP_IT (screen.rend[i + offset], swap.rend[i], rend_t *);
581         }
582       SWAP_IT (screen.cur.row, swap.cur.row, int16_t);
583       SWAP_IT (screen.cur.col, swap.cur.col, int16_t);
584 # ifdef DEBUG_STRICT
585       assert ((screen.cur.row >= 0) && (screen.cur.row < prev_nrow));
586       assert ((screen.cur.col >= 0) && (screen.cur.col < prev_ncol));
587 # else                          /* drive with your eyes closed */
588       MAX_IT (screen.cur.row, 0);
589       MIN_IT (screen.cur.row, (int32_t)prev_nrow - 1);
590       MAX_IT (screen.cur.col, 0);
591       MIN_IT (screen.cur.col, (int32_t)prev_ncol - 1);
592 # endif
593       SWAP_IT (screen.charset, swap.charset, int16_t);
594       SWAP_IT (screen.flags, swap.flags, int);
595       screen.flags |= Screen_VisibleCursor;
596       swap.flags |= Screen_VisibleCursor;
597     }
598   else
599 #endif
600     if (options & Opt_secondaryScroll)
601       //if (current_screen == PRIMARY)
602         scr_scroll_text (0, (prev_nrow - 1), prev_nrow, 0);
603   return scrn;
604 }
605
606 // clear WrapNext indicator, solidifying position on next line
607 void
608 rxvt_term::scr_do_wrap ()
609 {
610   if (!(screen.flags & Screen_WrapNext))
611     return;
612
613   screen.flags &= ~Screen_WrapNext;
614
615   screen.cur.col = 0;
616
617   if (screen.cur.row == screen.bscroll)
618     scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
619   else if (screen.cur.row < TermWin.nrow - 1)
620     screen.cur.row++;
621 }
622
623 /* ------------------------------------------------------------------------- */
624 /*
625  * Change the colour for following text
626  */
627 void
628 rxvt_term::scr_color (unsigned int color, int fgbg)
629 {
630   color &= RS_fgMask;
631   if (fgbg == Color_fg)
632     rstyle = SET_FGCOLOR (rstyle, color);
633   else
634     rstyle = SET_BGCOLOR (rstyle, color);
635 }
636
637 /* ------------------------------------------------------------------------- */
638 /*
639  * Change the rendition style for following text
640  */
641 void
642 rxvt_term::scr_rendition (int set, int style)
643   {
644     if (set)
645       rstyle |= style;
646     else if (style == ~RS_None)
647       rstyle = DEFAULT_RSTYLE;
648     else
649       rstyle &= ~style;
650   }
651
652 /* ------------------------------------------------------------------------- */
653 /*
654  * Scroll text between <row1> and <row2> inclusive, by <count> lines
655  * count positive ==> scroll up
656  * count negative ==> scroll down
657  * spec == 0 for normal routines
658  */
659 int
660 rxvt_term::scr_scroll_text (int row1, int row2, int count, int spec)
661 {
662   int i, j;
663   long nscrolled;
664
665   if (count == 0 || (row1 > row2))
666     return 0;
667
668   want_refresh = 1;
669
670   if (row1 == 0 && count > 0
671       && (current_screen == PRIMARY || options & Opt_secondaryScroll))
672     {
673       nscrolled = (long)TermWin.nscrolled + (long)count;
674
675       if (nscrolled > (long)TermWin.saveLines)
676         TermWin.nscrolled = TermWin.saveLines;
677       else
678         TermWin.nscrolled = (uint16_t)nscrolled;
679
680       if ((options & Opt_scrollWithBuffer)
681           && TermWin.view_start != 0
682           && TermWin.view_start != TermWin.saveLines)
683         scr_page (UP, count);
684     }
685   else if (!spec)
686     row1 += TermWin.saveLines;
687
688   row2 += TermWin.saveLines;
689
690   if (selection.op && current_screen == selection.screen)
691     {
692       i = selection.beg.row + TermWin.saveLines;
693       j = selection.end.row + TermWin.saveLines;
694       if ((i < row1 && j > row1)
695           || (i < row2 && j > row2)
696           || (i - count < row1 && i >= row1)
697           || (i - count > row2 && i <= row2)
698           || (j - count < row1 && j >= row1)
699           || (j - count > row2 && j <= row2))
700         {
701           CLEAR_ALL_SELECTION ();
702           selection.op = SELECTION_CLEAR;  /* XXX: too aggressive? */
703         }
704       else if (j >= row1 && j <= row2)
705         {
706           /* move selected region too */
707           selection.beg.row -= count;
708           selection.end.row -= count;
709           selection.mark.row -= count;
710         }
711     }
712
713   selection_check (0);        /* _after_ TermWin.nscrolled update */
714
715   num_scr += count;
716   j = count;
717
718   if (count < 0)
719     count = -count;
720
721   i = row2 - row1 + 1;
722   MIN_IT (count, i);
723
724   if (j > 0)
725     {
726       /* A: scroll up */
727
728       /* A1: Copy lines that will get clobbered by the rotation */
729       memcpy (buf_text, screen.text + row1, count * sizeof (text_t *));
730       memcpy (buf_rend, screen.rend + row1, count * sizeof (rend_t *));
731
732       /* A2: Rotate lines */
733       i = row2 - row1 - count + 1;
734       memmove (screen.tlen + row1, screen.tlen + row1 + count, i * sizeof (int16_t));
735       memmove (screen.text + row1, screen.text + row1 + count, i * sizeof (text_t *));
736       memmove (screen.rend + row1, screen.rend + row1 + count, i * sizeof (rend_t *));
737
738       j = row2 - count + 1, i = count;
739     }
740   else /* if (j < 0) */
741     {
742       /* B: scroll down */
743
744       /* B1: Copy lines that will get clobbered by the rotation */
745       for (i = 0, j = row2; i < count; i++, j--)
746         {
747           buf_text[i] = screen.text[j];
748           buf_rend[i] = screen.rend[j];
749         }
750
751       /* B2: Rotate lines */
752       for (j = row2, i = j - count; i >= row1; i--, j--)
753         {
754           screen.tlen[j] = screen.tlen[i];
755           screen.text[j] = screen.text[i];
756           screen.rend[j] = screen.rend[i];
757         }
758
759       j = row1, i = count;
760       count = -count;
761     }
762
763   /* C: Resurrect lines */
764   memset (screen.tlen + j, 0, i * sizeof (int16_t));
765   memcpy (screen.text + j, buf_text, i * sizeof (text_t *));
766   memcpy (screen.rend + j, buf_rend, i * sizeof (text_t *));
767   if (!spec) /* line length may not equal TermWin.ncol */
768     for (; i--; j++)
769       scr_blank_screen_mem (screen.text, screen.rend, (unsigned int)j, rstyle);
770
771   return count;
772 }
773
774 /* ------------------------------------------------------------------------- */
775 /*
776  * Add text given in <str> of length <len> to screen struct
777  */
778 void
779 rxvt_term::scr_add_lines (const unicode_t *str, int nlines, int len)
780 {
781   unsigned char checksel;
782   unicode_t c;
783   int i, row, last_col;
784   text_t *stp;
785   rend_t *srp;
786
787   if (len <= 0)               /* sanity */
788     return;
789
790   want_refresh = 1;
791   ZERO_SCROLLBACK ();
792   last_col = TermWin.ncol;
793
794   if (nlines > 0)
795     {
796       nlines += screen.cur.row - screen.bscroll;
797       if ((nlines > 0)
798           && (screen.tscroll == 0)
799           && (screen.bscroll == (TermWin.nrow - 1)))
800         {
801           /* _at least_ this many lines need to be scrolled */
802           scr_scroll_text (screen.tscroll, screen.bscroll, nlines, 0);
803           screen.cur.row -= nlines;
804         }
805     }
806
807 #ifdef DEBUG_STRICT
808   assert (screen.cur.col < last_col);
809   assert ((screen.cur.row < TermWin.nrow)
810           && (screen.cur.row >= - (int32_t)TermWin.nscrolled));
811 #else                           /* drive with your eyes closed */
812   MIN_IT (screen.cur.col, last_col - 1);
813   MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
814   MAX_IT (screen.cur.row, - (int32_t)TermWin.nscrolled);
815 #endif
816   row = screen.cur.row + TermWin.saveLines;
817
818   checksel = selection.op && current_screen == selection.screen ? 1 : 0;
819
820   stp = screen.text[row];
821   srp = screen.rend[row];
822
823   while (len--)
824     {
825       c = *str++;
826
827       if (c < 0x20)
828         switch (c)
829           {
830             case C0_HT:
831               scr_tab (1);
832               continue;
833
834             case C0_LF:
835               if (screen.tlen[row] != -1)      /* XXX: think about this */
836                 MAX_IT (screen.tlen[row], screen.cur.col);
837
838               screen.flags &= ~Screen_WrapNext;
839
840               if (screen.cur.row == screen.bscroll)
841                 scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
842               else if (screen.cur.row < (TermWin.nrow - 1))
843                 row = (++screen.cur.row) + TermWin.saveLines;
844
845               stp = screen.text[row];  /* _must_ refresh */
846               srp = screen.rend[row];  /* _must_ refresh */
847               continue;
848
849             case C0_CR:
850               if (screen.tlen[row] != -1)      /* XXX: think about this */
851                 MAX_IT (screen.tlen[row], screen.cur.col);
852
853               screen.flags &= ~Screen_WrapNext;
854               screen.cur.col = 0;
855               continue;
856           }
857
858       if (checksel            /* see if we're writing within selection */
859           && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
860           && ROWCOL_IS_BEFORE (screen.cur, selection.end))
861         {
862           checksel = 0;
863           /*
864            * If we wrote anywhere in the selected area, kill the selection
865            * XXX: should we kill the mark too?  Possibly, but maybe that
866            *      should be a similar check.
867            */
868           CLEAR_SELECTION ();
869         }
870
871       if (screen.flags & Screen_WrapNext)
872         {
873           screen.tlen[row] = -1;
874
875           scr_do_wrap (); row = screen.cur.row + TermWin.saveLines;
876
877           stp = screen.text[row];  /* _must_ refresh */
878           srp = screen.rend[row];  /* _must_ refresh */
879         }
880
881       if (screen.flags & Screen_Insert)
882         scr_insdel_chars (1, INSERT);
883
884       // rely on wcwidth to tell us the character width, at least for non-latin1
885       // do wcwidth before further replacements, as wcwidth says that line-drawing
886       // characters have width -1 (DOH!) on GNU/Linux sometimes.
887       int width = c < 0x100 ? 1 : wcwidth (c);
888
889       if (charsets[screen.charset] == '0') // DEC SPECIAL
890         {
891           // vt100 special graphics and line drawing
892           static uint16_t vt100_0[32] = { // 5f .. 7e
893             0x0020, 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
894             0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
895             0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534,
896             0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7,
897           };
898
899           if (c >= 0x5f && c <= 0x7e)
900             {
901               c = vt100_0[c - 0x5f];
902               width = 1;
903             }
904         }
905
906       if (width != 0)
907         {
908 #if !UNICODE_3
909           // trim characters we can't store directly :(
910           if (c >= 0x10000)
911 # if ENABLE_COMBINING
912             c = rxvt_composite.compose (c); // map to lower 16 bits
913 # else
914             c = 0xfffd;
915 # endif
916 #endif
917           bool bold = (options & Opt_realBold) && ((rstyle & RS_Bold) != 0);
918           rend_t rend = SET_FONT (rstyle,
919                                   c > 0x7f || bold || c < 0x20
920                                      ? TermWin.fontset->find_font (c, bold)
921                                      : TermWin.ascii_map [c - 0x20]);
922
923           do
924             {
925               stp[screen.cur.col] = c;
926               srp[screen.cur.col] = rend;
927
928               if (screen.cur.col < last_col - 1)
929                 screen.cur.col++;
930               else
931                 {
932                   screen.tlen[row] = last_col;
933                   if (screen.flags & Screen_Autowrap)
934                     screen.flags |= Screen_WrapNext;
935                   break;
936                 }
937
938               c = NOCHAR;
939             }
940           while (--width > 0);
941
942           // pad with spaces when overwriting wide character with smaller one
943           for (int c = screen.cur.col; c < last_col && stp[c] == NOCHAR; c++)
944             {
945               stp[c] = ' ';
946               srp[c] = rend;
947             }
948         }
949       else if (width == 0)
950         {
951 #if ENABLE_COMBINING
952           // handle combining characters
953           // we just tag the accent on the previous on-screen character.
954           // this is arguably not correct, but also arguably not wrong.
955           // we don't handle double-width characters nicely yet.
956
957           text_t *tp;
958           rend_t *rp;
959
960           if (screen.cur.col > 0)
961             {
962               tp = stp + screen.cur.col - 1;
963               rp = srp + screen.cur.col - 1;
964             }
965           else if (screen.cur.row > 0
966                    && screen.tlen [screen.cur.row - 1 + TermWin.saveLines] == -1)
967             {
968               tp = screen.text[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
969               rp = screen.rend[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
970             }
971           else
972             continue;
973
974           // handle double-width-chars by making them look extremely ugly
975           if (*tp == NOCHAR)
976             *tp = ' '; // hack //D //TODO //--tp, --rp;
977
978           // first try to find a precomposed character
979           unicode_t n = rxvt_compose (*tp, c);
980           if (n == NOCHAR)
981             n = rxvt_composite.compose (*tp, c);
982
983           *tp = n;
984           *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
985 #endif
986         }
987     }
988
989   if (screen.tlen[row] != -1)      /* XXX: think about this */
990     MAX_IT (screen.tlen[row], screen.cur.col);
991
992 #ifdef DEBUG_STRICT
993   assert (screen.cur.row >= 0);
994 #else                           /* drive with your eyes closed */
995   MAX_IT (screen.cur.row, 0);
996 #endif
997 }
998
999 /* ------------------------------------------------------------------------- */
1000 /*
1001  * Process Backspace.  Move back the cursor back a position, wrap if have to
1002  * XTERM_SEQ: CTRL-H
1003  */
1004 void
1005 rxvt_term::scr_backspace ()
1006 {
1007   want_refresh = 1;
1008
1009   if (screen.cur.col == 0)
1010     {
1011       if (screen.cur.row > 0)
1012         {
1013 #ifdef TERMCAP_HAS_BW
1014           screen.cur.col = TermWin.ncol - 1;
1015           screen.cur.row--;
1016           return;
1017 #endif
1018         }
1019     }
1020   else if (!(screen.flags & Screen_WrapNext))
1021     scr_gotorc (0, -1, RELATIVE);
1022
1023   screen.flags &= ~Screen_WrapNext;
1024 }
1025
1026 /* ------------------------------------------------------------------------- */
1027 /*
1028  * Process Horizontal Tab
1029  * count: +ve = forward; -ve = backwards
1030  * XTERM_SEQ: CTRL-I
1031  */
1032 void
1033 rxvt_term::scr_tab (int count)
1034 {
1035   int i, x;
1036
1037   want_refresh = 1;
1038   i = x = screen.cur.col;
1039
1040   if (count == 0)
1041     return;
1042   else if (count > 0)
1043     {
1044       for (; ++i < TermWin.ncol; )
1045         if (tabs[i])
1046           {
1047             x = i;
1048             if (!--count)
1049               break;
1050           }
1051
1052       if (count)
1053         x = TermWin.ncol - 1;
1054     }
1055   else /* if (count < 0) */
1056     {
1057       for (; --i >= 0; )
1058         if (tabs[i])
1059           {
1060             x = i;
1061             if (!++count)
1062               break;
1063           }
1064
1065       if (count)
1066         x = 0;
1067     }
1068
1069   if (x != screen.cur.col)
1070     scr_gotorc (0, x, R_RELATIVE);
1071 }
1072
1073 /* ------------------------------------------------------------------------- */
1074 /*
1075  * Process DEC Back Index
1076  * XTERM_SEQ: ESC 6
1077  * Move cursor left in row.  If we're at the left boundary, shift everything
1078  * in that row right.  Clear left column.
1079  */
1080 #if ENABLE_FRILLS
1081 void
1082 rxvt_term::scr_backindex ()
1083 {
1084   if (screen.cur.col > 0)
1085     scr_gotorc (0, -1, R_RELATIVE | C_RELATIVE);
1086   else
1087     {
1088       if (screen.tlen[screen.cur.row + TermWin.saveLines] == 0)
1089         return;             /* um, yeah? */
1090
1091       scr_insdel_chars (1, INSERT);
1092     }
1093 }
1094 #endif
1095 /* ------------------------------------------------------------------------- */
1096 /*
1097  * Process DEC Forward Index
1098  * XTERM_SEQ: ESC 9
1099  * Move cursor right in row.  If we're at the right boundary, shift everything
1100  * in that row left.  Clear right column.
1101  */
1102 #if ENABLE_FRILLS
1103 void
1104 rxvt_term::scr_forwardindex ()
1105 {
1106   int             row;
1107
1108   if (screen.cur.col < TermWin.ncol - 1)
1109     scr_gotorc (0, 1, R_RELATIVE | C_RELATIVE);
1110   else
1111     {
1112       row = screen.cur.row + TermWin.saveLines;
1113
1114       if (screen.tlen[row] == 0)
1115         return;             /* um, yeah? */
1116       else if (screen.tlen[row] == -1)
1117         screen.tlen[row] = TermWin.ncol;
1118
1119       scr_gotorc (0, 0, R_RELATIVE);
1120       scr_insdel_chars (1, DELETE);
1121       scr_gotorc (0, TermWin.ncol - 1, R_RELATIVE);
1122     }
1123 }
1124 #endif
1125
1126 /* ------------------------------------------------------------------------- */
1127 /*
1128  * Goto Row/Column
1129  */
1130 void
1131 rxvt_term::scr_gotorc (int row, int col, int relative)
1132 {
1133   want_refresh = 1;
1134   ZERO_SCROLLBACK ();
1135
1136   screen.cur.col = relative & C_RELATIVE ? screen.cur.col + col : col;
1137   MAX_IT (screen.cur.col, 0);
1138   MIN_IT (screen.cur.col, (int32_t)TermWin.ncol - 1);
1139
1140   screen.flags &= ~Screen_WrapNext;
1141
1142   if (relative & R_RELATIVE)
1143     {
1144       if (row > 0)
1145         {
1146           if (screen.cur.row <= screen.bscroll
1147               && (screen.cur.row + row) > screen.bscroll)
1148             screen.cur.row = screen.bscroll;
1149           else
1150             screen.cur.row += row;
1151         }
1152       else if (row < 0)
1153         {
1154           if (screen.cur.row >= screen.tscroll
1155               && (screen.cur.row + row) < screen.tscroll)
1156             screen.cur.row = screen.tscroll;
1157           else
1158             screen.cur.row += row;
1159         }
1160     }
1161   else
1162     {
1163       if (screen.flags & Screen_Relative)
1164         {        /* relative origin mode */
1165           screen.cur.row = row + screen.tscroll;
1166           MIN_IT (screen.cur.row, screen.bscroll);
1167         }
1168       else
1169         screen.cur.row = row;
1170     }
1171
1172   MAX_IT (screen.cur.row, 0);
1173   MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1174 }
1175
1176 /* ------------------------------------------------------------------------- */
1177 /*
1178  * direction should be UP or DN
1179  */
1180 void
1181 rxvt_term::scr_index (enum page_dirn direction)
1182 {
1183   int dirn;
1184
1185   want_refresh = 1;
1186   ZERO_SCROLLBACK ();
1187
1188   dirn = ((direction == UP) ? 1 : -1);
1189
1190   screen.flags &= ~Screen_WrapNext;
1191
1192   if ((screen.cur.row == screen.bscroll && direction == UP)
1193       || (screen.cur.row == screen.tscroll && direction == DN))
1194     scr_scroll_text (screen.tscroll, screen.bscroll, dirn, 0);
1195   else
1196     screen.cur.row += dirn;
1197
1198   MAX_IT (screen.cur.row, 0);
1199   MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1200   selection_check (0);
1201 }
1202
1203 /* ------------------------------------------------------------------------- */
1204 /*
1205  * Erase part or whole of a line
1206  * XTERM_SEQ: Clear line to right: ESC [ 0 K
1207  * XTERM_SEQ: Clear line to left : ESC [ 1 K
1208  * XTERM_SEQ: Clear whole line   : ESC [ 2 K
1209  */
1210 void
1211 rxvt_term::scr_erase_line (int mode)
1212 {
1213   unsigned int row, col, num;
1214
1215   want_refresh = 1;
1216   ZERO_SCROLLBACK ();
1217
1218   selection_check (1);
1219
1220   row = TermWin.saveLines + screen.cur.row;
1221   switch (mode)
1222     {
1223       case 0:                     /* erase to end of line */
1224         col = screen.cur.col;
1225         num = TermWin.ncol - col;
1226         MIN_IT (screen.tlen[row], (int16_t)col);
1227         if (ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur)
1228             || ROWCOL_IN_ROW_AT_OR_AFTER (selection.end, screen.cur))
1229           CLEAR_SELECTION ();
1230         break;
1231       case 1:                     /* erase to beginning of line */
1232         col = 0;
1233         num = screen.cur.col + 1;
1234         if (ROWCOL_IN_ROW_AT_OR_BEFORE (selection.beg, screen.cur)
1235             || ROWCOL_IN_ROW_AT_OR_BEFORE (selection.end, screen.cur))
1236           CLEAR_SELECTION ();
1237         break;
1238       case 2:                     /* erase whole line */
1239         col = 0;
1240         num = TermWin.ncol;
1241         screen.tlen[row] = 0;
1242         if (selection.beg.row <= screen.cur.row
1243             && selection.end.row >= screen.cur.row)
1244           CLEAR_SELECTION ();
1245         break;
1246       default:
1247         return;
1248     }
1249
1250   if (screen.text[row])
1251     scr_blank_line (&screen.text[row][col], &screen.rend[row][col], num, rstyle);
1252   else
1253     scr_blank_screen_mem (screen.text, screen.rend, row, rstyle);
1254 }
1255
1256 /* ------------------------------------------------------------------------- */
1257 /*
1258  * Erase part of whole of the screen
1259  * XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
1260  * XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
1261  * XTERM_SEQ: Clear whole screen        : ESC [ 2 J
1262  */
1263 void
1264 rxvt_term::scr_erase_screen (int mode)
1265 {
1266   int num;
1267   int32_t row, row_offset;
1268   rend_t ren;
1269   XGCValues gcvalue;
1270
1271   want_refresh = 1;
1272   ZERO_SCROLLBACK ();
1273
1274   row_offset = (int32_t)TermWin.saveLines;
1275
1276   switch (mode)
1277     {
1278       case 0:                     /* erase to end of screen */
1279         selection_check (1);
1280         scr_erase_line (0);
1281         row = screen.cur.row + 1;    /* possible OOB */
1282         num = TermWin.nrow - row;
1283         break;
1284       case 1:                     /* erase to beginning of screen */
1285         selection_check (3);
1286         scr_erase_line (1);
1287         row = 0;
1288         num = screen.cur.row;
1289         break;
1290       case 2:                     /* erase whole screen */
1291         selection_check (3);
1292         row = 0;
1293         num = TermWin.nrow;
1294         break;
1295       default:
1296         return;
1297     }
1298
1299   if (selection.op && current_screen == selection.screen
1300       && ((selection.beg.row >= row && selection.beg.row <= row + num)
1301           || (selection.end.row >= row
1302               && selection.end.row <= row + num)))
1303     CLEAR_SELECTION ();
1304
1305   if (row >= TermWin.nrow) /* Out Of Bounds */
1306     return;
1307
1308   MIN_IT (num, (TermWin.nrow - row));
1309
1310   if (rstyle & (RS_RVid | RS_Uline))
1311     ren = (rend_t) ~RS_None;
1312   else if (GET_BASEBG (rstyle) == Color_bg)
1313     {
1314       ren = DEFAULT_RSTYLE;
1315       CLEAR_ROWS (row, num);
1316     }
1317   else
1318     {
1319       ren = (rstyle & (RS_fgMask | RS_bgMask));
1320       gcvalue.foreground = pix_colors[GET_BGCOLOR (rstyle)];
1321       XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1322       ERASE_ROWS (row, num);
1323       gcvalue.foreground = pix_colors[Color_fg];
1324       XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1325     }
1326
1327   for (; num--; row++)
1328     {
1329       scr_blank_screen_mem (screen.text, screen.rend,
1330                             (unsigned int) (row + row_offset), rstyle);
1331       screen.tlen[row + row_offset] = 0;
1332       scr_blank_line (drawn_text[row], drawn_rend[row],
1333                       (unsigned int)TermWin.ncol, ren);
1334     }
1335 }
1336
1337 /* ------------------------------------------------------------------------- */
1338 /*
1339  * Fill the screen with `E's
1340  * XTERM_SEQ: Screen Alignment Test: ESC # 8
1341  */
1342 void
1343 rxvt_term::scr_E ()
1344 {
1345   int             i, j, k;
1346   rend_t         *r1, fs;
1347
1348   want_refresh = 1;
1349   ZERO_SCROLLBACK ();
1350
1351   num_scr_allow = 0;
1352   selection_check (3);
1353
1354   fs = SET_FONT (rstyle, TermWin.fontset->find_font ('E'));
1355   for (k = TermWin.saveLines, i = TermWin.nrow; i--; k++)
1356     {
1357       screen.tlen[k] = TermWin.ncol;    /* make the `E's selectable */
1358       fill_text (screen.text[k], 'E', TermWin.ncol);
1359       for (r1 = screen.rend[k], j = TermWin.ncol; j--; )
1360         *r1++ = fs;
1361     }
1362 }
1363
1364 /* ------------------------------------------------------------------------- */
1365 /*
1366  * Insert/Delete <count> lines
1367  */
1368 void
1369 rxvt_term::scr_insdel_lines (int count, int insdel)
1370 {
1371   int end;
1372
1373   ZERO_SCROLLBACK ();
1374
1375   selection_check (1);
1376
1377   if (screen.cur.row > screen.bscroll)
1378     return;
1379
1380   end = screen.bscroll - screen.cur.row + 1;
1381   if (count > end)
1382     {
1383       if (insdel == DELETE)
1384         return;
1385       else if (insdel == INSERT)
1386         count = end;
1387     }
1388
1389   scr_do_wrap ();
1390
1391   scr_scroll_text (screen.cur.row, screen.bscroll, insdel * count, 0);
1392 }
1393
1394 /* ------------------------------------------------------------------------- */
1395 /*
1396  * Insert/Delete <count> characters from the current position
1397  */
1398 void
1399 rxvt_term::scr_insdel_chars (int count, int insdel)
1400 {
1401   int col, row;
1402   rend_t tr;
1403   text_t *stp;
1404   rend_t *srp;
1405   int16_t *slp;
1406
1407   want_refresh = 1;
1408   ZERO_SCROLLBACK ();
1409
1410   if (count <= 0)
1411     return;
1412
1413   scr_do_wrap ();
1414
1415   selection_check (1);
1416   MIN_IT (count, (TermWin.ncol - screen.cur.col));
1417
1418   row = screen.cur.row + TermWin.saveLines;
1419
1420   stp = screen.text[row];
1421   srp = screen.rend[row];
1422   slp = &screen.tlen[row];
1423
1424   switch (insdel)
1425     {
1426       case INSERT:
1427         for (col = TermWin.ncol - 1; (col - count) >= screen.cur.col;
1428              col--)
1429           {
1430             stp[col] = stp[col - count];
1431             srp[col] = srp[col - count];
1432           }
1433
1434         if (*slp != -1)
1435           {
1436             *slp += count;
1437             MIN_IT (*slp, TermWin.ncol);
1438           }
1439
1440         if (selection.op && current_screen == selection.screen
1441             && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1442           {
1443             if (selection.end.row != screen.cur.row
1444                 || (selection.end.col + count >= TermWin.ncol))
1445               CLEAR_SELECTION ();
1446             else
1447               {              /* shift selection */
1448                 selection.beg.col += count;
1449                 selection.mark.col += count; /* XXX: yes? */
1450                 selection.end.col += count;
1451               }
1452           }
1453
1454         scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1455                         (unsigned int)count, rstyle);
1456         break;
1457
1458       case ERASE:
1459         screen.cur.col += count;     /* don't worry if > TermWin.ncol */
1460         selection_check (1);
1461         screen.cur.col -= count;
1462         scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1463                         (unsigned int)count, rstyle);
1464         break;
1465
1466       case DELETE:
1467         tr = srp[TermWin.ncol - 1] & (RS_fgMask | RS_bgMask | RS_baseattrMask);
1468
1469         for (col = screen.cur.col; (col + count) < TermWin.ncol; col++)
1470           {
1471             stp[col] = stp[col + count];
1472             srp[col] = srp[col + count];
1473           }
1474
1475         scr_blank_line (& (stp[TermWin.ncol - count]),
1476                         & (srp[TermWin.ncol - count]),
1477                         (unsigned int)count, tr);
1478
1479         if (*slp == -1) /* break line continuation */
1480           *slp = TermWin.ncol;
1481         
1482         *slp -= count;
1483         MAX_IT (*slp, 0);
1484
1485         if (selection.op && current_screen == selection.screen
1486             && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1487           {
1488             if (selection.end.row != screen.cur.row
1489                 || (screen.cur.col >= selection.beg.col - count)
1490                 || selection.end.col >= TermWin.ncol)
1491               CLEAR_SELECTION ();
1492             else
1493               {
1494                 /* shift selection */
1495                 selection.beg.col -= count;
1496                 selection.mark.col -= count; /* XXX: yes? */
1497                 selection.end.col -= count;
1498               }
1499           }
1500
1501         break;
1502     }
1503 }
1504
1505 /* ------------------------------------------------------------------------- */
1506 /*
1507  * Set the scrolling region
1508  * XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
1509  */
1510 void
1511 rxvt_term::scr_scroll_region (int top, int bot)
1512 {
1513   MAX_IT (top, 0);
1514   MIN_IT (bot, (int)TermWin.nrow - 1);
1515
1516   if (top > bot)
1517     return;
1518
1519   screen.tscroll = top;
1520   screen.bscroll = bot;
1521   scr_gotorc (0, 0, 0);
1522 }
1523
1524 /* ------------------------------------------------------------------------- */
1525 /*
1526  * Make the cursor visible/invisible
1527  * XTERM_SEQ: Make cursor visible  : ESC [ ? 25 h
1528  * XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
1529  */
1530 void
1531 rxvt_term::scr_cursor_visible (int mode)
1532 {
1533   want_refresh = 1;
1534
1535   if (mode)
1536     screen.flags |= Screen_VisibleCursor;
1537   else
1538     screen.flags &= ~Screen_VisibleCursor;
1539 }
1540
1541 /* ------------------------------------------------------------------------- */
1542 /*
1543  * Set/unset automatic wrapping
1544  * XTERM_SEQ: Set Wraparound  : ESC [ ? 7 h
1545  * XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
1546  */
1547 void
1548 rxvt_term::scr_autowrap (int mode)
1549 {
1550   if (mode)
1551     screen.flags |= Screen_Autowrap;
1552   else
1553     screen.flags &= ~(Screen_Autowrap | Screen_WrapNext);
1554 }
1555
1556 /* ------------------------------------------------------------------------- */
1557 /*
1558  * Set/unset margin origin mode
1559  * Absolute mode: line numbers are counted relative to top margin of screen
1560  *      and the cursor can be moved outside the scrolling region.
1561  * Relative mode: line numbers are relative to top margin of scrolling region
1562  *      and the cursor cannot be moved outside.
1563  * XTERM_SEQ: Set Absolute: ESC [ ? 6 h
1564  * XTERM_SEQ: Set Relative: ESC [ ? 6 l
1565  */
1566 void
1567 rxvt_term::scr_relative_origin (int mode)
1568 {
1569   if (mode)
1570     screen.flags |= Screen_Relative;
1571   else
1572     screen.flags &= ~Screen_Relative;
1573   scr_gotorc (0, 0, 0);
1574 }
1575
1576 /* ------------------------------------------------------------------------- */
1577 /*
1578  * Set insert/replace mode
1579  * XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
1580  * XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
1581  */
1582 void
1583 rxvt_term::scr_insert_mode (int mode)
1584 {
1585   if (mode)
1586     screen.flags |= Screen_Insert;
1587   else
1588     screen.flags &= ~Screen_Insert;
1589 }
1590
1591 /* ------------------------------------------------------------------------- */
1592 /*
1593  * Set/Unset tabs
1594  * XTERM_SEQ: Set tab at current column  : ESC H
1595  * XTERM_SEQ: Clear tab at current column: ESC [ 0 g
1596  * XTERM_SEQ: Clear all tabs             : ESC [ 3 g
1597  */
1598 void
1599 rxvt_term::scr_set_tab (int mode)
1600 {
1601   if (mode < 0)
1602     memset (tabs, 0, TermWin.ncol * sizeof (char));
1603   else if (screen.cur.col < TermWin.ncol)
1604     tabs[screen.cur.col] = (mode ? 1 : 0);
1605 }
1606
1607 /* ------------------------------------------------------------------------- */
1608 /*
1609  * Set reverse/normal video
1610  * XTERM_SEQ: Reverse video: ESC [ ? 5 h
1611  * XTERM_SEQ: Normal video : ESC [ ? 5 l
1612  */
1613 void
1614 rxvt_term::scr_rvideo_mode (int mode)
1615 {
1616   XGCValues gcvalue;
1617
1618   if (rvideo != mode)
1619     {
1620       rvideo = mode;
1621       SWAP_IT (pix_colors[Color_fg], pix_colors[Color_bg], rxvt_color);
1622 #if defined(XPM_BACKGROUND)
1623       if (bgPixmap.pixmap == None)
1624 #endif
1625 #if defined(TRANSPARENT)
1626         if (! (options & Opt_transparent) || am_transparent == 0)
1627 #endif
1628           XSetWindowBackground (display->display, TermWin.vt,
1629                                pix_colors[Color_bg]);
1630
1631       gcvalue.foreground = pix_colors[Color_fg];
1632       gcvalue.background = pix_colors[Color_bg];
1633       XChangeGC (display->display, TermWin.gc, GCBackground | GCForeground,
1634                 &gcvalue);
1635       scr_clear ();
1636       scr_touch (true);
1637     }
1638 }
1639
1640 /* ------------------------------------------------------------------------- */
1641 /*
1642  * Report current cursor position
1643  * XTERM_SEQ: Report position: ESC [ 6 n
1644  */
1645 void
1646 rxvt_term::scr_report_position ()
1647 {
1648   tt_printf ("\033[%d;%dR", screen.cur.row + 1, screen.cur.col + 1);
1649 }
1650 \f
1651 /* ------------------------------------------------------------------------- *
1652  *                                  FONTS                                    *
1653  * ------------------------------------------------------------------------- */
1654
1655 /*
1656  * Set font style
1657  */
1658 void
1659 rxvt_term::set_font_style ()
1660 {
1661   switch (charsets[screen.charset])
1662     {
1663       case '0':                   /* DEC Special Character & Line Drawing Set */
1664         break;
1665       case 'A':                   /* United Kingdom (UK) */
1666         break;
1667       case 'B':                   /* United States (USASCII) */
1668         break;
1669       case '<':                   /* Multinational character set */
1670         break;
1671       case '5':                   /* Finnish character set */
1672         break;
1673       case 'C':                   /* Finnish character set */
1674         break;
1675       case 'K':                   /* German character set */
1676         break;
1677     }
1678 }
1679
1680 /* ------------------------------------------------------------------------- */
1681 /*
1682  * Choose a font
1683  * XTERM_SEQ: Invoke G0 character set: CTRL-O
1684  * XTERM_SEQ: Invoke G1 character set: CTRL-N
1685  * XTERM_SEQ: Invoke G2 character set: ESC N
1686  * XTERM_SEQ: Invoke G3 character set: ESC O
1687  */
1688 void
1689 rxvt_term::scr_charset_choose (int set)
1690   {
1691     screen.charset = set;
1692     set_font_style ();
1693   }
1694
1695 /* ------------------------------------------------------------------------- */
1696 /*
1697  * Set a font
1698  * XTERM_SEQ: Set G0 character set: ESC ( <C>
1699  * XTERM_SEQ: Set G1 character set: ESC ) <C>
1700  * XTERM_SEQ: Set G2 character set: ESC * <C>
1701  * XTERM_SEQ: Set G3 character set: ESC + <C>
1702  * See set_font_style for possible values for <C>
1703  */
1704 void
1705 rxvt_term::scr_charset_set (int set, unsigned int ch)
1706   {
1707     charsets[set] = (unsigned char)ch;
1708     set_font_style ();
1709   }
1710
1711 \f
1712 /* ------------------------------------------------------------------------- *
1713  *                        MAJOR SCREEN MANIPULATION                          *
1714  * ------------------------------------------------------------------------- */
1715
1716 /*
1717  * refresh matching text.
1718  */
1719 bool
1720 rxvt_term::scr_refresh_rend (rend_t mask, rend_t value)
1721 {
1722   bool found = false;
1723
1724   for (int i = 0; i < TermWin.nrow; i++)
1725     {
1726       int col = 0;
1727       rend_t *drp = drawn_rend [i];
1728
1729       for (; col < TermWin.ncol; col++, drp++)
1730         if ((*drp & mask) == value)
1731           {
1732             found = true;
1733             *drp = ~value;
1734           }
1735     }
1736
1737   return found;
1738 }
1739
1740 /*
1741  * Refresh an area
1742  */
1743 enum {
1744   PART_BEG = 0,
1745   PART_END,
1746   RC_COUNT
1747 };
1748
1749 void
1750 rxvt_term::scr_expose (int x, int y, int width, int height, bool refresh)
1751 {
1752   int i;
1753   row_col_t rc[RC_COUNT];
1754
1755   if (drawn_text == NULL)  /* sanity check */
1756     return;
1757
1758 #ifndef NO_SLOW_LINK_SUPPORT
1759   if (refresh_type == FAST_REFRESH && !display->is_local)
1760     {
1761       y = 0;
1762       height = TermWin.height;
1763     }
1764 #endif
1765
1766 #ifdef DEBUG_STRICT
1767   x = max (x, 0);
1768   x = min (x, (int)TermWin.width);
1769   y = max (y, 0);
1770   y = min (y, (int)TermWin.height);
1771 #endif
1772
1773   /* round down */
1774   rc[PART_BEG].col = Pixel2Col (x);
1775   rc[PART_BEG].row = Pixel2Row (y);
1776   /* round up */
1777   rc[PART_END].col = Pixel2Width (x + width + TermWin.fwidth - 1);
1778   rc[PART_END].row = Pixel2Row (y + height + TermWin.fheight - 1);
1779
1780   /* sanity checks */
1781   for (i = PART_BEG; i < RC_COUNT; i++)
1782     {
1783       MIN_IT (rc[i].col, TermWin.ncol - 1);
1784       MIN_IT (rc[i].row, TermWin.nrow - 1);
1785     }
1786
1787   for (i = rc[PART_BEG].row; i <= rc[PART_END].row; i++)
1788     fill_text (&drawn_text[i][rc[PART_BEG].col], 0, rc[PART_END].col - rc[PART_BEG].col + 1);
1789
1790   if (refresh)
1791     scr_refresh (SLOW_REFRESH);
1792 }
1793
1794 /* ------------------------------------------------------------------------- */
1795 /*
1796  * Refresh the entire screen
1797  */
1798 void
1799 rxvt_term::scr_touch (bool refresh)
1800 {
1801   scr_expose (0, 0, TermWin.width, TermWin.height, refresh);
1802 }
1803
1804 /* ------------------------------------------------------------------------- */
1805 /*
1806  * Move the display so that the line represented by scrollbar value Y is at
1807  * the top of the screen
1808  */
1809 int
1810 rxvt_term::scr_move_to (int y, int len)
1811 {
1812   long p = 0;
1813   uint16_t oldviewstart;
1814
1815   oldviewstart = TermWin.view_start;
1816
1817   if (y < len)
1818     {
1819       p = (TermWin.nrow + TermWin.nscrolled) * (len - y) / len;
1820       p -= (long) (TermWin.nrow - 1);
1821       p = max (p, 0);
1822     }
1823
1824   TermWin.view_start = (uint16_t)min (p, TermWin.nscrolled);
1825
1826   return scr_changeview (oldviewstart);
1827 }
1828
1829 /* ------------------------------------------------------------------------- */
1830 /*
1831  * Page the screen up/down nlines
1832  * direction should be UP or DN
1833  */
1834 int
1835 rxvt_term::scr_page (enum page_dirn direction, int nlines)
1836 {
1837   int n;
1838   uint16_t oldviewstart;
1839
1840 #ifdef DEBUG_STRICT
1841   assert ((nlines >= 0) && (nlines <= TermWin.nrow));
1842 #endif
1843   oldviewstart = TermWin.view_start;
1844   if (direction == UP)
1845     {
1846       n = TermWin.view_start + nlines;
1847       TermWin.view_start = min (n, TermWin.nscrolled);
1848     }
1849   else
1850     {
1851       n = TermWin.view_start - nlines;
1852       TermWin.view_start = max (n, 0);
1853     }
1854   return scr_changeview (oldviewstart);
1855 }
1856
1857 int
1858 rxvt_term::scr_changeview (uint16_t oldviewstart)
1859 {
1860   if (TermWin.view_start != oldviewstart)
1861     {
1862       want_refresh = 1;
1863       num_scr -= (TermWin.view_start - oldviewstart);
1864     }
1865
1866   return (int) (TermWin.view_start - oldviewstart);
1867 }
1868
1869 /* ------------------------------------------------------------------------- */
1870 void
1871 rxvt_term::scr_bell ()
1872 {
1873 #ifndef NO_BELL
1874 # ifndef NO_MAPALERT
1875 #  ifdef MAPALERT_OPTION
1876   if (options & Opt_mapAlert)
1877 #  endif
1878     XMapWindow (display->display, TermWin.parent[0]);
1879 # endif
1880   if (options & Opt_visualBell)
1881     {
1882       scr_rvideo_mode (!rvideo); /* refresh also done */
1883       scr_rvideo_mode (!rvideo); /* refresh also done */
1884     }
1885   else
1886     XBell (display->display, 0);
1887 #endif
1888 }
1889
1890 /* ------------------------------------------------------------------------- */
1891 /* ARGSUSED */
1892 void
1893 rxvt_term::scr_printscreen (int fullhist)
1894 {
1895 #ifdef PRINTPIPE
1896   int i, r1, nrows, row_offset;
1897   FILE *fd;
1898
1899   if ((fd = popen_printer ()) == NULL)
1900     return;
1901
1902   nrows = TermWin.nrow;
1903   row_offset = TermWin.saveLines;
1904
1905   if (!fullhist)
1906     row_offset -= TermWin.view_start;
1907   else
1908     {
1909       nrows += TermWin.nscrolled;
1910       row_offset -= TermWin.nscrolled;
1911     }
1912
1913   wctomb (0, 0);
1914
1915   for (r1 = 0; r1 < nrows; r1++)
1916     {
1917       text_t *tp = screen.text[r1 + row_offset];
1918       int len = screen.tlen[r1 + row_offset];
1919
1920       for (i = len >= 0 ? len : TermWin.ncol - 1; i--; )
1921         {
1922           char mb[MB_LEN_MAX];
1923           text_t t = *tp++;
1924           if (t == NOCHAR)
1925             continue;
1926
1927           len = wctomb (mb, t);
1928
1929           if (len <= 0)
1930             {
1931               mb[0] = ' ';
1932               len = 1;
1933             }
1934
1935           fwrite (mb, 1, len, fd);
1936         }
1937
1938       fputc ('\n', fd);
1939     }
1940
1941   pclose_printer (fd);
1942 #endif
1943 }
1944
1945 /* ------------------------------------------------------------------------- */
1946 /*
1947  * Refresh the screen
1948  * drawn_text/drawn_rend contain the screen information before the update.
1949  * screen.text/screen.rend contain what the screen will change to.
1950  */
1951
1952 #define FONT_WIDTH(X, Y)                                                \
1953     (X)->per_char[ (Y) - (X)->min_char_or_byte2].width
1954 #define FONT_RBEAR(X, Y)                                                \
1955     (X)->per_char[ (Y) - (X)->min_char_or_byte2].rbearing
1956 #define FONT_LBEAR(X, Y)                                                \
1957     (X)->per_char[ (Y) - (X)->min_char_or_byte2].lbearing
1958 #define IS_FONT_CHAR(X, Y)                                              \
1959     ((Y) >= (X)->min_char_or_byte2 && (Y) <= (X)->max_char_or_byte2)
1960
1961 void
1962 rxvt_term::scr_refresh (unsigned char refresh_type)
1963 {
1964   unsigned char must_clear, /* use draw_string not draw_image_string     */
1965                 rvid,       /* reverse video this position               */
1966                 showcursor; /* show the cursor                           */
1967   int16_t col, row,   /* column/row we're processing               */
1968           ocrow;      /* old cursor row                            */
1969   int i,              /* tmp                                       */
1970   row_offset;         /* basic offset in screen structure          */
1971 #ifndef NO_CURSORCOLOR
1972   rend_t cc1;         /* store colours at cursor position (s)      */
1973 #endif
1974   rend_t *crp;        // cursor rendition pointer
1975
1976   if (refresh_type == NO_REFRESH || !TermWin.mapped)
1977     return;
1978
1979   /*
1980    * A: set up vars
1981    */
1982   must_clear = 0;
1983   refresh_count = 0;
1984
1985   row_offset = TermWin.saveLines - TermWin.view_start;
1986
1987 #ifdef XPM_BACKGROUND
1988   must_clear |= (bgPixmap.pixmap != None);
1989 #endif
1990 #ifdef TRANSPARENT
1991   must_clear |= ((options & Opt_transparent) && am_transparent);
1992 #endif
1993   ocrow = oldcursor.row; /* is there an old outline cursor on screen? */
1994
1995   /*
1996    * B: reverse any characters which are selected
1997    */
1998   scr_reverse_selection ();
1999 #if ENABLE_OVERLAY
2000   scr_swap_overlay ();
2001 #endif
2002
2003   /*
2004    * C: set the cursor character (s)
2005    */
2006   {
2007     unsigned char setoldcursor;
2008     rend_t ccol1,  /* Cursor colour       */
2009            ccol2;  /* Cursor colour2      */
2010
2011     showcursor = (screen.flags & Screen_VisibleCursor);
2012 #ifdef CURSOR_BLINK
2013     if (hidden_cursor)
2014       showcursor = 0;
2015 #endif
2016
2017     if (showcursor)
2018       {
2019         int col = screen.cur.col;
2020
2021         while (col && screen.text[screen.cur.row + TermWin.saveLines][col] == NOCHAR)
2022           col--;
2023
2024         crp = &screen.rend[screen.cur.row + TermWin.saveLines][col];
2025
2026         if (showcursor && TermWin.focus)
2027           {
2028             *crp ^= RS_RVid;
2029 #ifndef NO_CURSORCOLOR
2030             cc1 = *crp & (RS_fgMask | RS_bgMask);
2031             if (ISSET_PIXCOLOR (Color_cursor))
2032               ccol1 = Color_cursor;
2033             else
2034 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2035               ccol1 = GET_FGCOLOR (rstyle);
2036 #else
2037               ccol1 = Color_fg;
2038 #endif
2039             if (ISSET_PIXCOLOR (Color_cursor2))
2040               ccol2 = Color_cursor2;
2041             else
2042 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2043               ccol2 = GET_BGCOLOR (rstyle);
2044 #else
2045               ccol2 = Color_bg;
2046 #endif
2047             *crp = SET_FGCOLOR (*crp, ccol1);
2048             *crp = SET_BGCOLOR (*crp, ccol2);
2049 #endif
2050           }
2051       }
2052
2053     /* make sure no outline cursor is left around */
2054     setoldcursor = 0;
2055     if (ocrow != -1)
2056       {
2057         if (screen.cur.row + TermWin.view_start != ocrow
2058             || screen.cur.col != oldcursor.col)
2059           {
2060             if (ocrow < TermWin.nrow
2061                 && oldcursor.col < TermWin.ncol)
2062               drawn_rend[ocrow][oldcursor.col] ^= (RS_RVid | RS_Uline);
2063
2064             if (TermWin.focus || !showcursor)
2065               oldcursor.row = -1;
2066             else
2067               setoldcursor = 1;
2068           }
2069       }
2070     else if (!TermWin.focus)
2071       setoldcursor = 1;
2072
2073     if (setoldcursor)
2074       {
2075         if (screen.cur.row + TermWin.view_start >= TermWin.nrow)
2076           oldcursor.row = -1;
2077         else
2078           {
2079             oldcursor.row = screen.cur.row + TermWin.view_start;
2080             oldcursor.col = screen.cur.col;
2081           }
2082       }
2083   }
2084
2085   rend_t *drp, *srp;  /* drawn-rend-pointer, screen-rend-pointer   */
2086   text_t *dtp, *stp;  /* drawn-text-pointer, screen-text-pointer   */
2087 #ifndef NO_SLOW_LINK_SUPPORT
2088   /*
2089    * D: CopyArea pass - very useful for slower links
2090    *    This has been deliberately kept simple.
2091    */
2092   i = num_scr;
2093   if (!display->is_local
2094       && refresh_type == FAST_REFRESH && num_scr_allow && i
2095       && abs (i) < TermWin.nrow && !must_clear)
2096     {
2097       int16_t nits;
2098       int j;
2099       int len, wlen;
2100
2101       j = TermWin.nrow;
2102       wlen = len = -1;
2103       row = i > 0 ? 0 : j - 1;
2104       for (; j-- >= 0; row += (i > 0 ? 1 : -1))
2105         {
2106           if (row + i >= 0 && row + i < TermWin.nrow && row + i != ocrow)
2107             {
2108               text_t *stp  = screen.text[row + row_offset];
2109               rend_t *srp  = screen.rend[row + row_offset];
2110               text_t *dtp  = drawn_text[row];
2111               text_t *dtp2 = drawn_text[row + i];
2112               rend_t *drp  = drawn_rend[row];
2113               rend_t *drp2 = drawn_rend[row + i];
2114
2115               for (nits = 0, col = TermWin.ncol; col--; )
2116                 if (stp[col] != dtp2[col] || srp[col] != drp2[col])
2117                   nits--;
2118                 else if (stp[col] != dtp[col] || srp[col] != drp[col])
2119                   nits++;
2120
2121               if (nits > 8) /* XXX: arbitrary choice */
2122                 {
2123                   for (col = TermWin.ncol; col--; )
2124                     {
2125                       *dtp++ = *dtp2++;
2126                       *drp++ = *drp2++;
2127                     }
2128
2129                   if (len == -1)
2130                     len = row;
2131
2132                   wlen = row;
2133                   continue;
2134                 }
2135             }
2136
2137           if (len != -1)
2138             {
2139               /* also comes here at end if needed because of >= above */
2140               if (wlen < len)
2141                 SWAP_IT (wlen, len, int);
2142
2143               XCopyArea (display->display, TermWin.vt, TermWin.vt,
2144                          TermWin.gc, 0, Row2Pixel (len + i),
2145                          (unsigned int)TermWin_TotalWidth (),
2146                          (unsigned int)Height2Pixel (wlen - len + 1),
2147                          0, Row2Pixel (len));
2148               len = -1;
2149             }
2150         }
2151     }
2152 #endif
2153
2154   /*
2155    * E: main pass across every character
2156    */
2157   for (row = 0; row < TermWin.nrow; row++)
2158     {
2159       text_t *stp = screen.text[row + row_offset];
2160       rend_t *srp = screen.rend[row + row_offset];
2161       text_t *dtp = drawn_text[row];
2162       rend_t *drp = drawn_rend[row];
2163
2164       /*
2165        * E2: OK, now the real pass
2166        */
2167       int ypixel = (int)Row2Pixel (row);
2168
2169       for (col = 0; col < TermWin.ncol; col++)
2170         {
2171           /* compare new text with old - if exactly the same then continue */
2172           if (stp[col] == dtp[col]    /* Must match characters to skip. */
2173               && (srp[col] == drp[col]    /* Either rendition the same or   */
2174                   || (stp[col] == ' ' /* space w/ no background change  */
2175                       && GET_BGATTR (srp[col]) == GET_BGATTR (drp[col]))))
2176             continue;
2177
2178           // redraw one or more characters
2179
2180           // seek to the beginning if wide characters
2181           while (stp[col] == NOCHAR && col > 0)
2182             --col;
2183
2184           rend_t rend = srp[col];     /* screen rendition (target rendtion) */
2185           text_t *text = stp + col;
2186           int count = 1;
2187
2188           dtp[col] = stp[col];
2189           drp[col] = rend;
2190
2191           int xpixel = Col2Pixel (col);
2192
2193           // this loop looks very messy, it can probably be optimized
2194           // and cleaned a bit by you?
2195           for (i = 0; ++col < TermWin.ncol; )
2196             {
2197               if (stp[col] == NOCHAR)
2198                 {
2199                   dtp[col] = stp[col];
2200                   drp[col] = rend;
2201                   count++;
2202                   i++;
2203
2204                   continue;
2205                 }
2206
2207               if (rend != srp[col])
2208                 break;
2209
2210               count++;
2211
2212               if (stp[col] != dtp[col]
2213                   || srp[col] != drp[col])
2214                 {
2215                   if (must_clear && (i++ > count / 2))
2216                     break;
2217
2218                   dtp[col] = stp[col];
2219                   drp[col] = rend;
2220                   i = 0;
2221                 }
2222               else if (must_clear || (stp[col] != ' ' && ++i >= 16))
2223                 break;
2224             }
2225
2226           col--;      /* went one too far.  move back */
2227           count -= i; /* dump any matching trailing chars */
2228
2229           // sometimes we optimize away the trailing NOCHAR's, add them back
2230           while (i && text[count] == NOCHAR)
2231             count++, i--;
2232
2233           /*
2234            * Determine the attributes for the string
2235            */
2236           int fid = GET_FONT (rend);
2237           int fore = GET_FGCOLOR (rend); // desired foreground
2238           int back = GET_BGCOLOR (rend); // desired background
2239
2240           rend = GET_ATTR (rend);
2241
2242           rvid = !!(rend & RS_RVid);
2243
2244 #ifndef NO_BOLD_UNDERLINE_REVERSE
2245           if (rend & RS_Bold && fore == Color_fg && !(options & Opt_realBold))
2246             {
2247               if (ISSET_PIXCOLOR (Color_BD))
2248                 fore = Color_BD;
2249               else
2250                 rvid = !rvid;
2251             }
2252
2253           if (rend & RS_Uline)
2254             if (ISSET_PIXCOLOR (Color_UL))
2255               fore = Color_UL;
2256 #endif
2257
2258           if (rvid)
2259             {
2260               SWAP_IT (fore, back, int);
2261
2262 #ifndef NO_BOLD_UNDERLINE_REVERSE
2263               if (ISSET_PIXCOLOR (Color_RV)
2264 # ifndef NO_CURSORCOLOR
2265                   && !ISSET_PIXCOLOR (Color_cursor)
2266 # endif
2267                  )
2268                 back = Color_RV;
2269 #endif
2270             }
2271
2272 #ifdef TEXT_BLINK
2273           if (rend & RS_Blink && (back == Color_bg || fore == Color_bg))
2274             {
2275               if (!text_blink_ev.active)
2276                 {
2277                   text_blink_ev.start (NOW + TEXT_BLINK_INTERVAL);
2278                   hidden_text = 0;
2279                 }
2280               else if (hidden_text)
2281                 fore = back;
2282             }
2283 #endif
2284
2285           /*
2286            * Actually do the drawing of the string here
2287            */
2288           rxvt_font *font = (*TermWin.fontset)[fid];
2289
2290           if (back == fore)
2291             font->clear_rect (*TermWin.drawable, xpixel, ypixel,
2292                               TermWin.fwidth * count, TermWin.fheight,
2293                               back);
2294           else if (back == Color_bg)
2295             {
2296               if (must_clear)
2297                 {
2298                   CLEAR_CHARS (xpixel, ypixel, count);
2299
2300                   for (i = 0; i < count; i++) /* don't draw empty strings */
2301                     if (text[i] != ' ')
2302                       {
2303                         font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, -1);
2304                         break;
2305                       }
2306                 }
2307               else
2308                 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, Color_bg);
2309             }
2310           else
2311             font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, back);
2312
2313           if (rend & RS_Uline && font->descent > 1 && fore != back)
2314             XDrawLine (display->display, drawBuffer, TermWin.gc,
2315                        xpixel, ypixel + font->ascent + 1,
2316                        xpixel + Width2Pixel (count) - 1, ypixel + font->ascent + 1);
2317         }                     /* for (col....) */
2318     }                         /* for (row....) */
2319
2320   /*
2321    * G: cleanup cursor and display outline cursor if necessary
2322    */
2323   if (showcursor)
2324     {
2325       if (TermWin.focus)
2326         {
2327           *crp ^= RS_RVid;
2328 #ifndef NO_CURSORCOLOR
2329           *crp = (*crp & ~ (RS_fgMask | RS_bgMask)) | cc1;
2330 #endif
2331         }
2332       else if (oldcursor.row >= 0)
2333         {
2334 #ifndef NO_CURSORCOLOR
2335           if (ISSET_PIXCOLOR (Color_cursor))
2336             XSetForeground (display->display, TermWin.gc, pix_colors[Color_cursor]);
2337 #endif
2338           int cursorwidth = 1;
2339           while (oldcursor.col + cursorwidth < TermWin.ncol
2340                  && drawn_text[oldcursor.row][oldcursor.col + cursorwidth] == NOCHAR)
2341             cursorwidth++;
2342
2343           XDrawRectangle (display->display, drawBuffer, TermWin.gc,
2344                           Col2Pixel (oldcursor.col),
2345                           Row2Pixel (oldcursor.row),
2346                           (unsigned int) (Width2Pixel (cursorwidth) - 1),
2347                           (unsigned int) (Height2Pixel (1) - TermWin.lineSpace - 1));
2348         }
2349     }
2350
2351   /*
2352    * H: cleanup selection
2353    */
2354 #if ENABLE_OVERLAY
2355   scr_swap_overlay ();
2356 #endif
2357   scr_reverse_selection ();
2358
2359   if (refresh_type & SMOOTH_REFRESH)
2360     XFlush (display->display);
2361
2362   num_scr = 0;
2363   num_scr_allow = 1;
2364   want_refresh = 0;        /* screen is current */
2365 }
2366
2367 void
2368 rxvt_term::scr_remap_chars (text_t *tp, rend_t *rp)
2369 {
2370   if (!rp || !tp)
2371     return;
2372
2373   for (int i = TermWin.ncol; i; i--, rp++, tp++)
2374     *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
2375 }
2376
2377 void
2378 rxvt_term::scr_remap_chars ()
2379 {
2380   for (int i = TermWin.nrow + TermWin.saveLines; i--; )
2381     scr_remap_chars (screen.text[i], screen.rend[i]);
2382
2383   for (int i = TermWin.nrow; i--; )
2384     {
2385       scr_remap_chars (drawn_text[i], drawn_rend[i]);
2386       scr_remap_chars (swap.text[i], swap.rend[i]);
2387     }
2388 }
2389
2390 /* ------------------------------------------------------------------------- */
2391 void
2392 rxvt_term::scr_clear (bool really)
2393 {
2394   if (!TermWin.mapped)
2395     return;
2396
2397   num_scr_allow = 0;
2398   want_refresh = 1;
2399
2400 #ifdef TRANSPARENT
2401   if ((options & Opt_transparent) && (am_pixmap_trans == 0))
2402     {
2403       int i;
2404
2405       if (! (options & Opt_transparent_all))
2406         i = 0;
2407       else
2408         i = (int) (sizeof (TermWin.parent) / sizeof (Window));
2409
2410       while (i--)
2411         if (TermWin.parent[i] != None)
2412           XClearWindow (display->display, TermWin.parent[i]);
2413     }
2414 #endif
2415
2416   if (really)
2417     XClearWindow (display->display, TermWin.vt);
2418 }
2419
2420 /* ------------------------------------------------------------------------- */
2421 void
2422 rxvt_term::scr_reverse_selection ()
2423 {
2424   if (selection.op && current_screen == selection.screen)
2425     {
2426       int end_row = TermWin.saveLines - TermWin.view_start;
2427       int i = selection.beg.row + TermWin.saveLines;
2428       int col, row = selection.end.row + TermWin.saveLines;
2429       rend_t *srp;
2430
2431       if (i >= end_row)
2432         col = selection.beg.col;
2433       else
2434         {
2435           col = 0;
2436           i = end_row;
2437         }
2438
2439       end_row += TermWin.nrow;
2440       for (; i < row && i < end_row; i++, col = 0)
2441         for (srp = screen.rend[i]; col < TermWin.ncol; col++)
2442           srp[col] ^= RS_RVid;
2443
2444       if (i == row && i < end_row)
2445         for (srp = screen.rend[i]; col < selection.end.col; col++)
2446           srp[col] ^= RS_RVid;
2447     }
2448 }
2449
2450 /* ------------------------------------------------------------------------- */
2451 /*
2452  * Dump the whole scrollback and screen to the passed filedescriptor.  The
2453  * invoking routine must close the fd.
2454  */
2455 #if 0
2456 void
2457 rxvt_term::scr_dump (int fd)
2458 {
2459   int             row, wrote;
2460   unsigned int    width, towrite;
2461   char            r1[] = "\n";
2462
2463   for (row = TermWin.saveLines - TermWin.nscrolled;
2464        row < TermWin.saveLines + TermWin.nrow - 1; row++)
2465     {
2466       width = screen.tlen[row] >= 0 ? screen.tlen[row]
2467               : TermWin.ncol;
2468       for (towrite = width; towrite; towrite -= wrote)
2469         {
2470           wrote = write (fd, & (screen.text[row][width - towrite]),
2471                         towrite);
2472           if (wrote < 0)
2473             return;         /* XXX: death, no report */
2474         }
2475       if (screen.tlen[row] >= 0)
2476         if (write (fd, r1, 1) <= 0)
2477           return; /* XXX: death, no report */
2478     }
2479 }
2480 #endif
2481 \f
2482 /* ------------------------------------------------------------------------- *
2483  *                           CHARACTER SELECTION                             *
2484  * ------------------------------------------------------------------------- */
2485
2486 /*
2487  * -TermWin.nscrolled <= (selection row) <= TermWin.nrow - 1
2488  */
2489 void
2490 rxvt_term::selection_check (int check_more)
2491 {
2492   row_col_t pos;
2493
2494   if (!selection.op)
2495     return;
2496
2497   pos.row = pos.col = 0;
2498   if ((selection.beg.row < - (int32_t)TermWin.nscrolled)
2499       || (selection.beg.row >= TermWin.nrow)
2500       || (selection.mark.row < - (int32_t)TermWin.nscrolled)
2501       || (selection.mark.row >= TermWin.nrow)
2502       || (selection.end.row < - (int32_t)TermWin.nscrolled)
2503       || (selection.end.row >= TermWin.nrow)
2504       || (check_more == 1
2505           && current_screen == selection.screen
2506           && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
2507           && ROWCOL_IS_BEFORE (screen.cur, selection.end))
2508       || (check_more == 2
2509           && ROWCOL_IS_BEFORE (selection.beg, pos)
2510           && ROWCOL_IS_AFTER (selection.end, pos))
2511       || (check_more == 3
2512           && ROWCOL_IS_AFTER (selection.end, pos))
2513       || (check_more == 4     /* screen width change */
2514           && (selection.beg.row != selection.end.row
2515               || selection.end.col > TermWin.ncol)))
2516     CLEAR_SELECTION ();
2517 }
2518
2519 /* ------------------------------------------------------------------------- */
2520 /*
2521  * Paste a selection direct to the command fd
2522  */
2523 void
2524 rxvt_term::paste (const unsigned char *data, unsigned int len)
2525 {
2526   unsigned int i, j, n;
2527   unsigned char *ds = (unsigned char *)rxvt_malloc (PROP_SIZE);
2528
2529   /* convert normal newline chars into common keyboard Return key sequence */
2530   for (i = 0; i < len; i += PROP_SIZE)
2531     {
2532       n = min (len - i, PROP_SIZE);
2533       memcpy (ds, data + i, n);
2534
2535       for (j = 0; j < n; j++)
2536         if (ds[j] == C0_LF)
2537           ds[j] = C0_CR;
2538
2539       tt_write (ds, (int)n);
2540     }
2541
2542   free (ds);
2543 }
2544
2545 /* ------------------------------------------------------------------------- */
2546 /*
2547  * Respond to a notification that a primary selection has been sent
2548  * EXT: SelectionNotify
2549  */
2550 int
2551 rxvt_term::selection_paste (Window win, Atom prop, bool delete_prop)
2552 {
2553   long nread = 0;
2554   unsigned long bytes_after;
2555   XTextProperty ct;
2556
2557   if (prop == None)         /* check for failed XConvertSelection */
2558     {
2559       if ((selection_type & Sel_CompoundText))
2560         {
2561           int selnum = selection_type & Sel_whereMask;
2562
2563           selection_type = 0;
2564           if (selnum != Sel_direct)
2565             selection_request_other (XA_STRING, selnum);
2566         }
2567
2568       if ((selection_type & Sel_UTF8String))
2569         {
2570           int selnum = selection_type & Sel_whereMask;
2571
2572           selection_type = Sel_CompoundText;
2573           if (selnum != Sel_direct)
2574             selection_request_other (xa[XA_COMPOUND_TEXT], selnum);
2575           else
2576             selection_type = 0;
2577         }
2578
2579       return 0;
2580     }
2581
2582   for (;;)
2583     {
2584       if (XGetWindowProperty (display->display, win, prop, (long) (nread / 4),
2585                               (long) (PROP_SIZE / 4), delete_prop,
2586                               AnyPropertyType, &ct.encoding, &ct.format,
2587                               &ct.nitems, &bytes_after,
2588                               &ct.value) != Success)
2589         break;
2590
2591       if (ct.encoding == 0)
2592         break;
2593
2594       if (ct.encoding == xa[XA_INCR])
2595         {
2596           // INCR selection, start handshake
2597           XDeleteProperty (display->display, win, prop);
2598           selection_wait = Sel_incr;
2599           incr_ev.start (NOW + 10);
2600           break;
2601         }
2602
2603       if (ct.value == NULL)
2604         continue;
2605
2606       if (ct.nitems == 0)
2607         {
2608           if (selection_wait == Sel_normal && nread == 0
2609               && (win != display->root || prop != XA_CUT_BUFFER0)) // avoid recursion
2610             {
2611               /*
2612                * pass through again trying CUT_BUFFER0 if we've come from
2613                * XConvertSelection () but nothing was presented
2614                */
2615               selection_paste (display->root, XA_CUT_BUFFER0, False);
2616             }
2617
2618           nread = -1;         /* discount any previous stuff */
2619           break;
2620         }
2621
2622       nread += ct.nitems;
2623
2624       char **cl;
2625       int cr;
2626       if (XmbTextPropertyToTextList (display->display, &ct, &cl, &cr) >= 0 && cl)
2627         {
2628           for (int i = 0; i < cr; i++)
2629             paste ((unsigned char *)cl[i], strlen (cl[i]));
2630
2631           XFreeStringList (cl);
2632         }
2633       else
2634         paste (ct.value, ct.nitems);
2635
2636       if (bytes_after == 0)
2637         break;
2638
2639       XFree (ct.value);
2640     }
2641
2642   if (ct.value)
2643     XFree (ct.value);
2644
2645   if (selection_wait == Sel_normal)
2646     selection_wait = Sel_none;
2647
2648   return (int)nread;
2649 }
2650
2651 void
2652 rxvt_term::incr_cb (time_watcher &w)
2653 {
2654   selection_wait = Sel_none;
2655
2656   rxvt_warn ("data loss: timeout on INCR selection paste, ignoring.\n");
2657 }
2658
2659 /*
2660  * INCR support originally provided by Paul Sheer <psheer@obsidian.co.za>
2661  */
2662 void
2663 rxvt_term::selection_property (Window win, Atom prop)
2664 {
2665   if (prop == None || selection_wait != Sel_incr)
2666     return;
2667
2668   if (selection_paste (win, prop, 1) > 0)
2669     incr_ev.start (NOW + 10);
2670   else
2671     {
2672       selection_wait = Sel_none;
2673       incr_ev.stop ();
2674     }
2675 }
2676
2677 /* ------------------------------------------------------------------------- */
2678 /*
2679  * Request the current selection: 
2680  * Order: > internal selection if available
2681  *        > PRIMARY, SECONDARY, CLIPBOARD if ownership is claimed (+)
2682  *        > CUT_BUFFER0
2683  * (+) if ownership is claimed but property is empty, rxvt_selection_paste ()
2684  *     will auto fallback to CUT_BUFFER0
2685  * EXT: button 2 release
2686  */
2687 void
2688 rxvt_term::selection_request (Time tm, int x, int y)
2689 {
2690   if (x < 0 || x >= TermWin.width || y < 0 || y >= TermWin.height)
2691     return;                 /* outside window */
2692
2693   if (selection.text)
2694     { /* internal selection */
2695       char *str = rxvt_wcstombs (selection.text, selection.len);
2696       paste ((unsigned char *)str, strlen (str));
2697       free (str);
2698       return;
2699     }
2700   else
2701     {
2702       int i;
2703
2704       selection_request_time = tm;
2705       selection_wait = Sel_normal;
2706
2707       for (i = Sel_Primary; i <= Sel_Clipboard; i++)
2708         {
2709 #if X_HAVE_UTF8_STRING
2710           selection_type = Sel_UTF8String;
2711           if (selection_request_other (xa[XA_UTF8_STRING], i))
2712             return;
2713 #else
2714           selection_type = Sel_CompoundText;
2715           if (selection_request_other (xa[XA_COMPOUND_TEXT], i))
2716             return;
2717 #endif
2718
2719         }
2720     }
2721
2722   selection_wait = Sel_none;       /* don't loop in rxvt_selection_paste () */
2723   selection_paste (display->root, XA_CUT_BUFFER0, False);
2724 }
2725
2726 int
2727 rxvt_term::selection_request_other (Atom target, int selnum)
2728 {
2729   Atom sel;
2730 #ifdef DEBUG_SELECT
2731   char *debug_xa_names[] = { "PRIMARY", "SECONDARY", "CLIPBOARD" };
2732 #endif
2733
2734   selection_type |= selnum;
2735
2736   if (selnum == Sel_Primary)
2737     sel = XA_PRIMARY;
2738   else if (selnum == Sel_Secondary)
2739     sel = XA_SECONDARY;
2740   else
2741     sel = xa[XA_CLIPBOARD];
2742
2743   if (XGetSelectionOwner (display->display, sel) != None)
2744     {
2745       XConvertSelection (display->display, sel, target, xa[XA_VT_SELECTION],
2746                          TermWin.vt, selection_request_time);
2747       return 1;
2748     }
2749
2750   return 0;
2751 }
2752
2753 /* ------------------------------------------------------------------------- */
2754 /*
2755  * Clear all selected text
2756  * EXT: SelectionClear
2757  */
2758 void
2759 rxvt_term::selection_clear ()
2760 {
2761   want_refresh = 1;
2762   free (selection.text);
2763   selection.text = NULL;
2764   selection.len = 0;
2765   CLEAR_SELECTION ();
2766
2767   if (display->selection_owner == this)
2768     display->selection_owner = 0;
2769 }
2770
2771 /* ------------------------------------------------------------------------- */
2772 /*
2773  * Copy a selection into the cut buffer
2774  * EXT: button 1 or 3 release
2775  */
2776 void
2777 rxvt_term::selection_make (Time tm)
2778 {
2779   int i, col, end_col, row, end_row;
2780   wchar_t *new_selection_text;
2781   text_t *t;
2782
2783   switch (selection.op)
2784     {
2785       case SELECTION_CONT:
2786         break;
2787       case SELECTION_INIT:
2788         CLEAR_SELECTION ();
2789         /* FALLTHROUGH */
2790       case SELECTION_BEGIN:
2791         selection.op = SELECTION_DONE;
2792         /* FALLTHROUGH */
2793       default:
2794         return;
2795     }
2796
2797   selection.op = SELECTION_DONE;
2798
2799   if (selection.clicks == 4)
2800     return;                 /* nothing selected, go away */
2801
2802   i = (selection.end.row - selection.beg.row + 1) * (TermWin.ncol + 1);
2803   new_selection_text = (wchar_t *)rxvt_malloc ((i + 4) * sizeof (wchar_t));
2804
2805   col = selection.beg.col;
2806   MAX_IT (col, 0);
2807   row = selection.beg.row + TermWin.saveLines;
2808   end_row = selection.end.row + TermWin.saveLines;
2809   int ofs = 0;
2810   int extra = 0;
2811
2812   for (; row <= end_row; row++, col = 0)
2813     {
2814       t = &screen.text[row][col];
2815
2816       end_col = screen.tlen[row];
2817
2818       if (end_col == -1)
2819         end_col = TermWin.ncol;
2820
2821       if (row == end_row)
2822         MIN_IT (end_col, selection.end.col);
2823
2824       for (; col < end_col; col++)
2825         {
2826           if (*t == NOCHAR)
2827             t++;
2828 #if ENABLE_COMBINING
2829           else if (IS_COMPOSE (*t))
2830             {
2831               int len = rxvt_composite.expand (*t, 0);
2832
2833               extra -= (len - 1);
2834
2835               if (extra < 0)
2836                 {
2837                   extra += i;
2838                   i += i;
2839                   new_selection_text = (wchar_t *)rxvt_realloc (new_selection_text, (i + 4) * sizeof (wchar_t));
2840                 }
2841
2842               ofs += rxvt_composite.expand (*t++, new_selection_text + ofs);
2843             }
2844 #endif
2845           else
2846             new_selection_text[ofs++] = *t++;
2847         }
2848
2849       if (screen.tlen[row] != -1 && row != end_row)
2850         new_selection_text[ofs++] = C0_LF;
2851     }
2852
2853   if (end_col != selection.end.col)
2854     new_selection_text[ofs++] = C0_LF;
2855
2856   new_selection_text[ofs] = 0;
2857
2858   if (ofs == 0)
2859     {
2860       free (new_selection_text);
2861       return;
2862     }
2863
2864   free (selection.text);
2865
2866   // we usually allocate much more than necessary, so realloc it smaller again
2867   selection.len = ofs;
2868   selection.text = (wchar_t *)rxvt_realloc (new_selection_text, (ofs + 1) * sizeof (wchar_t));
2869
2870   XSetSelectionOwner (display->display, XA_PRIMARY, TermWin.vt, tm);
2871   if (XGetSelectionOwner (display->display, XA_PRIMARY) == TermWin.vt)
2872     display->set_selection_owner (this);
2873   else
2874     rxvt_warn ("can't get primary selection, ignoring.\n");
2875
2876 #if 0
2877   XTextProperty ct;
2878
2879   if (XwcTextListToTextProperty (display->display, &selection.text, 1, XStringStyle, &ct) >= 0)
2880     {
2881       XChangeProperty (display->display, display->root, XA_CUT_BUFFER0, XA_STRING, 8,
2882                        PropModeReplace, ct.value, ct.nitems);
2883       XFree (ct.value);
2884     }
2885 #endif
2886
2887   selection_time = tm;
2888 }
2889
2890 /* ------------------------------------------------------------------------- */
2891 /*
2892  * Mark or select text based upon number of clicks: 1, 2, or 3
2893  * EXT: button 1 press
2894  */
2895 void
2896 rxvt_term::selection_click (int clicks, int x, int y)
2897 {
2898   clicks = ((clicks - 1) % 3) + 1;
2899   selection.clicks = clicks;       /* save clicks so extend will work */
2900
2901   selection_start_colrow (Pixel2Col (x), Pixel2Row (y));
2902
2903   if (clicks == 2 || clicks == 3)
2904     selection_extend_colrow (selection.mark.col,
2905                              selection.mark.row + TermWin.view_start,
2906                              0, /* button 3     */
2907                              1, /* button press */
2908                              0);        /* click change */
2909 }
2910
2911 /* ------------------------------------------------------------------------- */
2912 /*
2913  * Mark a selection at the specified col/row
2914  */
2915 void
2916 rxvt_term::selection_start_colrow (int col, int row)
2917 {
2918   want_refresh = 1;
2919   selection.mark.col = col;
2920   selection.mark.row = row - TermWin.view_start;
2921
2922   MAX_IT (selection.mark.row, - (int32_t)TermWin.nscrolled);
2923   MIN_IT (selection.mark.row, (int32_t)TermWin.nrow - 1);
2924   MAX_IT (selection.mark.col, 0);
2925   MIN_IT (selection.mark.col, (int32_t)TermWin.ncol - 1);
2926
2927   while (selection.mark.col > 0
2928          && screen.text[selection.mark.row + TermWin.saveLines][selection.mark.col] == NOCHAR)
2929     --selection.mark.col;
2930   
2931   if (selection.op)
2932     {      /* clear the old selection */
2933       selection.beg.row = selection.end.row = selection.mark.row;
2934       selection.beg.col = selection.end.col = selection.mark.col;
2935     }
2936
2937   selection.op = SELECTION_INIT;
2938   selection.screen = current_screen;
2939 }
2940
2941 /* ------------------------------------------------------------------------- */
2942 /*
2943  * Word select: select text for 2 clicks
2944  * We now only find out the boundary in one direction
2945  */
2946
2947 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
2948 #define DELIMIT_TEXT(x)                 \
2949     (unicode::is_space (x) ? 2 : (x) <= 0xff && !!strchr (rs[Rs_cutchars], (x)))
2950 #define DELIMIT_REND(x)        1
2951
2952 void
2953 rxvt_term::selection_delimit_word (enum page_dirn dirn, const row_col_t *mark, row_col_t *ret)
2954 {
2955   int col, row, dirnadd, tcol, trow, w1, w2;
2956   row_col_t bound;
2957   text_t *stp;
2958   rend_t *srp;
2959
2960   if (dirn == UP)
2961     {
2962       bound.row = TermWin.saveLines - TermWin.nscrolled - 1;
2963       bound.col = 0;
2964       dirnadd = -1;
2965     }
2966   else
2967     {
2968       bound.row = TermWin.saveLines + TermWin.nrow;
2969       bound.col = TermWin.ncol - 1;
2970       dirnadd = 1;
2971     }
2972
2973   row = mark->row + TermWin.saveLines;
2974   col = mark->col;
2975   MAX_IT (col, 0);
2976   /* find the edge of a word */
2977   stp = &screen.text[row][col];
2978   w1 = DELIMIT_TEXT (*stp);
2979
2980   srp = &screen.rend[row][col];
2981   w2 = DELIMIT_REND (*srp);
2982
2983   for (;;)
2984     {
2985       for (; col != bound.col; col += dirnadd)
2986         {
2987           stp += dirnadd;
2988           srp += dirnadd;
2989
2990           if (*stp == NOCHAR)
2991             continue;
2992
2993           if (DELIMIT_TEXT (*stp) != w1)
2994             break;
2995           if (DELIMIT_REND (*srp) != w2)
2996             break;
2997         }
2998
2999       if ((col == bound.col) && (row != bound.row))
3000         {
3001           if (screen.tlen[ (row - (dirn == UP ? 1 : 0))] == -1)
3002             {
3003               trow = row + dirnadd;
3004               tcol = dirn == UP ? TermWin.ncol - 1 : 0;
3005
3006               if (screen.text[trow] == NULL)
3007                 break;
3008
3009               stp = & (screen.text[trow][tcol]);
3010               srp = & (screen.rend[trow][tcol]);
3011
3012               if (DELIMIT_TEXT (*stp) != w1 || DELIMIT_REND (*srp) != w2)
3013                 break;
3014
3015               row = trow;
3016               col = tcol;
3017               continue;
3018             }
3019         }
3020       break;
3021     }
3022
3023 Old_Word_Selection_You_Die:
3024   if (dirn == DN)
3025     col++;                  /* put us on one past the end */
3026
3027   /* Poke the values back in */
3028   ret->row = row - TermWin.saveLines;
3029   ret->col = col;
3030 }
3031
3032 /* ------------------------------------------------------------------------- */
3033 /*
3034  * Extend the selection to the specified x/y pixel location
3035  * EXT: button 3 press; button 1 or 3 drag
3036  * flag == 0 ==> button 1
3037  * flag == 1 ==> button 3 press
3038  * flag == 2 ==> button 3 motion
3039  */
3040 void
3041 rxvt_term::selection_extend (int x, int y, int flag)
3042 {
3043   int col, row;
3044
3045   col = Pixel2Col (x);
3046   row = Pixel2Row (y);
3047   MAX_IT (row, 0);
3048   MIN_IT (row, (int)TermWin.nrow - 1);
3049   MAX_IT (col, 0);
3050   MIN_IT (col, (int)TermWin.ncol);
3051
3052   /*
3053   * If we're selecting characters (single click) then we must check first
3054   * if we are at the same place as the original mark.  If we are then
3055   * select nothing.  Otherwise, if we're to the right of the mark, you have to
3056   * be _past_ a character for it to be selected.
3057   */
3058   if (((selection.clicks % 3) == 1) && !flag
3059       && (col == selection.mark.col
3060           && (row == selection.mark.row + TermWin.view_start)))
3061     {
3062       /* select nothing */
3063       selection.beg.row = selection.end.row = 0;
3064       selection.beg.col = selection.end.col = 0;
3065       selection.clicks = 4;
3066       want_refresh = 1;
3067       return;
3068     }
3069
3070   if (selection.clicks == 4)
3071     selection.clicks = 1;
3072
3073   selection_extend_colrow (col, row, !!flag,  /* ? button 3      */
3074                            flag == 1 ? 1 : 0,     /* ? button press  */
3075                            0);    /* no click change */
3076 }
3077
3078 /* ------------------------------------------------------------------------- */
3079 /*
3080  * Extend the selection to the specified col/row
3081  */
3082 void
3083 rxvt_term::selection_extend_colrow (int32_t col, int32_t row, int button3, int buttonpress, int clickchange)
3084 {
3085   int16_t ncol = TermWin.ncol;
3086   int end_col;
3087   row_col_t pos;
3088   enum {
3089     LEFT, RIGHT
3090   } closeto = RIGHT;
3091
3092   want_refresh = 1;
3093   switch (selection.op)
3094     {
3095       case SELECTION_INIT:
3096         CLEAR_SELECTION ();
3097         selection.op = SELECTION_BEGIN;
3098         /* FALLTHROUGH */
3099       case SELECTION_BEGIN:
3100         if (row != selection.mark.row || col != selection.mark.col
3101             || (!button3 && buttonpress))
3102           selection.op = SELECTION_CONT;
3103         break;
3104       case SELECTION_DONE:
3105         selection.op = SELECTION_CONT;
3106         /* FALLTHROUGH */
3107       case SELECTION_CONT:
3108         break;
3109       case SELECTION_CLEAR:
3110         selection_start_colrow (col, row);
3111         /* FALLTHROUGH */
3112       default:
3113         return;
3114     }
3115   if (selection.beg.col == selection.end.col
3116       && selection.beg.col != selection.mark.col
3117       && selection.beg.row == selection.end.row
3118       && selection.beg.row != selection.mark.row)
3119     {
3120       selection.beg.col = selection.end.col = selection.mark.col;
3121       selection.beg.row = selection.end.row = selection.mark.row;
3122     }
3123
3124   pos.col = col;
3125   pos.row = row;
3126
3127   pos.row -= TermWin.view_start;   /* adjust for scroll */
3128
3129   /*
3130    * This is mainly xterm style selection with a couple of differences, mainly
3131    * in the way button3 drag extension works.
3132    * We're either doing: button1 drag; button3 press; or button3 drag
3133    *  a) button1 drag : select around a midpoint/word/line - that point/word/line
3134    *     is always at the left/right edge of the selection.
3135    *  b) button3 press: extend/contract character/word/line at whichever edge of
3136    *     the selection we are closest to.
3137    *  c) button3 drag : extend/contract character/word/line - we select around
3138    *     a point/word/line which is either the start or end of the selection
3139    *     and it was decided by whichever point/word/line was `fixed' at the
3140    *     time of the most recent button3 press
3141    */
3142   if (button3 && buttonpress)
3143     { /* button3 press */
3144       /*
3145        * first determine which edge of the selection we are closest to
3146        */
3147       if (ROWCOL_IS_BEFORE (pos, selection.beg)
3148           || (!ROWCOL_IS_AFTER (pos, selection.end)
3149               && (((pos.col - selection.beg.col)
3150                    + ((pos.row - selection.beg.row) * ncol))
3151                   < ((selection.end.col - pos.col)
3152                      + ((selection.end.row - pos.row) * ncol)))))
3153         closeto = LEFT;
3154
3155       if (closeto == LEFT)
3156         {
3157           selection.beg.row = pos.row;
3158           selection.beg.col = pos.col;
3159           selection.mark.row = selection.end.row;
3160           selection.mark.col = selection.end.col - (selection.clicks == 2);
3161         }
3162       else
3163         {
3164           selection.end.row = pos.row;
3165           selection.end.col = pos.col;
3166           selection.mark.row = selection.beg.row;
3167           selection.mark.col = selection.beg.col;
3168         }
3169     }
3170   else
3171     { /* button1 drag or button3 drag */
3172       if (ROWCOL_IS_AFTER (selection.mark, pos))
3173         {
3174           if ((selection.mark.row == selection.end.row)
3175               && (selection.mark.col == selection.end.col)
3176               && clickchange && selection.clicks == 2)
3177             selection.mark.col--;
3178
3179           selection.beg.row = pos.row;
3180           selection.beg.col = pos.col;
3181           selection.end.row = selection.mark.row;
3182           selection.end.col = selection.mark.col + (selection.clicks == 2);
3183         }
3184       else
3185         {
3186           selection.beg.row = selection.mark.row;
3187           selection.beg.col = selection.mark.col;
3188           selection.end.row = pos.row;
3189           selection.end.col = pos.col;
3190         }
3191     }
3192
3193   if (selection.clicks == 1)
3194     {
3195       end_col = screen.tlen[selection.beg.row + TermWin.saveLines];
3196
3197       if (end_col != -1 && selection.beg.col > end_col)
3198         {
3199 #if 1
3200           selection.beg.col = ncol;
3201 #else
3202           if (selection.beg.row != selection.end.row)
3203             selection.beg.col = ncol;
3204           else
3205             selection.beg.col = selection.mark.col;
3206 #endif
3207         }
3208
3209       end_col = screen.tlen[selection.end.row + TermWin.saveLines];
3210
3211       if (end_col != -1 && selection.end.col > end_col)
3212         selection.end.col = ncol;
3213     }
3214   else if (selection.clicks == 2)
3215     {
3216       if (ROWCOL_IS_AFTER (selection.end, selection.beg))
3217         selection.end.col--;
3218
3219       selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3220       selection_delimit_word (DN, & (selection.end), & (selection.end));
3221     }
3222   else if (selection.clicks == 3)
3223     {
3224 #if ENABLE_FRILLS
3225       if ((options & Opt_tripleclickwords))
3226         {
3227           int end_row;
3228
3229           selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3230           end_row = screen.tlen[selection.mark.row + TermWin.saveLines];
3231
3232           for (end_row = selection.mark.row; end_row < TermWin.nrow; end_row++)
3233             {
3234               end_col = screen.tlen[end_row + TermWin.saveLines];
3235
3236               if (end_col != -1)
3237                 {
3238                   selection.end.row = end_row;
3239                   selection.end.col = end_col;
3240                   selection_remove_trailing_spaces ();
3241                   break;
3242                 }
3243             }
3244         }
3245       else
3246 #endif
3247         {
3248           if (ROWCOL_IS_AFTER (selection.mark, selection.beg))
3249             selection.mark.col++;
3250           selection.beg.col = 0;
3251           selection.end.col = ncol;
3252         }
3253     }
3254
3255   if (button3 && buttonpress)
3256     { /* mark may need to be changed */
3257       if (closeto == LEFT)
3258         {
3259           selection.mark.row = selection.end.row;
3260           selection.mark.col = selection.end.col - (selection.clicks == 2);
3261         }
3262       else
3263         {
3264           selection.mark.row = selection.beg.row;
3265           selection.mark.col = selection.beg.col;
3266         }
3267     }
3268 }
3269
3270 #if ENABLE_FRILLS
3271 void
3272 rxvt_term::selection_remove_trailing_spaces ()
3273 {
3274   int32_t         end_col, end_row;
3275   text_t         *stp;
3276
3277   end_col = selection.end.col;
3278   end_row = selection.end.row;
3279   for ( ; end_row >= selection.beg.row; )
3280     {
3281       stp = screen.text[end_row + TermWin.saveLines];
3282       while (--end_col >= 0)
3283         {
3284           if (stp[end_col] != ' ' && stp[end_col] != '\t')
3285             break;
3286         }
3287       if (end_col >= 0
3288           || screen.tlen[end_row - 1 + TermWin.saveLines] != -1)
3289         {
3290           selection.end.col = end_col + 1;
3291           selection.end.row = end_row;
3292           break;
3293         }
3294       end_row--;
3295       end_col = TermWin.ncol;
3296     }
3297   if (selection.mark.row > selection.end.row)
3298     {
3299       selection.mark.row = selection.end.row;
3300       selection.mark.col = selection.end.col;
3301     }
3302   else if (selection.mark.row == selection.end.row
3303            && selection.mark.col > selection.end.col)
3304     selection.mark.col = selection.end.col;
3305 }
3306 #endif
3307
3308 /* ------------------------------------------------------------------------- */
3309 /*
3310  * Double click on button 3 when already selected
3311  * EXT: button 3 double click
3312  */
3313 void
3314 rxvt_term::selection_rotate (int x, int y)
3315 {
3316   selection.clicks = selection.clicks % 3 + 1;
3317   selection_extend_colrow (Pixel2Col (x), Pixel2Row (y), 1, 0, 1);
3318 }
3319
3320 /* ------------------------------------------------------------------------- */
3321 /*
3322  * On some systems, the Atom typedef is 64 bits wide.  We need to have a type
3323  * that is exactly 32 bits wide, because a format of 64 is not allowed by
3324  * the X11 protocol.
3325  */
3326 typedef CARD32 Atom32;
3327
3328 /* ------------------------------------------------------------------------- */
3329 /*
3330  * Respond to a request for our current selection
3331  * EXT: SelectionRequest
3332  */
3333 void
3334 rxvt_term::selection_send (const XSelectionRequestEvent &rq)
3335 {
3336   XSelectionEvent ev;
3337   XTextProperty ct;
3338   XICCEncodingStyle style;
3339   Atom target;
3340
3341   ev.type = SelectionNotify;
3342   ev.property = None;
3343   ev.display = rq.display;
3344   ev.requestor = rq.requestor;
3345   ev.selection = rq.selection;
3346   ev.target = rq.target;
3347   ev.time = rq.time;
3348
3349   if (rq.target == xa[XA_TARGETS])
3350     {
3351       Atom32 target_list[6];
3352       Atom32 *target = target_list;
3353
3354       *target++ = (Atom32) xa[XA_TARGETS];
3355       *target++ = (Atom32) xa[XA_TIMESTAMP];
3356       *target++ = (Atom32) XA_STRING;
3357       *target++ = (Atom32) xa[XA_TEXT];
3358       *target++ = (Atom32) xa[XA_COMPOUND_TEXT];
3359 #if X_HAVE_UTF8_STRING
3360       *target++ = (Atom32) xa[XA_UTF8_STRING];
3361 #endif
3362
3363       XChangeProperty (display->display, rq.requestor, rq.property, XA_ATOM,
3364                        (8 * sizeof (target_list[0])), PropModeReplace,
3365                        (unsigned char *)target_list,
3366                        target - target_list);
3367       ev.property = rq.property;
3368     }
3369 #if TODO // TODO
3370   else if (rq.target == xa[XA_MULTIPLE])
3371     {
3372       /* TODO: Handle MULTIPLE */
3373     }
3374 #endif
3375   else if (rq.target == xa[XA_TIMESTAMP] && selection.text)
3376     {
3377       XChangeProperty (display->display, rq.requestor, rq.property, XA_INTEGER,
3378                        (8 * sizeof (Time)), PropModeReplace,
3379                        (unsigned char *)&selection_time, 1);
3380       ev.property = rq.property;
3381     }
3382   else if (rq.target == XA_STRING
3383            || rq.target == xa[XA_TEXT]
3384            || rq.target == xa[XA_COMPOUND_TEXT]
3385            || rq.target == xa[XA_UTF8_STRING]
3386           )
3387     {
3388       short freect = 0;
3389       int selectlen;
3390       wchar_t *cl;
3391
3392       target = rq.target;
3393
3394       if (target == XA_STRING)
3395         // we actually don't do XA_STRING, but who cares, as i18n clients
3396         // will ask for another format anyways.
3397         style = XStringStyle;
3398       else if (target == xa[XA_TEXT])
3399         style = XStdICCTextStyle;
3400       else if (target == xa[XA_COMPOUND_TEXT])
3401         style = XCompoundTextStyle;
3402 #if X_HAVE_UTF8_STRING
3403       else if (target == xa[XA_UTF8_STRING])
3404         style = XUTF8StringStyle;
3405 #endif
3406       else
3407         {
3408           target = xa[XA_COMPOUND_TEXT];
3409           style = XCompoundTextStyle;
3410         }
3411
3412       if (selection.text)
3413         {
3414           cl = selection.text;
3415           selectlen = selection.len;
3416         }
3417       else
3418         {
3419           cl = L"";
3420           selectlen = 0;
3421         }
3422
3423       // Xwc doesn't handle iso-10646 in wchar_t gracefully, so maybe recode it
3424       // manually for XUTF8StringStyle.
3425       if (XwcTextListToTextProperty (display->display, &cl, 1, style, &ct) >= 0)
3426         freect = 1;
3427       else
3428         {
3429           /* if we failed to convert then send it raw */
3430           ct.value = (unsigned char *)cl;
3431           ct.nitems = selectlen;
3432         }
3433
3434       XChangeProperty (display->display, rq.requestor, rq.property,
3435                        target, 8, PropModeReplace,
3436                        ct.value, (int)ct.nitems);
3437       ev.property = rq.property;
3438
3439       if (freect)
3440         XFree (ct.value);
3441     }
3442
3443   XSendEvent (display->display, rq.requestor, False, 0L, (XEvent *)&ev);
3444 }
3445 \f
3446 /* ------------------------------------------------------------------------- *
3447  *                              MOUSE ROUTINES                               *
3448  * ------------------------------------------------------------------------- */
3449
3450 /*
3451  * return col/row values corresponding to x/y pixel values
3452  */
3453 void
3454 rxvt_term::pixel_position (int *x, int *y)
3455 {
3456   *x = Pixel2Col (*x);
3457   /* MAX_IT (*x, 0); MIN_IT (*x, (int)TermWin.ncol - 1); */
3458   *y = Pixel2Row (*y);
3459   /* MAX_IT (*y, 0); MIN_IT (*y, (int)TermWin.nrow - 1); */
3460 }
3461
3462 /* ------------------------------------------------------------------------- */
3463 #ifdef USE_XIM
3464 void
3465 rxvt_term::im_set_position (XPoint &pos)
3466 {
3467   XWindowAttributes xwa;
3468
3469   XGetWindowAttributes (display->display, TermWin.vt, &xwa);
3470
3471   pos.x = xwa.x + Col2Pixel    (screen.cur.col);
3472   pos.y = xwa.y + Height2Pixel (screen.cur.row) + TermWin.fbase;
3473 }
3474 #endif
3475
3476 #if ENABLE_OVERLAY
3477 void
3478 rxvt_term::scr_overlay_new (int x, int y, int w, int h)
3479 {
3480   if (TermWin.nrow < 3 || TermWin.ncol < 3)
3481     return;
3482
3483   want_refresh = 1;
3484
3485   scr_overlay_off ();
3486
3487   if (x < 0) x = TermWin.ncol - w;
3488   if (y < 0) y = TermWin.nrow - h;
3489
3490   // make space for border
3491   w += 2; MIN_IT (w, TermWin.ncol);
3492   h += 2; MIN_IT (h, TermWin.nrow);
3493
3494   x -= 1; MAX_IT (x, 0);
3495   y -= 1; MAX_IT (y, 0);
3496
3497   MIN_IT (x, TermWin.ncol - w);
3498   MIN_IT (y, TermWin.nrow - h);
3499
3500   ov_x = x; ov_y = y;
3501   ov_w = w; ov_h = h;
3502
3503   ov_text = new text_t *[h];
3504   ov_rend = new rend_t *[h];
3505
3506   for (y = 0; y < h; y++)
3507     {
3508       text_t *tp = ov_text[y] = new text_t[w];
3509       rend_t *rp = ov_rend[y] = new rend_t[w];
3510
3511       text_t t0, t1, t2;
3512       rend_t r = SET_BGCOLOR (SET_FGCOLOR (RS_None, Color_bg), Color_fg);
3513
3514       if (y == 0)
3515         t0 = 0x2554, t1 = 0x2550, t2 = 0x2557;
3516       else if (y < h - 1)
3517         t0 = 0x2551, t1 = 0x0020, t2 = 0x2551;
3518       else
3519         t0 = 0x255a, t1 = 0x2550, t2 = 0x255d;
3520
3521       *tp++ = t0;
3522       *rp++ = r;
3523
3524       for (x = w - 2; x > 0; --x)
3525         {
3526           *tp++ = t1;
3527           *rp++ = r;
3528         }
3529
3530       *tp = t2;
3531       *rp = r;
3532     }
3533 }
3534
3535 void
3536 rxvt_term::scr_overlay_off ()
3537 {
3538   if (!ov_text)
3539     return;
3540
3541   want_refresh = 1;
3542
3543   for (int y = 0; y < ov_h; y++)
3544     {
3545       delete [] ov_text[y];
3546       delete [] ov_rend[y];
3547     }
3548
3549   delete [] ov_text; ov_text = 0;
3550   delete [] ov_rend; ov_rend = 0;
3551 }
3552
3553 void
3554 rxvt_term::scr_overlay_set (int x, int y, text_t text, rend_t rend)
3555 {
3556   if (!ov_text || x >= ov_w - 2 || y >= ov_h)
3557     return;
3558
3559   x++, y++;
3560
3561   ov_text[y][x] = text;
3562   ov_rend[y][x] = rend;
3563 }
3564
3565 void
3566 rxvt_term::scr_overlay_set (int x, int y, const char *s)
3567 {
3568   while (*s)
3569     scr_overlay_set (x++, y, *s++);
3570 }
3571
3572 void
3573 rxvt_term::scr_swap_overlay ()
3574 {
3575   if (!ov_text)
3576     return;
3577
3578   int row_offset = ov_y + TermWin.saveLines - TermWin.view_start;
3579
3580   // swap screen mem with overlay
3581   for (int y = ov_h; y--; )
3582     {
3583       text_t *t1 = ov_text[y];
3584       rend_t *r1 = ov_rend[y];
3585
3586       text_t *t2 = screen.text[y + row_offset] + ov_x;
3587       rend_t *r2 = screen.rend[y + row_offset] + ov_x;
3588
3589       for (int x = ov_w; x--; )
3590         {
3591           text_t t = *t1; *t1++ = *t2; *t2++ = t;
3592           rend_t r = *r1; *r1++ = *r2; *r2++ = SET_FONT (r, TermWin.fontset->find_font (t, false));
3593         }
3594     }
3595 }
3596
3597 #endif
3598 /* ------------------------------------------------------------------------- */