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