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