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