ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/screen.C
Revision: 1.340
Committed: Sat Apr 17 23:00:45 2010 UTC (14 years, 1 month ago) by sf-exg
Content type: text/plain
Branch: MAIN
Changes since 1.339: +22 -17 lines
Log Message:
Disable colorRV resource.
Make OSC 17 apply to highlightColor.
Make highlightColor apply also to selected cells with reverse video.
Add highlightTextColor resource to change the foreground colour of
highlighted characters.

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