ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/screen.C
Revision: 1.406
Committed: Wed Nov 16 18:26:47 2011 UTC (12 years, 6 months ago) by sf-exg
Content type: text/plain
Branch: MAIN
Changes since 1.405: +0 -3 lines
Log Message:
Remove useless selection checks in scr_erase_screen.

The check below the switch already clears the selection if it overlaps
with the screen area to be erased. Moreover, the selection check used in
the 'clear screen before the cursor' case was incorrect as it cleared
the selection even when outside of the relevant screen area.

File Contents

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