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