ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/screen.C
Revision: 1.442
Committed: Sat Mar 30 08:47:06 2013 UTC (11 years, 1 month ago) by sf-exg
Content type: text/plain
Branch: MAIN
Changes since 1.441: +8 -0 lines
Log Message:
Fix bug in core double-click word selection.

selection_delimit_word does not account for (left) delimiters with width > 1.
In such a case, it sets the starting position of the selection to the
second column of the delimiter (thereby including the delimiter in the
selection) rather than to the first column of the character next to it.
This bug was masked before by 'selection_make' but a recent fix to the
selection code revealed it.

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 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *--------------------------------------------------------------------------*/
22
23 /*
24 * This file handles _all_ screen updates and selections
25 */
26
27 #include "../config.h" /* NECESSARY */
28 #include "rxvt.h" /* NECESSARY */
29 #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 scr_changeview ((top_row - nrow) * (len - y) / len + (nrow - 1));
1903 }
1904
1905 /* ------------------------------------------------------------------------- */
1906 /*
1907 * Page the screen up/down nlines
1908 * direction should be UP or DN
1909 */
1910 bool
1911 rxvt_term::scr_page (int nlines) NOTHROW
1912 {
1913 return scr_changeview (view_start - nlines);
1914 }
1915
1916 bool
1917 rxvt_term::scr_changeview (int new_view_start) NOTHROW
1918 {
1919 clamp_it (new_view_start, top_row, 0);
1920
1921 if (new_view_start == view_start)
1922 return false;
1923
1924 num_scr += new_view_start - view_start;
1925 view_start = new_view_start;
1926 want_refresh = 1;
1927
1928 HOOK_INVOKE ((this, HOOK_VIEW_CHANGE, DT_INT, view_start, DT_END));
1929
1930 return true;
1931 }
1932
1933 #ifndef NO_BELL
1934 void
1935 rxvt_term::bell_cb (ev::timer &w, int revents)
1936 {
1937 rvideo_bell = false;
1938 scr_rvideo_mode (rvideo_mode);
1939 refresh_check ();
1940 }
1941 #endif
1942
1943 /* ------------------------------------------------------------------------- */
1944 void
1945 rxvt_term::scr_bell () NOTHROW
1946 {
1947 #ifndef NO_BELL
1948
1949 # ifndef NO_MAPALERT
1950 # ifdef MAPALERT_OPTION
1951 if (option (Opt_mapAlert))
1952 # endif
1953 XMapWindow (dpy, parent);
1954 # endif
1955
1956 # if ENABLE_FRILLS
1957 if (option (Opt_urgentOnBell))
1958 set_urgency (1);
1959 # endif
1960
1961 if (option (Opt_visualBell))
1962 {
1963 rvideo_bell = true;
1964 scr_rvideo_mode (rvideo_mode);
1965 flush ();
1966
1967 bell_ev.start (VISUAL_BELL_DURATION);
1968 }
1969 else
1970 XBell (dpy, 0);
1971 HOOK_INVOKE ((this, HOOK_BELL, DT_END));
1972 #endif
1973 }
1974
1975 /* ------------------------------------------------------------------------- */
1976 void ecb_cold
1977 rxvt_term::scr_printscreen (int fullhist) NOTHROW
1978 {
1979 #ifdef PRINTPIPE
1980 int nrows, row_start;
1981 FILE *fd = popen_printer ();
1982
1983 if (!fd)
1984 return;
1985
1986 if (fullhist)
1987 {
1988 nrows = nrow - top_row;
1989 row_start = top_row;
1990 }
1991 else
1992 {
1993 nrows = nrow;
1994 row_start = view_start;
1995 }
1996
1997 wctomb (0, 0);
1998
1999 for (int r1 = row_start; r1 < row_start + nrows; r1++)
2000 {
2001 text_t *tp = ROW(r1).t;
2002 int len = ROW(r1).l;
2003
2004 for (int i = len >= 0 ? len : ncol - 1; i--; ) //TODO//FIXME//LEN
2005 {
2006 char mb[MB_LEN_MAX];
2007 text_t t = *tp++;
2008 if (t == NOCHAR)
2009 continue;
2010
2011 len = wctomb (mb, t);
2012
2013 if (len <= 0)
2014 {
2015 mb[0] = ' ';
2016 len = 1;
2017 }
2018
2019 fwrite (mb, 1, len, fd);
2020 }
2021
2022 fputc ('\n', fd);
2023 }
2024
2025 pclose_printer (fd);
2026 #endif
2027 }
2028
2029 /* ------------------------------------------------------------------------- */
2030 /*
2031 * Refresh the screen
2032 * drawn_text/drawn_rend contain the screen information before the update.
2033 * screen.text/screen.rend contain what the screen will change to.
2034 */
2035 void ecb_hot
2036 rxvt_term::scr_refresh () NOTHROW
2037 {
2038 int16_t col, row, /* column/row we're processing */
2039 ocrow; /* old cursor row */
2040 int i; /* tmp */
2041 rend_t ccol1, /* Cursor colour */
2042 ccol2; /* Cursor colour2 */
2043 rend_t cur_rend;
2044 int cur_col;
2045 int cursorwidth;
2046
2047 want_refresh = 0; /* screen is current */
2048
2049 if (refresh_type == NO_REFRESH || !mapped)
2050 return;
2051
2052 /*
2053 * A: set up vars
2054 */
2055 refresh_count = 0;
2056
2057 unsigned int old_screen_flags = screen.flags;
2058 bool have_bg = 0;
2059 #ifdef HAVE_IMG
2060 have_bg = bg_img != 0;
2061 #endif
2062 ocrow = oldcursor.row; /* is there an old outline cursor on screen? */
2063
2064 /*
2065 * B: reverse any characters which are selected
2066 */
2067 scr_reverse_selection ();
2068
2069 HOOK_INVOKE ((this, HOOK_REFRESH_BEGIN, DT_END));
2070 #if ENABLE_OVERLAY
2071 scr_swap_overlay ();
2072 #endif
2073
2074 bool showcursor = screen.flags & Screen_VisibleCursor;
2075
2076 /*
2077 * C: set the cursor character (s)
2078 */
2079 {
2080 #ifdef CURSOR_BLINK
2081 if (hidden_cursor)
2082 showcursor = 0;
2083 #endif
2084
2085 if (showcursor)
2086 {
2087 int col = screen.cur.col;
2088
2089 while (col && ROW(screen.cur.row).t[col] == NOCHAR)
2090 col--;
2091
2092 cursorwidth = 1;
2093 while (col + cursorwidth < ncol
2094 && ROW(screen.cur.row).t[col + cursorwidth] == NOCHAR)
2095 cursorwidth++;
2096
2097 cur_rend = ROW(screen.cur.row).r[col];
2098 cur_col = col;
2099
2100 #ifndef NO_CURSORCOLOR
2101 if (ISSET_PIXCOLOR (Color_cursor))
2102 ccol1 = Color_cursor;
2103 else
2104 #endif
2105 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2106 ccol1 = fgcolor_of (rstyle);
2107 #else
2108 ccol1 = Color_fg;
2109 #endif
2110
2111 #ifndef NO_CURSORCOLOR
2112 if (ISSET_PIXCOLOR (Color_cursor2))
2113 ccol2 = Color_cursor2;
2114 else
2115 #endif
2116 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2117 ccol2 = bgcolor_of (rstyle);
2118 #else
2119 ccol2 = Color_bg;
2120 #endif
2121
2122 if (focus)
2123 {
2124 rend_t rend = cur_rend;
2125
2126 if (option (Opt_cursorUnderline))
2127 rend ^= RS_Uline;
2128 else
2129 {
2130 rend ^= RS_RVid;
2131 rend = SET_FGCOLOR (rend, ccol1);
2132 rend = SET_BGCOLOR (rend, ccol2);
2133 }
2134
2135 scr_set_char_rend (ROW(screen.cur.row), cur_col, rend);
2136 }
2137 }
2138
2139 /* make sure no outline cursor is left around */
2140 if (ocrow != -1)
2141 {
2142 if (screen.cur.row - view_start != ocrow
2143 || screen.cur.col != oldcursor.col)
2144 {
2145 if (ocrow < nrow
2146 && oldcursor.col < ncol)
2147 drawn_buf[ocrow].r[oldcursor.col] ^= (RS_RVid | RS_Uline);
2148 }
2149 }
2150
2151 // save the current cursor coordinates if the cursor is visible
2152 // and the window is unfocused, so as to clear the outline cursor
2153 // in the next refresh if the cursor moves
2154 if (showcursor && !focus && screen.cur.row - view_start < nrow)
2155 {
2156 oldcursor.row = screen.cur.row - view_start;
2157 oldcursor.col = screen.cur.col;
2158 }
2159 else
2160 oldcursor.row = -1;
2161 }
2162
2163 #ifndef NO_SLOW_LINK_SUPPORT
2164 /*
2165 * D: CopyArea pass - very useful for slower links
2166 * This has been deliberately kept simple.
2167 */
2168 if (!display->is_local
2169 && refresh_type == FAST_REFRESH && num_scr_allow && num_scr
2170 && abs (num_scr) < nrow && !have_bg)
2171 {
2172 int16_t nits;
2173 int i = num_scr;
2174 int j;
2175 int len, wlen;
2176
2177 j = nrow;
2178 wlen = len = -1;
2179 row = i > 0 ? 0 : j - 1;
2180
2181 for (; j-- >= 0; row += (i > 0 ? 1 : -1))
2182 {
2183 if (row + i >= 0 && row + i < nrow && row + i != ocrow)
2184 {
2185 line_t s = ROW(view_start + row);
2186 line_t d = drawn_buf[row];
2187 line_t d2 = drawn_buf[row + i];
2188
2189 for (nits = 0, col = ncol; col--; )
2190 if (s.t[col] != d2.t[col] || s.r[col] != d2.r[col])
2191 nits--;
2192 else if (s.t[col] != d.t[col] || s.r[col] != d.r[col])
2193 nits++;
2194
2195 if (nits > 8) /* XXX: arbitrary choice */
2196 {
2197 for (col = ncol; col--; )
2198 {
2199 *d.t++ = *d2.t++;
2200 *d.r++ = *d2.r++;
2201 }
2202
2203 if (len == -1)
2204 len = row;
2205
2206 wlen = row;
2207 continue;
2208 }
2209 }
2210
2211 if (len >= 0)
2212 {
2213 /* also comes here at end if needed because of >= above */
2214 if (wlen < len)
2215 ::swap (wlen, len);
2216
2217 XGCValues gcv;
2218
2219 gcv.graphics_exposures = 1; XChangeGC (dpy, gc, GCGraphicsExposures, &gcv);
2220 XCopyArea (dpy, vt, vt,
2221 gc, 0, Row2Pixel (len + i),
2222 (unsigned int)this->width,
2223 (unsigned int)Height2Pixel (wlen - len + 1),
2224 0, Row2Pixel (len));
2225 gcv.graphics_exposures = 0; XChangeGC (dpy, gc, GCGraphicsExposures, &gcv);
2226
2227 len = -1;
2228 }
2229 }
2230 }
2231 #endif
2232
2233 /*
2234 * E: main pass across every character
2235 */
2236 for (row = 0; row < nrow; row++)
2237 {
2238 text_t *stp = ROW(view_start + row).t;
2239 rend_t *srp = ROW(view_start + row).r;
2240 text_t *dtp = drawn_buf[row].t;
2241 rend_t *drp = drawn_buf[row].r;
2242
2243 /*
2244 * E2: OK, now the real pass
2245 */
2246 int ypixel = (int)Row2Pixel (row);
2247
2248 for (col = 0; col < ncol; col++)
2249 {
2250 /* compare new text with old - if exactly the same then continue */
2251 if (stp[col] == dtp[col] /* Must match characters to skip. */
2252 && (RS_SAME (srp[col], drp[col]) /* Either rendition the same or */
2253 || (stp[col] == ' ' /* space w/ no background change */
2254 && GET_BGATTR (srp[col]) == GET_BGATTR (drp[col]))))
2255 continue;
2256
2257 // redraw one or more characters
2258
2259 // seek to the beginning of wide characters
2260 while (ecb_unlikely (stp[col] == NOCHAR && col > 0))
2261 --col;
2262
2263 rend_t rend = srp[col]; /* screen rendition (target rendition) */
2264 text_t *text = stp + col;
2265 int count = 1;
2266
2267 dtp[col] = stp[col];
2268 drp[col] = rend;
2269
2270 int xpixel = Col2Pixel (col);
2271
2272 for (i = 0; ++col < ncol; )
2273 {
2274 if (stp[col] == NOCHAR)
2275 {
2276 dtp[col] = stp[col];
2277 drp[col] = srp[col];
2278
2279 count++;
2280 i++;
2281
2282 continue;
2283 }
2284
2285 if (!RS_SAME (rend, srp[col]))
2286 break;
2287
2288 count++;
2289
2290 if (stp[col] != dtp[col]
2291 || !RS_SAME (srp[col], drp[col]))
2292 {
2293 if (have_bg && (i++ > count / 2))
2294 break;
2295
2296 dtp[col] = stp[col];
2297 drp[col] = rend;
2298 i = 0;
2299 }
2300 else if (have_bg || (stp[col] != ' ' && ++i >= 16))
2301 break;
2302 }
2303
2304 col--; /* went one too far. move back */
2305 count -= i; /* dump any matching trailing chars */
2306
2307 // sometimes we optimize away the trailing NOCHAR's, add them back
2308 while (ecb_unlikely (i && text[count] == NOCHAR))
2309 count++, i--;
2310
2311 /*
2312 * Determine the attributes for the string
2313 */
2314 int fore = fgcolor_of (rend); // desired foreground
2315 int back = bgcolor_of (rend); // desired background
2316
2317 // only do special processing if any attributes are set, which is unlikely
2318 if (ecb_unlikely (rend & (RS_baseattrMask | RS_Careful | RS_Sel)))
2319 {
2320 bool invert = rend & RS_RVid;
2321
2322 #ifndef NO_BOLD_UNDERLINE_REVERSE
2323 if (rend & RS_Bold && fore == Color_fg)
2324 {
2325 if (ISSET_PIXCOLOR (Color_BD))
2326 fore = Color_BD;
2327 # if !ENABLE_STYLES
2328 else
2329 invert = !invert;
2330 # endif
2331 }
2332
2333 if (rend & RS_Italic && fore == Color_fg)
2334 {
2335 if (ISSET_PIXCOLOR (Color_IT))
2336 fore = Color_IT;
2337 # if !ENABLE_STYLES
2338 else
2339 invert = !invert;
2340 # endif
2341 }
2342
2343 if (rend & RS_Uline && fore == Color_fg && ISSET_PIXCOLOR (Color_UL))
2344 fore = Color_UL;
2345 #endif
2346
2347 #ifdef OPTION_HC
2348 if (rend & RS_Sel)
2349 {
2350 /* invert the column if no highlightColor is set or it is the
2351 * current cursor column */
2352 if (!(showcursor && row == screen.cur.row && text - stp == screen.cur.col)
2353 && ISSET_PIXCOLOR (Color_HC))
2354 {
2355 if (ISSET_PIXCOLOR (Color_HTC))
2356 fore = Color_HTC;
2357 // if invert is 0 reverse video is set so we use bg color as fg color
2358 else if (!invert)
2359 fore = back;
2360
2361 back = Color_HC;
2362 invert = 0;
2363 }
2364 }
2365 #endif
2366
2367 if (invert)
2368 {
2369 ::swap (fore, back);
2370
2371 #ifndef NO_BOLD_UNDERLINE_REVERSE
2372 if (fore == back)
2373 {
2374 fore = Color_bg;
2375 back = Color_fg;
2376 }
2377 #endif
2378 }
2379
2380 #ifdef TEXT_BLINK
2381 if (rend & RS_Blink && (back == Color_bg || fore == Color_bg))
2382 {
2383 if (!text_blink_ev.is_active ())
2384 {
2385 text_blink_ev.again ();
2386 hidden_text = 0;
2387 }
2388 else if (hidden_text)
2389 fore = back;
2390 }
2391 #endif
2392
2393 #if ENABLE_STYLES
2394 // "careful" (too wide) character handling
2395
2396 // include previous careful character(s) if possible, looks nicer (best effort...)
2397 while (text > stp
2398 && srp[text - stp - 1] & RS_Careful
2399 && RS_SAME (rend, srp[text - stp - 1]))
2400 text--, count++, xpixel -= fwidth;
2401
2402 // force redraw after "careful" characters to avoid pixel droppings
2403 for (int i = 0; srp[col + i] & RS_Careful && col + i < ncol - 1; i++)
2404 drp[col + i + 1] = srp[col + i + 1] ^ RS_redraw;
2405
2406 // force redraw before "careful" characters to avoid pixel droppings
2407 for (int i = 0; srp[text - stp - i] & RS_Careful && text - i > stp; i++)
2408 drp[text - stp - i - 1] = srp[text - stp - i - 1] ^ RS_redraw;
2409 #endif
2410 }
2411
2412 /*
2413 * Actually do the drawing of the string here
2414 */
2415 rxvt_font *font = (*fontset[GET_STYLE (rend)])[GET_FONT (rend)];
2416
2417 if (ecb_likely (have_bg && back == Color_bg))
2418 {
2419 // this is very ugly, maybe push it into ->draw?
2420
2421 for (i = 0; i < count; i++) /* don't draw empty strings */
2422 if (text[i] != ' ')
2423 {
2424 font->draw (*drawable, xpixel, ypixel, text, count, fore, Color_transparent);
2425 goto did_clear;
2426 }
2427
2428 CLEAR_CHARS (xpixel, ypixel, count);
2429 did_clear: ;
2430 }
2431 else
2432 font->draw (*drawable, xpixel, ypixel, text, count, fore, back);
2433
2434 if (ecb_unlikely (rend & RS_Uline && font->descent > 1 && fore != back))
2435 {
2436 if (showcursor && focus && row == screen.cur.row
2437 && IN_RANGE_EXC (col, cur_col, cur_col + cursorwidth))
2438 XSetForeground (dpy, gc, pix_colors[ccol1]);
2439 else
2440 #if ENABLE_FRILLS
2441 if (ISSET_PIXCOLOR (Color_underline))
2442 XSetForeground (dpy, gc, pix_colors[Color_underline]);
2443 else
2444 #endif
2445 XSetForeground (dpy, gc, pix_colors[fore]);
2446
2447 XDrawLine (dpy, vt, gc,
2448 xpixel, ypixel + font->ascent + 1,
2449 xpixel + Width2Pixel (count) - 1, ypixel + font->ascent + 1);
2450 }
2451 } /* for (col....) */
2452 } /* for (row....) */
2453
2454 /*
2455 * G: cleanup cursor and display outline cursor if necessary
2456 */
2457 if (showcursor)
2458 {
2459 if (focus)
2460 scr_set_char_rend (ROW(screen.cur.row), cur_col, cur_rend);
2461 else if (oldcursor.row >= 0)
2462 {
2463 XSetForeground (dpy, gc, pix_colors[ccol1]);
2464
2465 XDrawRectangle (dpy, vt, gc,
2466 Col2Pixel (cur_col),
2467 Row2Pixel (oldcursor.row),
2468 (unsigned int) (Width2Pixel (cursorwidth) - 1),
2469 (unsigned int) (Height2Pixel (1) - 1));
2470 }
2471 }
2472
2473 /*
2474 * H: cleanup selection
2475 */
2476 #if ENABLE_OVERLAY
2477 scr_swap_overlay ();
2478 #endif
2479 HOOK_INVOKE ((this, HOOK_REFRESH_END, DT_END));
2480
2481 scr_reverse_selection ();
2482
2483 screen.flags = old_screen_flags;
2484 num_scr = 0;
2485 num_scr_allow = 1;
2486 }
2487
2488 void ecb_cold
2489 rxvt_term::scr_remap_chars (line_t &l) NOTHROW
2490 {
2491 if (!l.valid ())
2492 return;
2493
2494 l.touch (); // maybe a bit of an overkill, but it's not performance-relevant
2495
2496 for (int i = ncol; i--; )
2497 l.r[i] = SET_FONT (l.r[i], FONTSET (l.r[i])->find_font (l.t[i]));
2498 }
2499
2500 void ecb_cold
2501 rxvt_term::scr_remap_chars () NOTHROW
2502 {
2503 for (int i = total_rows; i--; )
2504 scr_remap_chars (row_buf [i]);
2505
2506 for (int i = nrow; i--; )
2507 {
2508 scr_remap_chars (drawn_buf [i]);
2509 scr_remap_chars (swap_buf [i]);
2510 }
2511 }
2512
2513 void ecb_cold
2514 rxvt_term::scr_recolour (bool refresh) NOTHROW
2515 {
2516 bool transparent = false;
2517
2518 #ifdef HAVE_IMG
2519 if (bg_img != 0)
2520 {
2521 # if ENABLE_TRANSPARENCY
2522 if (bg_flags & BG_IS_TRANSPARENT)
2523 {
2524 XSetWindowBackgroundPixmap (dpy, parent, bg_img->pm);
2525 XSetWindowBackgroundPixmap (dpy, vt, ParentRelative);
2526
2527 transparent = true;
2528 }
2529 else
2530 # endif
2531 {
2532 XSetWindowBackground (dpy, parent, pix_colors[Color_border]);
2533 XSetWindowBackgroundPixmap (dpy, vt, bg_img->pm);
2534 }
2535 }
2536 else
2537 #endif
2538 {
2539 XSetWindowBackground (dpy, parent, pix_colors[Color_border]);
2540 XSetWindowBackground (dpy, vt, pix_colors[Color_bg]);
2541 }
2542
2543 XClearWindow (dpy, parent);
2544
2545 if (scrollBar.win)
2546 {
2547 if (transparent)
2548 XSetWindowBackgroundPixmap (dpy, scrollBar.win, ParentRelative);
2549 else
2550 XSetWindowBackground (dpy, scrollBar.win, pix_colors[scrollBar.color ()]);
2551 scrollBar.state = SB_STATE_IDLE;
2552 scrollBar.show (0);
2553 }
2554
2555 if (refresh)
2556 {
2557 scr_clear ();
2558 scr_touch (true);
2559 }
2560 want_refresh = 1;
2561 }
2562
2563 /* ------------------------------------------------------------------------- */
2564 void
2565 rxvt_term::scr_clear (bool really) NOTHROW
2566 {
2567 if (!mapped)
2568 return;
2569
2570 num_scr_allow = 0;
2571 want_refresh = 1;
2572
2573 if (really)
2574 XClearWindow (dpy, vt);
2575 }
2576
2577 void
2578 rxvt_term::scr_xor_rect (int beg_row, int beg_col, int end_row, int end_col, rend_t rstyle1, rend_t rstyle2) NOTHROW
2579 {
2580 int view_end = view_start + nrow;
2581 int row, col;
2582
2583 for (row = max (beg_row, view_start); row <= min (end_row, view_end); row++)
2584 {
2585 text_t *stp = ROW(row).t;
2586 rend_t *srp = ROW(row).r;
2587
2588 for (col = beg_col; col < end_col; col++)
2589 srp[col] ^= rstyle1;
2590
2591 while (col-- > beg_col && (stp[col] == NOCHAR || unicode::is_space (stp[col])))
2592 srp[col] ^= rstyle2;
2593
2594 if (++col < end_col)
2595 srp[col] ^= rstyle2;
2596 }
2597 }
2598
2599 void
2600 rxvt_term::scr_xor_span (int beg_row, int beg_col, int end_row, int end_col, rend_t rstyle) NOTHROW
2601 {
2602 int view_end = view_start + nrow;
2603 int row, col;
2604
2605 if (beg_row >= view_start)
2606 {
2607 col = beg_col;
2608 row = beg_row;
2609 }
2610 else
2611 {
2612 col = 0;
2613 row = view_start;
2614 }
2615
2616 for (; row < min (end_row, view_end); row++, col = 0)
2617 for (rend_t *srp = ROW(row).r; col < ncol; col++)
2618 srp[col] ^= rstyle;
2619
2620 if (row == end_row)
2621 for (rend_t *srp = ROW(row).r; col < end_col; col++)
2622 srp[col] ^= rstyle;
2623 }
2624
2625 /* ------------------------------------------------------------------------- */
2626 void ecb_hot
2627 rxvt_term::scr_reverse_selection () NOTHROW
2628 {
2629 if (selection.op
2630 && current_screen == selection.screen
2631 && selection.end.row >= view_start)
2632 {
2633 #if !ENABLE_MINIMAL
2634 if (selection.rect)
2635 scr_xor_rect (selection.beg.row, selection.beg.col,
2636 selection.end.row, selection.end.col,
2637 RS_Sel | RS_RVid, RS_Sel | RS_RVid | RS_Uline);
2638 else
2639 #endif
2640 scr_xor_span (selection.beg.row, selection.beg.col,
2641 selection.end.row, selection.end.col,
2642 RS_Sel | RS_RVid);
2643 }
2644 }
2645
2646 /* ------------------------------------------------------------------------- */
2647 /*
2648 * Dump the whole scrollback and screen to the passed filedescriptor. The
2649 * invoking routine must close the fd.
2650 */
2651 #if 0
2652 void
2653 rxvt_term::scr_dump (int fd) NOTHROW
2654 {
2655 // if this method is needed, it can be implemented by factoring the
2656 // relevant code in scr_printscreen
2657 }
2658 #endif
2659
2660 /* ------------------------------------------------------------------------- *
2661 * CHARACTER SELECTION *
2662 * ------------------------------------------------------------------------- */
2663 void
2664 rxvt_term::selection_check (int check_more) NOTHROW
2665 {
2666 if (!selection.op)
2667 return;
2668
2669 if (!IN_RANGE_EXC (selection.beg.row, top_row, nrow)
2670 || !IN_RANGE_EXC (selection.mark.row, top_row, nrow)
2671 || !IN_RANGE_EXC (selection.end.row, top_row, nrow)
2672 || (check_more == 1
2673 && current_screen == selection.screen
2674 && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
2675 && ROWCOL_IS_BEFORE (screen.cur, selection.end)))
2676 CLEAR_ALL_SELECTION ();
2677 }
2678
2679 void
2680 rxvt_term::selection_changed () NOTHROW
2681 {
2682 line_t &r1 = ROW (selection.beg.row);
2683 while (selection.beg.col > 0 && r1.t [selection.beg.col] == NOCHAR)
2684 --selection.beg.col;
2685
2686 line_t &r2 = ROW (selection.end.row);
2687 while (selection.end.col < r2.l && r2.t [selection.end.col] == NOCHAR)
2688 ++selection.end.col;
2689
2690 want_refresh = 1;
2691 }
2692
2693 /* ------------------------------------------------------------------------- */
2694 /*
2695 * Paste a selection direct to the command fd
2696 */
2697 void
2698 rxvt_term::tt_paste (char *data, unsigned int len) NOTHROW
2699 {
2700 /* convert normal newline chars into common keyboard Return key sequence */
2701 for (unsigned int i = 0; i < len; i++)
2702 if (data[i] == C0_LF)
2703 data[i] = C0_CR;
2704
2705 if (priv_modes & PrivMode_BracketPaste)
2706 tt_printf ("\x1b[200~");
2707
2708 tt_write (data, len);
2709
2710 if (priv_modes & PrivMode_BracketPaste)
2711 tt_printf ("\x1b[201~");
2712 }
2713
2714 void
2715 rxvt_term::paste (char *data, unsigned int len) NOTHROW
2716 {
2717 if (HOOK_INVOKE ((this, HOOK_TT_PASTE, DT_STR_LEN, data, len, DT_END)))
2718 return;
2719
2720 tt_paste (data, len);
2721 }
2722
2723 /* ------------------------------------------------------------------------- */
2724 /*
2725 * Request PRIMARY, SECONDARY or CLIPBOARD selection.
2726 * if the requested selection has no owner or is empty CUT_BUFFER0 is used
2727 * as fallback
2728 * EXT: button 2 release
2729 */
2730 void
2731 rxvt_term::selection_request (Time tm, int selnum) NOTHROW
2732 {
2733 if (!selection_req)
2734 {
2735 selection_req = new rxvt_selection (display, selnum, tm, vt, xa[XA_VT_SELECTION], this);
2736 selection_req->run ();
2737 }
2738 }
2739
2740 /* ------------------------------------------------------------------------- */
2741 /*
2742 * Clear all selected text
2743 * EXT: SelectionClear
2744 */
2745 void
2746 rxvt_term::selection_clear (bool clipboard) NOTHROW
2747 {
2748 if (!clipboard)
2749 {
2750 want_refresh = 1;
2751 free (selection.text);
2752 selection.text = NULL;
2753 selection.len = 0;
2754 CLEAR_SELECTION ();
2755
2756 if (display->selection_owner == this)
2757 display->selection_owner = 0;
2758 }
2759 else
2760 {
2761 free (selection.clip_text);
2762 selection.clip_text = NULL;
2763 selection.clip_len = 0;
2764
2765 if (display->clipboard_owner == this)
2766 display->clipboard_owner = 0;
2767 }
2768 }
2769
2770 /* ------------------------------------------------------------------------- */
2771 /*
2772 * Copy a selection into the cut buffer
2773 * EXT: button 1 or 3 release
2774 */
2775 void
2776 rxvt_term::selection_make (Time tm)
2777 {
2778 int size;
2779 wchar_t *new_selection_text;
2780 text_t *t;
2781
2782 switch (selection.op)
2783 {
2784 case SELECTION_CONT:
2785 break;
2786 case SELECTION_INIT:
2787 CLEAR_SELECTION ();
2788 /* FALLTHROUGH */
2789 case SELECTION_BEGIN:
2790 selection.op = SELECTION_DONE;
2791 /* FALLTHROUGH */
2792 default:
2793 return;
2794 }
2795
2796 selection.op = SELECTION_DONE;
2797
2798 if (selection.clicks == 4)
2799 return; /* nothing selected, go away */
2800
2801 if (HOOK_INVOKE ((this, HOOK_SEL_MAKE, DT_LONG, (long)tm, DT_END)))
2802 return;
2803
2804 size = (selection.end.row - selection.beg.row + 1) * (ncol + 1);
2805 new_selection_text = (wchar_t *)rxvt_malloc ((size + 4) * sizeof (wchar_t));
2806
2807 int ofs = 0;
2808 int extra = 0;
2809
2810 int col = selection.beg.col;
2811 int row = selection.beg.row;
2812
2813 int end_col;
2814
2815 for (; row <= selection.end.row; row++, col = 0)
2816 {
2817 #if !ENABLE_MINIMAL
2818 if (selection.rect)
2819 {
2820 col = selection.beg.col;
2821 end_col = selection.end.col;
2822 }
2823 else
2824 #endif
2825 end_col = ROW(row).l;
2826
2827 col = max (col, 0);
2828
2829 if (row == selection.end.row)
2830 min_it (end_col, selection.end.col);
2831
2832 t = ROW(row).t + col;
2833
2834 for (; col < end_col; col++)
2835 {
2836 if (*t == NOCHAR)
2837 t++;
2838 #if ENABLE_COMBINING
2839 else if (IS_COMPOSE (*t))
2840 {
2841 int len = rxvt_composite.expand (*t, 0);
2842
2843 extra -= (len - 1);
2844
2845 if (extra < 0)
2846 {
2847 extra += size;
2848 size += size;
2849 new_selection_text = (wchar_t *)rxvt_realloc (new_selection_text, (size + 4) * sizeof (wchar_t));
2850 }
2851
2852 ofs += rxvt_composite.expand (*t++, new_selection_text + ofs);
2853 }
2854 #endif
2855 else
2856 new_selection_text[ofs++] = *t++;
2857 }
2858
2859 #if !ENABLE_MINIMAL
2860 if (selection.rect)
2861 {
2862 while (ofs
2863 && new_selection_text[ofs - 1] != C0_LF
2864 && unicode::is_space (new_selection_text[ofs - 1]))
2865 --ofs;
2866
2867 new_selection_text[ofs++] = C0_LF;
2868 }
2869 else
2870 #endif
2871 if (!ROW(row).is_longer ()
2872 && (row != selection.end.row || end_col != selection.end.col)
2873 && (row != selection.beg.row || selection.beg.col < ncol))
2874 new_selection_text[ofs++] = C0_LF;
2875 }
2876
2877 new_selection_text[ofs] = 0;
2878
2879 if (ofs == 0)
2880 {
2881 free (new_selection_text);
2882 return;
2883 }
2884
2885 free (selection.text);
2886
2887 // we usually allocate much more than necessary, so realloc it smaller again
2888 selection.len = ofs;
2889 selection.text = (wchar_t *)rxvt_realloc (new_selection_text, (ofs + 1) * sizeof (wchar_t));
2890
2891 if (HOOK_INVOKE ((this, HOOK_SEL_GRAB, DT_LONG, (long)tm, DT_END)))
2892 return;
2893
2894 selection_grab (tm);
2895 }
2896
2897 bool
2898 rxvt_term::selection_grab (Time tm, bool clipboard) NOTHROW
2899 {
2900 Atom sel;
2901
2902 if (!clipboard)
2903 {
2904 selection_time = tm;
2905 sel = XA_PRIMARY;
2906 }
2907 else
2908 {
2909 clipboard_time = tm;
2910 sel = xa[XA_CLIPBOARD];
2911 }
2912
2913 XSetSelectionOwner (dpy, sel, vt, tm);
2914 if (XGetSelectionOwner (dpy, sel) == vt)
2915 {
2916 display->set_selection_owner (this, clipboard);
2917 return true;
2918 }
2919 else
2920 {
2921 selection_clear (clipboard);
2922 return false;
2923 }
2924
2925 #if 0
2926 XTextProperty ct;
2927
2928 if (XwcTextListToTextProperty (dpy, &selection.text, 1, XStringStyle, &ct) >= 0)
2929 {
2930 set_string_property (XA_CUT_BUFFER0, ct.value, ct.nitems);
2931 XFree (ct.value);
2932 }
2933 #endif
2934 }
2935
2936 /* ------------------------------------------------------------------------- */
2937 /*
2938 * Mark or select text based upon number of clicks: 1, 2, or 3
2939 * EXT: button 1 press
2940 */
2941 void ecb_cold
2942 rxvt_term::selection_click (int clicks, int x, int y) NOTHROW
2943 {
2944 clicks = ((clicks - 1) % 3) + 1;
2945 selection.clicks = clicks; /* save clicks so extend will work */
2946
2947 if (clicks == 2 && !selection.rect
2948 && HOOK_INVOKE ((this, HOOK_SEL_EXTEND, DT_END)))
2949 {
2950 MEvent.clicks = 1; // what a mess
2951 selection.screen = current_screen;
2952 selection.op = SELECTION_CONT;
2953 return;
2954 }
2955
2956 selection_start_colrow (Pixel2Col (x), Pixel2Row (y));
2957
2958 if (clicks == 2 || clicks == 3)
2959 selection_extend_colrow (selection.mark.col,
2960 selection.mark.row - view_start,
2961 0, /* button 3 */
2962 1, /* button press */
2963 0); /* click change */
2964 }
2965
2966 /* ------------------------------------------------------------------------- */
2967 /*
2968 * Mark a selection at the specified col/row
2969 */
2970 void ecb_cold
2971 rxvt_term::selection_start_colrow (int col, int row) NOTHROW
2972 {
2973 want_refresh = 1;
2974
2975 selection.mark.row = row + view_start;
2976 selection.mark.col = col;
2977
2978 selection.mark.row = clamp (selection.mark.row, top_row, nrow - 1);
2979 selection.mark.col = clamp (selection.mark.col, 0, ncol - 1);
2980
2981 while (selection.mark.col > 0
2982 && ROW(selection.mark.row).t[selection.mark.col] == NOCHAR)
2983 --selection.mark.col;
2984
2985 if (selection.op)
2986 {
2987 /* clear the old selection */
2988 selection.beg.row = selection.end.row = selection.mark.row;
2989 selection.beg.col = selection.end.col = selection.mark.col;
2990 }
2991
2992 selection.op = SELECTION_INIT;
2993 selection.screen = current_screen;
2994 }
2995
2996 /* ------------------------------------------------------------------------- */
2997 /*
2998 * Word select: select text for 2 clicks
2999 * We now only find out the boundary in one direction
3000 */
3001
3002 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
3003 #define DELIMIT_TEXT(x) \
3004 (unicode::is_space (x) ? 2 : (x) <= 0xff && !!strchr (rs[Rs_cutchars], (x)))
3005 #define DELIMIT_REND(x) 1
3006
3007 void ecb_cold
3008 rxvt_term::selection_delimit_word (enum page_dirn dirn, const row_col_t *mark, row_col_t *ret) NOTHROW
3009 {
3010 int col, row, dirnadd, tcol, trow, w1, w2;
3011 row_col_t bound;
3012 text_t *stp;
3013 rend_t *srp;
3014
3015 if (dirn == UP)
3016 {
3017 bound.row = top_row - 1;
3018 bound.col = 0;
3019 dirnadd = -1;
3020 }
3021 else
3022 {
3023 bound.row = nrow;
3024 bound.col = ncol - 1;
3025 dirnadd = 1;
3026 }
3027
3028 row = mark->row;
3029 col = max (mark->col, 0);
3030
3031 /* find the edge of a word */
3032 stp = ROW(row).t + col; w1 = DELIMIT_TEXT (*stp);
3033 srp = ROW(row).r + col; w2 = DELIMIT_REND (*srp);
3034
3035 for (;;)
3036 {
3037 for (; col != bound.col; col += dirnadd)
3038 {
3039 stp += dirnadd;
3040 srp += dirnadd;
3041
3042 if (*stp == NOCHAR)
3043 continue;
3044
3045 if (DELIMIT_TEXT (*stp) != w1)
3046 break;
3047 if (DELIMIT_REND (*srp) != w2)
3048 break;
3049 }
3050
3051 if ((col == bound.col) && (row != bound.row))
3052 {
3053 if (ROW(row - (dirn == UP ? 1 : 0)).is_longer ())
3054 {
3055 trow = row + dirnadd;
3056 tcol = dirn == UP ? ncol - 1 : 0;
3057
3058 if (!ROW(trow).t)
3059 break;
3060
3061 stp = ROW(trow).t + tcol;
3062 srp = ROW(trow).r + tcol;
3063
3064 if (DELIMIT_TEXT (*stp) != w1 || DELIMIT_REND (*srp) != w2)
3065 break;
3066
3067 row = trow;
3068 col = tcol;
3069 continue;
3070 }
3071 }
3072 break;
3073 }
3074
3075 if (dirn == DN)
3076 col++; /* put us on one past the end */
3077 else
3078 {
3079 // if the delimiter on the left has width > 1 col points to the
3080 // first NOCHAR in the delimiter cell and so we must move it to the
3081 // beginning of the next cell.
3082 while (ROW(row).t[col] == NOCHAR)
3083 col++;
3084 }
3085
3086 /* Poke the values back in */
3087 ret->row = row;
3088 ret->col = col;
3089 }
3090
3091 /* ------------------------------------------------------------------------- */
3092 /*
3093 * Extend the selection to the specified x/y pixel location
3094 * EXT: button 3 press; button 1 or 3 drag
3095 * flag == 0 ==> button 1
3096 * flag == 1 ==> button 3 press
3097 * flag == 2 ==> button 3 motion
3098 */
3099 void ecb_cold
3100 rxvt_term::selection_extend (int x, int y, int flag) NOTHROW
3101 {
3102 int col = clamp (Pixel2Col (x), 0, ncol);
3103 int row = clamp (Pixel2Row (y), 0, nrow - 1);
3104
3105 /*
3106 * If we're selecting characters (single click) then we must check first
3107 * if we are at the same place as the original mark. If we are then
3108 * select nothing. Otherwise, if we're to the right of the mark, you have to
3109 * be _past_ a character for it to be selected.
3110 */
3111 if (((selection.clicks % 3) == 1) && !flag
3112 && (col == selection.mark.col
3113 && (row == selection.mark.row - view_start)))
3114 {
3115 /* select nothing */
3116 selection.beg.row = selection.end.row = 0;
3117 selection.beg.col = selection.end.col = 0;
3118 selection.clicks = 4;
3119 want_refresh = 1;
3120 return;
3121 }
3122
3123 if (selection.clicks == 4)
3124 selection.clicks = 1;
3125
3126 selection_extend_colrow (col, row, !!flag, /* ? button 3 */
3127 flag == 1 ? 1 : 0, /* ? button press */
3128 0); /* no click change */
3129 }
3130
3131 /* ------------------------------------------------------------------------- */
3132 /*
3133 * Extend the selection to the specified col/row
3134 */
3135 void ecb_cold
3136 rxvt_term::selection_extend_colrow (int32_t col, int32_t row, int button3, int buttonpress, int clickchange) NOTHROW
3137 {
3138 row_col_t pos;
3139 enum {
3140 LEFT, RIGHT
3141 } closeto = RIGHT;
3142
3143 switch (selection.op)
3144 {
3145 case SELECTION_INIT:
3146 CLEAR_SELECTION ();
3147 selection.op = SELECTION_BEGIN;
3148 /* FALLTHROUGH */
3149 case SELECTION_BEGIN:
3150 if (row != selection.mark.row || col != selection.mark.col
3151 || (!button3 && buttonpress))
3152 selection.op = SELECTION_CONT;
3153 break;
3154 case SELECTION_DONE:
3155 selection.op = SELECTION_CONT;
3156 /* FALLTHROUGH */
3157 case SELECTION_CONT:
3158 break;
3159 case SELECTION_CLEAR:
3160 selection_start_colrow (col, row);
3161 /* FALLTHROUGH */
3162 default:
3163 return;
3164 }
3165
3166 if (selection.beg.col == selection.end.col
3167 && selection.beg.col != selection.mark.col
3168 && selection.beg.row == selection.end.row
3169 && selection.beg.row != selection.mark.row)
3170 {
3171 selection.beg.col = selection.end.col = selection.mark.col;
3172 selection.beg.row = selection.end.row = selection.mark.row;
3173 }
3174
3175 pos.col = col;
3176 pos.row = view_start + row;
3177
3178 /*
3179 * This is mainly xterm style selection with a couple of differences, mainly
3180 * in the way button3 drag extension works.
3181 * We're either doing: button1 drag; button3 press; or button3 drag
3182 * a) button1 drag : select around a midpoint/word/line - that point/word/line
3183 * is always at the left/right edge of the selection.
3184 * b) button3 press: extend/contract character/word/line at whichever edge of
3185 * the selection we are closest to.
3186 * c) button3 drag : extend/contract character/word/line - we select around
3187 * a point/word/line which is either the start or end of the selection
3188 * and it was decided by whichever point/word/line was `fixed' at the
3189 * time of the most recent button3 press
3190 */
3191 if (button3 && buttonpress)
3192 {
3193 /* button3 press */
3194 /*
3195 * first determine which edge of the selection we are closest to
3196 */
3197 if (ROWCOL_IS_BEFORE (pos, selection.beg)
3198 || (!ROWCOL_IS_AFTER (pos, selection.end)
3199 && (((pos.col - selection.beg.col)
3200 + ((pos.row - selection.beg.row) * ncol))
3201 < ((selection.end.col - pos.col)
3202 + ((selection.end.row - pos.row) * ncol)))))
3203 closeto = LEFT;
3204
3205 if (closeto == LEFT)
3206 {
3207 selection.beg.row = pos.row;
3208 selection.beg.col = pos.col;
3209 selection.mark.row = selection.end.row;
3210 selection.mark.col = selection.end.col - (selection.clicks == 2);
3211 }
3212 else
3213 {
3214 selection.end.row = pos.row;
3215 selection.end.col = pos.col;
3216 selection.mark.row = selection.beg.row;
3217 selection.mark.col = selection.beg.col;
3218 }
3219 }
3220 else
3221 {
3222 /* button1 drag or button3 drag */
3223 if (ROWCOL_IS_AFTER (selection.mark, pos))
3224 {
3225 if (selection.mark.row == selection.end.row
3226 && selection.mark.col == selection.end.col
3227 && clickchange
3228 && selection.clicks == 2)
3229 selection.mark.col--;
3230
3231 selection.beg.row = pos.row;
3232 selection.beg.col = pos.col;
3233 selection.end.row = selection.mark.row;
3234 selection.end.col = selection.mark.col + (selection.clicks == 2);
3235 }
3236 else
3237 {
3238 selection.beg.row = selection.mark.row;
3239 selection.beg.col = selection.mark.col;
3240 selection.end.row = pos.row;
3241 selection.end.col = pos.col;
3242 }
3243 }
3244
3245 if (selection.clicks == 1)
3246 {
3247 if (selection.beg.col > ROW(selection.beg.row).l //TODO//FIXME//LEN
3248 && !ROW(selection.beg.row).is_longer ()
3249 #if !ENABLE_MINIMAL
3250 && !selection.rect
3251 #endif
3252 )
3253 selection.beg.col = ncol;
3254
3255 if (
3256 selection.end.col > ROW(selection.end.row).l //TODO//FIXME//LEN
3257 && !ROW(selection.end.row).is_longer ()
3258 #if !ENABLE_MINIMAL
3259 && !selection.rect
3260 #endif
3261 )
3262 selection.end.col = ncol;
3263 }
3264 else if (selection.clicks == 2)
3265 {
3266 if (ROWCOL_IS_AFTER (selection.end, selection.beg))
3267 selection.end.col--;
3268
3269 selection_delimit_word (UP, &selection.beg, &selection.beg);
3270 selection_delimit_word (DN, &selection.end, &selection.end);
3271 }
3272 else if (selection.clicks == 3)
3273 {
3274 #if ENABLE_FRILLS
3275 if (option (Opt_tripleclickwords))
3276 {
3277 selection_delimit_word (UP, &selection.beg, &selection.beg);
3278
3279 for (int end_row = selection.mark.row; end_row < nrow; end_row++)
3280 {
3281 if (!ROW(end_row).is_longer ())
3282 {
3283 selection.end.row = end_row;
3284 selection.end.col = ROW(end_row).l;
3285 # if !ENABLE_MINIMAL
3286 selection_remove_trailing_spaces ();
3287 # endif
3288 break;
3289 }
3290 }
3291 }
3292 else
3293 #endif
3294 {
3295 if (ROWCOL_IS_AFTER (selection.mark, selection.beg))
3296 selection.mark.col++;
3297
3298 selection.beg.col = 0;
3299 selection.end.col = ncol;
3300
3301 // select a complete logical line
3302 while (selection.beg.row > -saveLines
3303 && ROW(selection.beg.row - 1).is_longer ())
3304 selection.beg.row--;
3305
3306 while (selection.end.row < nrow
3307 && ROW(selection.end.row).is_longer ())
3308 selection.end.row++;
3309 }
3310 }
3311
3312 if (button3 && buttonpress)
3313 {
3314 /* mark may need to be changed */
3315 if (closeto == LEFT)
3316 {
3317 selection.mark.row = selection.end.row;
3318 selection.mark.col = selection.end.col - (selection.clicks == 2);
3319 }
3320 else
3321 {
3322 selection.mark.row = selection.beg.row;
3323 selection.mark.col = selection.beg.col;
3324 }
3325 }
3326
3327 #if !ENABLE_MINIMAL
3328 if (selection.rect && selection.beg.col > selection.end.col)
3329 ::swap (selection.beg.col, selection.end.col);
3330 #endif
3331
3332 selection_changed ();
3333 }
3334
3335 #if !ENABLE_MINIMAL
3336 void ecb_cold
3337 rxvt_term::selection_remove_trailing_spaces () NOTHROW
3338 {
3339 int32_t end_col, end_row;
3340 text_t *stp;
3341
3342 end_col = selection.end.col;
3343 end_row = selection.end.row;
3344
3345 for (; end_row >= selection.beg.row; )
3346 {
3347 stp = ROW(end_row).t;
3348
3349 while (--end_col >= 0)
3350 {
3351 if (stp[end_col] != NOCHAR
3352 && !unicode::is_space (stp[end_col]))
3353 break;
3354 }
3355
3356 if (end_col >= 0
3357 || !ROW(end_row - 1).is_longer ())
3358 {
3359 selection.end.col = end_col + 1;
3360 selection.end.row = end_row;
3361 break;
3362 }
3363
3364 end_row--;
3365 end_col = ncol;
3366 }
3367
3368 if (selection.mark.row > selection.end.row)
3369 {
3370 selection.mark.row = selection.end.row;
3371 selection.mark.col = selection.end.col;
3372 }
3373 else if (selection.mark.row == selection.end.row
3374 && selection.mark.col > selection.end.col)
3375 selection.mark.col = selection.end.col;
3376 }
3377 #endif
3378
3379 /* ------------------------------------------------------------------------- */
3380 /*
3381 * Double click on button 3 when already selected
3382 * EXT: button 3 double click
3383 */
3384 void ecb_cold
3385 rxvt_term::selection_rotate (int x, int y) NOTHROW
3386 {
3387 selection.clicks = selection.clicks % 3 + 1;
3388 selection_extend_colrow (Pixel2Col (x), Pixel2Row (y), 1, 0, 1);
3389 }
3390
3391 /* ------------------------------------------------------------------------- */
3392 /*
3393 * Respond to a request for our current selection
3394 * EXT: SelectionRequest
3395 */
3396 void ecb_cold
3397 rxvt_term::selection_send (const XSelectionRequestEvent &rq) NOTHROW
3398 {
3399 Atom property = rq.property == None ? rq.target : rq.property;
3400 XSelectionEvent ev;
3401
3402 ev.type = SelectionNotify;
3403 ev.property = None;
3404 ev.display = rq.display;
3405 ev.requestor = rq.requestor;
3406 ev.selection = rq.selection;
3407 ev.target = rq.target;
3408 ev.time = rq.time;
3409
3410 if (rq.target == xa[XA_TARGETS])
3411 {
3412 Atom target_list[6];
3413 Atom *target = target_list;
3414
3415 *target++ = xa[XA_TARGETS];
3416 *target++ = xa[XA_TIMESTAMP];
3417 *target++ = XA_STRING;
3418 *target++ = xa[XA_TEXT];
3419 *target++ = xa[XA_COMPOUND_TEXT];
3420 #if X_HAVE_UTF8_STRING
3421 *target++ = xa[XA_UTF8_STRING];
3422 #endif
3423
3424 XChangeProperty (dpy, rq.requestor, property, XA_ATOM,
3425 32, PropModeReplace,
3426 (unsigned char *)target_list, target - target_list);
3427 ev.property = property;
3428 }
3429 #if TODO // TODO
3430 else if (rq.target == xa[XA_MULTIPLE])
3431 {
3432 /* TODO: Handle MULTIPLE */
3433 }
3434 #endif
3435 else if (rq.target == xa[XA_TIMESTAMP] && rq.selection == XA_PRIMARY && selection.text)
3436 {
3437 XChangeProperty (dpy, rq.requestor, property, rq.target,
3438 32, PropModeReplace, (unsigned char *)&selection_time, 1);
3439 ev.property = property;
3440 }
3441 else if (rq.target == xa[XA_TIMESTAMP] && rq.selection == xa[XA_CLIPBOARD] && selection.clip_text)
3442 {
3443 XChangeProperty (dpy, rq.requestor, property, rq.target,
3444 32, PropModeReplace, (unsigned char *)&clipboard_time, 1);
3445 ev.property = property;
3446 }
3447 else if (rq.target == XA_STRING
3448 || rq.target == xa[XA_TEXT]
3449 || rq.target == xa[XA_COMPOUND_TEXT]
3450 || rq.target == xa[XA_UTF8_STRING]
3451 )
3452 {
3453 XTextProperty ct;
3454 Atom target = rq.target;
3455 short freect = 0;
3456 int selectlen;
3457 wchar_t *cl;
3458 enum {
3459 enc_string = XStringStyle,
3460 enc_text = XStdICCTextStyle,
3461 enc_compound_text = XCompoundTextStyle,
3462 #ifdef X_HAVE_UTF8_STRING
3463 enc_utf8 = XUTF8StringStyle,
3464 #else
3465 enc_utf8 = -1,
3466 #endif
3467 } style;
3468
3469 if (target == XA_STRING)
3470 // we actually don't do XA_STRING, but who cares, as i18n clients
3471 // will ask for another format anyways.
3472 style = enc_string;
3473 else if (target == xa[XA_TEXT])
3474 style = enc_text;
3475 else if (target == xa[XA_COMPOUND_TEXT])
3476 style = enc_compound_text;
3477 #if !ENABLE_MINIMAL
3478 else if (target == xa[XA_UTF8_STRING])
3479 style = enc_utf8;
3480 #endif
3481 else
3482 {
3483 target = xa[XA_COMPOUND_TEXT];
3484 style = enc_compound_text;
3485 }
3486
3487 if (rq.selection == XA_PRIMARY && selection.text)
3488 {
3489 cl = selection.text;
3490 selectlen = selection.len;
3491 }
3492 else if (rq.selection == xa[XA_CLIPBOARD] && selection.clip_text)
3493 {
3494 cl = selection.clip_text;
3495 selectlen = selection.clip_len;
3496 }
3497 else
3498 {
3499 cl = L"";
3500 selectlen = 0;
3501 }
3502
3503 #if !ENABLE_MINIMAL
3504 // xlib is horribly broken with respect to UTF8_STRING, and nobody cares to fix it
3505 // so recode it manually
3506 if (style == enc_utf8)
3507 {
3508 freect = 1;
3509 ct.encoding = target;
3510 ct.format = 8;
3511 ct.value = (unsigned char *)rxvt_wcstoutf8 (cl, selectlen);
3512 ct.nitems = strlen ((char *)ct.value);
3513 }
3514 else
3515 #endif
3516 if (XwcTextListToTextProperty (dpy, &cl, 1, (XICCEncodingStyle) style, &ct) >= 0)
3517 freect = 1;
3518 else
3519 {
3520 /* if we failed to convert then send it raw */
3521 ct.value = (unsigned char *)cl;
3522 ct.nitems = selectlen;
3523 ct.encoding = target;
3524 }
3525
3526 XChangeProperty (dpy, rq.requestor, property,
3527 ct.encoding, 8, PropModeReplace,
3528 ct.value, (int)ct.nitems);
3529 ev.property = property;
3530
3531 if (freect)
3532 XFree (ct.value);
3533 }
3534
3535 XSendEvent (dpy, rq.requestor, False, 0L, (XEvent *)&ev);
3536 }
3537
3538 /* ------------------------------------------------------------------------- */
3539 #if USE_XIM
3540 void ecb_cold
3541 rxvt_term::im_set_position (XPoint &pos) NOTHROW
3542 {
3543 XWindowAttributes xwa;
3544
3545 XGetWindowAttributes (dpy, vt, &xwa);
3546
3547 pos.x = xwa.x + Col2Pixel (screen.cur.col);
3548 pos.y = xwa.y + Height2Pixel (screen.cur.row) + fbase;
3549 }
3550 #endif
3551
3552 #if ENABLE_OVERLAY
3553 void
3554 rxvt_term::scr_overlay_new (int x, int y, int w, int h) NOTHROW
3555 {
3556 if (nrow < 1 || ncol < 1)
3557 return;
3558
3559 want_refresh = 1;
3560
3561 scr_overlay_off ();
3562
3563 if (x < 0) x = ncol - w;
3564 if (y < 0) y = nrow - h;
3565
3566 // make space for border
3567 w += 2; min_it (w, ncol);
3568 h += 2; min_it (h, nrow);
3569
3570 x -= 1; clamp_it (x, 0, ncol - w);
3571 y -= 1; clamp_it (y, 0, nrow - h);
3572
3573 ov.x = x; ov.y = y;
3574 ov.w = w; ov.h = h;
3575
3576 ov.text = new text_t *[h];
3577 ov.rend = new rend_t *[h];
3578
3579 for (y = 0; y < h; y++)
3580 {
3581 text_t *tp = ov.text[y] = new text_t[w];
3582 rend_t *rp = ov.rend[y] = new rend_t[w];
3583
3584 text_t t0, t1, t2;
3585 rend_t r = OVERLAY_RSTYLE;
3586
3587 if (y == 0)
3588 t0 = 0x2554, t1 = 0x2550, t2 = 0x2557;
3589 else if (y < h - 1)
3590 t0 = 0x2551, t1 = 0x0020, t2 = 0x2551;
3591 else
3592 t0 = 0x255a, t1 = 0x2550, t2 = 0x255d;
3593
3594 *tp++ = t0;
3595 *rp++ = r;
3596
3597 for (x = w - 2; x > 0; --x)
3598 {
3599 *tp++ = t1;
3600 *rp++ = r;
3601 }
3602
3603 *tp = t2;
3604 *rp = r;
3605 }
3606 }
3607
3608 void
3609 rxvt_term::scr_overlay_off () NOTHROW
3610 {
3611 if (!ov.text)
3612 return;
3613
3614 want_refresh = 1;
3615
3616 for (int y = 0; y < ov.h; y++)
3617 {
3618 delete [] ov.text[y];
3619 delete [] ov.rend[y];
3620 }
3621
3622 delete [] ov.text; ov.text = 0;
3623 delete [] ov.rend; ov.rend = 0;
3624 }
3625
3626 void
3627 rxvt_term::scr_overlay_set (int x, int y, text_t text, rend_t rend) NOTHROW
3628 {
3629 if (!ov.text || x >= ov.w - 2 || y >= ov.h - 2)
3630 return;
3631
3632 x++, y++;
3633
3634 ov.text[y][x] = text;
3635 ov.rend[y][x] = rend;
3636 }
3637
3638 void
3639 rxvt_term::scr_overlay_set (int x, int y, const char *s) NOTHROW
3640 {
3641 while (*s)
3642 scr_overlay_set (x++, y, *s++);
3643 }
3644
3645 void
3646 rxvt_term::scr_overlay_set (int x, int y, const wchar_t *s) NOTHROW
3647 {
3648 while (*s)
3649 {
3650 text_t t = *s++;
3651 int width = WCWIDTH (t);
3652
3653 while (width--)
3654 {
3655 scr_overlay_set (x++, y, t);
3656 t = NOCHAR;
3657 }
3658 }
3659 }
3660
3661 void
3662 rxvt_term::scr_swap_overlay () NOTHROW
3663 {
3664 if (!ov.text)
3665 return;
3666
3667 // hide cursor if it is within the overlay area
3668 if (IN_RANGE_EXC (screen.cur.col - ov.x, 0, ov.w)
3669 && IN_RANGE_EXC (screen.cur.row - ov.y, 0, ov.h))
3670 screen.flags &= ~Screen_VisibleCursor;
3671
3672 // swap screen mem with overlay
3673 for (int y = ov.h; y--; )
3674 {
3675 text_t *t1 = ov.text[y];
3676 rend_t *r1 = ov.rend[y];
3677
3678 text_t *t2 = ROW(y + ov.y + view_start).t + ov.x;
3679 rend_t *r2 = ROW(y + ov.y + view_start).r + ov.x;
3680
3681 for (int x = ov.w; x--; )
3682 {
3683 text_t t = *t1; *t1++ = *t2; *t2++ = t;
3684 rend_t r = *r1; *r1++ = *r2; *r2++ = SET_FONT (r, FONTSET (r)->find_font (t));
3685 }
3686 }
3687 }
3688
3689 #endif
3690 /* ------------------------------------------------------------------------- */
3691