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