ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/screen.C
Revision: 1.448
Committed: Thu Oct 30 09:53:05 2014 UTC (9 years, 6 months ago) by sf-exg
Content type: text/plain
Branch: MAIN
Changes since 1.447: +1 -1 lines
Log Message:
Fix optimization in refresh for cells containing a space.

scr_refresh skips refresh for cells containing a space and whose
rendition attributes defining the bg color have not changed. However,
the computation of such attributes erroneously assumes that, if reverse
video is set, the bg color is given by the fg color, which is not true
in the case where bg == fg. This causes cells with a space and bg == fg
not being refreshed also when the reverse video attribute has changed,
as observed in red hat bug #830236. Fix the bug by amending the
attributes test so that the refresh of such a cell is performed if the
reverse video attribute has changed.

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