ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/screen.C
Revision: 1.457
Committed: Tue Jun 21 12:03:56 2016 UTC (7 years, 11 months ago) by sf-exg
Content type: text/plain
Branch: MAIN
Changes since 1.456: +1 -1 lines
Log Message:
24-bit direct color support (patch by Fengguang Wu)

Support directly setting RGB fg/bg colors via ISO-8613-3 24-bit
ANSI color escapes:

  ESC[38;2;<r>;<g>;<b>m Select RGB foreground color
  ESC[48;2;<r>;<g>;<b>m Select RGB background color

The killer applications for me are vim in tmux. It'll not only modernize
their look and feeling, but also bring more eye friendly color schemes.
Very helpful for long time programmers.

To avoid memory overheads and keep the patch non-intrusive, it takes the
approach to adapt the nearest color in an hidden 6x6x4 (88-color mode)
or 7x7x5 (256-color mode) color cube to the new 24-bit RGB color.

The pros/cons are:

+) least memory footprint (close to 0)
   comparing to konsole, gnome-terminal etc. real 24-bit arrays

+) exact colors and excellent user feelings
   comparing to xterm, mlterm, etc. approximating to 256 palette

+) usable in both the existing 88/256-color modes

   Most vim GUI color schemes show up the same as gvim in rxvt-unicode's
   88-color mode, not to mention the 256-color mode. Typical applications
   only use one or two dozens of colors at the same time.

-) may not be able to show 2+ close 24-bit colors

   RGB colors close to each other will likely fall into the same slot in
   the 6x6x4 or 7x7x5 color cube. If necessary, it could be improved
   effectively by implementing some collision avoidance logic, trying to
   find empty/eldest slot in the +1/-1 r/g/b indices (ie. 3-8 neighbors).

The CPU overheads of map_rgb24_color() look ignorable: I feel no
perceptible slow down when doing vim operations in 24-bit color mode.

A micro benchmark running a test script from [1]:

% time (for i in {1..100}; do 24-bit-color.sh; done)

vanilla rxvt-unicode
====================
  2.42s user 1.88s system 31% cpu 13.555 total
  2.59s user 1.74s system 31% cpu 13.615 total
  2.46s user 1.85s system 31% cpu 13.631 total

THIS PATCH (adapt hidden color cube to 24-bit)
==============================================
  2.33s user 1.97s system 31% cpu 13.598 total
  2.46s user 1.89s system 31% cpu 13.613 total
  2.51s user 1.82s system 31% cpu 13.556 total

https://github.com/spudowiar/rxvt-unicode (real 24-bit array)
=============================================================
  2.61s user 1.75s system 31% cpu 13.721 total
  2.48s user 1.82s system 31% cpu 13.566 total
  2.60s user 1.76s system 31% cpu 13.631 total

USE_256_COLORS is defined in all the above rxvt-unicode builds.

References:

[1] True Colour (16 million colours) support in various terminal
    applications and terminals
    https://gist.github.com/XVilka/8346728

[2] https://en.wikipedia.org/wiki/ANSI_escape_code#Colors

File Contents

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