1 /*--------------------------------*-C-*--------------------------------------*
3 *---------------------------------------------------------------------------*
5 * Copyright (c) 1997-2001 Geoff Wing <gcw@pobox.com>
6 * Copyright (c) 2003-2004 Marc Lehmann <pcg@goof.com>
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.
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.
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 *--------------------------------------------------------------------------*/
24 * This file handles _all_ screen updates and selections
27 #include "../config.h" /* NECESSARY */
29 #include "rxvt.h" /* NECESSARY */
30 #include "screen.intpro" /* PROTOS for internal routines */
32 #include <X11/Xmd.h> /* get the typedef for CARD32 */
37 #include "salloc.C" // HACK, should be a seperate compile!
39 inline void fill_text (text_t *start, text_t value, int len)
45 /* ------------------------------------------------------------------------- */
46 #define PROP_SIZE 16384
47 #define TABSIZE 8 /* default tab size */
49 /* ------------------------------------------------------------------------- *
50 * GENERAL SCREEN AND SELECTION UPDATE ROUTINES *
51 * ------------------------------------------------------------------------- */
52 #define ZERO_SCROLLBACK() \
53 if (Options & Opt_scrollTtyOutput) \
54 TermWin.view_start = 0
55 #define CLEAR_SELECTION() \
56 selection.beg.row = selection.beg.col \
57 = selection.end.row = selection.end.col = 0
58 #define CLEAR_ALL_SELECTION() \
59 selection.beg.row = selection.beg.col \
60 = selection.mark.row = selection.mark.col \
61 = selection.end.row = selection.end.col = 0
63 #define ROW_AND_COL_IS_AFTER(A, B, C, D) \
64 (((A) > (C)) || (((A) == (C)) && ((B) > (D))))
65 #define ROW_AND_COL_IS_BEFORE(A, B, C, D) \
66 (((A) < (C)) || (((A) == (C)) && ((B) < (D))))
67 #define ROW_AND_COL_IN_ROW_AFTER(A, B, C, D) \
68 (((A) == (C)) && ((B) > (D)))
69 #define ROW_AND_COL_IN_ROW_AT_OR_AFTER(A, B, C, D) \
70 (((A) == (C)) && ((B) >= (D)))
71 #define ROW_AND_COL_IN_ROW_BEFORE(A, B, C, D) \
72 (((A) == (C)) && ((B) < (D)))
73 #define ROW_AND_COL_IN_ROW_AT_OR_BEFORE(A, B, C, D) \
74 (((A) == (C)) && ((B) <= (D)))
76 /* these must be row_col_t */
77 #define ROWCOL_IS_AFTER(X, Y) \
78 ROW_AND_COL_IS_AFTER ((X).row, (X).col, (Y).row, (Y).col)
79 #define ROWCOL_IS_BEFORE(X, Y) \
80 ROW_AND_COL_IS_BEFORE ((X).row, (X).col, (Y).row, (Y).col)
81 #define ROWCOL_IN_ROW_AFTER(X, Y) \
82 ROW_AND_COL_IN_ROW_AFTER ((X).row, (X).col, (Y).row, (Y).col)
83 #define ROWCOL_IN_ROW_BEFORE(X, Y) \
84 ROW_AND_COL_IN_ROW_BEFORE ((X).row, (X).col, (Y).row, (Y).col)
85 #define ROWCOL_IN_ROW_AT_OR_AFTER(X, Y) \
86 ROW_AND_COL_IN_ROW_AT_OR_AFTER ((X).row, (X).col, (Y).row, (Y).col)
87 #define ROWCOL_IN_ROW_AT_OR_BEFORE(X, Y) \
88 ROW_AND_COL_IN_ROW_AT_OR_BEFORE ((X).row, (X).col, (Y).row, (Y).col)
91 * CLEAR_ROWS : clear <num> rows starting from row <row>
92 * CLEAR_CHARS: clear <num> chars starting from pixel position <x,y>
93 * ERASE_ROWS : set <num> rows starting from row <row> to the foreground colour
95 #define drawBuffer TermWin.vt
97 #define CLEAR_ROWS(row, num) \
99 XClearArea (display->display, drawBuffer, 0, \
100 Row2Pixel (row), (unsigned int)TermWin.width, \
101 (unsigned int)Height2Pixel (num), False)
103 #define CLEAR_CHARS(x, y, num) \
104 if (TermWin.mapped) \
105 XClearArea (display->display, drawBuffer, x, y, \
106 (unsigned int)Width2Pixel (num), \
107 (unsigned int)Height2Pixel (1), False)
109 #define ERASE_ROWS(row, num) \
110 XFillRectangle (display->display, drawBuffer, TermWin.gc, \
111 0, Row2Pixel (row), \
112 (unsigned int)TermWin.width, \
113 (unsigned int)Height2Pixel (num))
115 /* ------------------------------------------------------------------------- *
116 * SCREEN `COMMON' ROUTINES *
117 * ------------------------------------------------------------------------- */
118 /* Fill part/all of a line with blanks. */
120 rxvt_term::scr_blank_line (text_t *et, rend_t *er, unsigned int width, rend_t efs)
122 efs &= ~RS_baseattrMask;
123 efs = SET_FONT (efs, TermWin.fontset->find_font (' '));
132 /* ------------------------------------------------------------------------- */
133 /* Fill a full line with blanks - make sure it is allocated first */
135 rxvt_term::scr_blank_screen_mem (text_t **tp, rend_t **rp, unsigned int row, rend_t efs)
138 assert ((tp[row] && rp[row]) || (tp[row] == NULL && rp[row] == NULL));
142 tp[row] = (text_t *)talloc->alloc ();
143 rp[row] = (rend_t *)ralloc->alloc ();
146 scr_blank_line (tp[row], rp[row], TermWin.ncol, efs);
149 /* ------------------------------------------------------------------------- *
150 * SCREEN INITIALISATION *
151 * ------------------------------------------------------------------------- */
153 rxvt_term::scr_reset ()
155 unsigned int ncol, nrow, total_rows, prev_total_rows;
160 D_SCREEN ((stderr, "rxvt_scr_reset ()"));
162 TermWin.view_start = 0;
165 if (TermWin.ncol == 0)
168 if (TermWin.nrow == 0)
174 if (ncol == prev_ncol && nrow == prev_nrow)
179 total_rows = nrow + TermWin.saveLines;
180 prev_total_rows = prev_nrow + TermWin.saveLines;
183 screen.bscroll = nrow - 1;
187 talloc = new rxvt_salloc (ncol * sizeof (text_t));
188 ralloc = new rxvt_salloc (ncol * sizeof (rend_t));
194 * first time called so just malloc everything: don't rely on realloc
195 * Note: this is still needed so that all the scrollback lines are NULL
197 screen.text = (text_t **)rxvt_calloc (total_rows, sizeof (text_t *));
198 buf_text = (text_t **)rxvt_calloc (total_rows, sizeof (text_t *));
199 drawn_text = (text_t **)rxvt_calloc (nrow, sizeof (text_t *));
200 swap.text = (text_t **)rxvt_calloc (nrow, sizeof (text_t *));
202 screen.tlen = (int16_t *)rxvt_calloc (total_rows, sizeof (int16_t));
203 swap.tlen = (int16_t *)rxvt_calloc (nrow, sizeof (int16_t));
205 screen.rend = (rend_t **)rxvt_calloc (total_rows, sizeof (rend_t *));
206 buf_rend = (rend_t **)rxvt_calloc (total_rows, sizeof (rend_t *));
207 drawn_rend = (rend_t **)rxvt_calloc (nrow, sizeof (rend_t *));
208 swap.rend = (rend_t **)rxvt_calloc (nrow, sizeof (rend_t *));
210 for (p = 0; p < nrow; p++)
212 q = p + TermWin.saveLines;
213 scr_blank_screen_mem (screen.text, screen.rend, q, DEFAULT_RSTYLE);
214 scr_blank_screen_mem (swap.text, swap.rend, p, DEFAULT_RSTYLE);
215 screen.tlen[q] = swap.tlen[p] = 0;
216 scr_blank_screen_mem (drawn_text, drawn_rend, p, DEFAULT_RSTYLE);
219 MEMSET (charsets, 'B', sizeof (charsets));
220 TermWin.nscrolled = 0; /* no saved lines */
221 rstyle = DEFAULT_RSTYLE;
222 screen.flags = Screen_DefaultFlags;
223 screen.cur.row = screen.cur.col = 0;
225 current_screen = PRIMARY;
229 swap.flags = Screen_DefaultFlags;
230 swap.cur.row = swap.cur.col = 0;
232 current_screen = SECONDARY;
234 current_screen = PRIMARY;
237 selection.text = NULL;
239 selection.op = SELECTION_CLEAR;
240 selection.screen = PRIMARY;
241 selection.clicks = 0;
242 CLEAR_ALL_SELECTION ();
248 * add or delete rows as appropriate
250 setrstyle = DEFAULT_RSTYLE;
252 if (nrow < prev_nrow)
255 k = min (TermWin.nscrolled, prev_nrow - nrow);
256 scr_scroll_text (0, (int)prev_nrow - 1, k, 1);
258 for (p = nrow; p < prev_nrow; p++)
260 q = p + TermWin.saveLines;
265 assert (screen.rend[q]);
267 talloc->free (screen.text[q]);
268 ralloc->free (screen.rend[q]);
274 assert (swap.rend[p]);
276 talloc->free (swap.text[p]);
277 ralloc->free (swap.rend[p]);
281 assert (drawn_text[p] && drawn_rend[p]);
283 talloc->free (drawn_text[p]);
284 ralloc->free (drawn_rend[p]);
287 /* we have fewer rows so fix up cursor position */
288 MIN_IT (screen.cur.row, (int32_t)nrow - 1);
289 MIN_IT (swap.cur.row, (int32_t)nrow - 1);
291 scr_reset_realloc (); /* realloc _last_ */
293 else if (nrow > prev_nrow)
296 scr_reset_realloc (); /* realloc _first_ */
298 TermWin.ncol = prev_ncol; // save b/c scr_blank_screen_mem uses this
300 k = min (TermWin.nscrolled, nrow - prev_nrow);
302 for (p = prev_total_rows; p < total_rows; p++)
305 screen.text[p] = NULL;
306 screen.rend[p] = NULL;
309 for (p = prev_total_rows; p < total_rows - k; p++)
310 scr_blank_screen_mem (screen.text, screen.rend, p, setrstyle);
312 for (p = prev_nrow; p < nrow; p++)
317 drawn_text[p] = NULL;
318 drawn_rend[p] = NULL;
319 scr_blank_screen_mem (swap.text, swap.rend, p, setrstyle);
320 scr_blank_screen_mem (drawn_text, drawn_rend, p, setrstyle);
325 scr_scroll_text (0, (int)nrow - 1, -k, 1);
327 screen.s_cur.row += k;
328 TermWin.nscrolled -= k;
331 assert (screen.cur.row < TermWin.nrow);
332 assert (swap.cur.row < TermWin.nrow);
333 #else /* drive with your eyes closed */
335 MIN_IT (screen.cur.row, nrow - 1);
336 MIN_IT (swap.cur.row, nrow - 1);
338 TermWin.ncol = ncol; // save b/c scr_blank_screen_mem uses this
342 if (ncol != prev_ncol)
344 rxvt_salloc *ta = new rxvt_salloc (ncol * sizeof (text_t));
345 rxvt_salloc *ra = new rxvt_salloc (ncol * sizeof (rend_t));
347 for (p = 0; p < total_rows; p++)
351 screen.text[p] = (text_t *)ta->alloc (screen.text[p], prev_ncol * sizeof (text_t));
352 screen.rend[p] = (rend_t *)ra->alloc (screen.rend[p], prev_ncol * sizeof (rend_t));
354 MIN_IT (screen.tlen[p], (int16_t)ncol);
356 if (ncol > prev_ncol)
357 scr_blank_line (&screen.text[p][prev_ncol],
358 &screen.rend[p][prev_ncol],
359 ncol - prev_ncol, setrstyle);
363 for (p = 0; p < nrow; p++)
365 drawn_text[p] = (text_t *)ta->alloc (drawn_text[p], prev_ncol * sizeof (text_t));
366 drawn_rend[p] = (rend_t *)ra->alloc (drawn_rend[p], prev_ncol * sizeof (rend_t));
368 if (ncol > prev_ncol)
369 scr_blank_line (&drawn_text[p][prev_ncol],
370 &drawn_rend[p][prev_ncol],
371 ncol - prev_ncol, setrstyle);
375 swap.text[p] = (text_t *)ta->alloc (swap.text[p], prev_ncol * sizeof (text_t));
376 swap.rend[p] = (rend_t *)ra->alloc (swap.rend[p], prev_ncol * sizeof (rend_t));
378 MIN_IT (swap.tlen[p], (int16_t)ncol);
380 if (ncol > prev_ncol)
381 scr_blank_line (&swap.text[p][prev_ncol],
382 &swap.rend[p][prev_ncol],
383 ncol - prev_ncol, setrstyle);
388 MIN_IT (screen.cur.col, (int16_t)ncol - 1);
389 MIN_IT (swap.cur.col, (int16_t)ncol - 1);
391 delete talloc; talloc = ta;
392 delete ralloc; ralloc = ra;
402 tabs = (char *)rxvt_malloc (ncol * sizeof (char));
404 for (p = 0; p < ncol; p++)
405 tabs[p] = (p % TABSIZE == 0) ? 1 : 0;
411 rxvt_term::scr_reset_realloc ()
413 uint16_t total_rows, nrow;
416 total_rows = nrow + TermWin.saveLines;
418 screen.text = (text_t **)rxvt_realloc (screen.text, total_rows * sizeof (text_t *));
419 buf_text = (text_t **)rxvt_realloc (buf_text , total_rows * sizeof (text_t *));
420 drawn_text = (text_t **)rxvt_realloc (drawn_text , nrow * sizeof (text_t *));
421 swap.text = (text_t **)rxvt_realloc (swap.text , nrow * sizeof (text_t *));
423 screen.tlen = (int16_t *)rxvt_realloc (screen.tlen, total_rows * sizeof (int16_t));
424 swap.tlen = (int16_t *)rxvt_realloc (swap.tlen , total_rows * sizeof (int16_t));
426 screen.rend = (rend_t **)rxvt_realloc (screen.rend, total_rows * sizeof (rend_t *));
427 buf_rend = (rend_t **)rxvt_realloc (buf_rend , total_rows * sizeof (rend_t *));
428 drawn_rend = (rend_t **)rxvt_realloc (drawn_rend , nrow * sizeof (rend_t *));
429 swap.rend = (rend_t **)rxvt_realloc (swap.rend , nrow * sizeof (rend_t *));
433 /* ------------------------------------------------------------------------- */
435 * Free everything. That way malloc debugging can find leakage.
438 rxvt_term::scr_release ()
443 total_rows = TermWin.nrow + TermWin.saveLines;
445 delete talloc; talloc = 0;
446 delete ralloc; ralloc = 0;
460 /* NULL these so if anything tries to use them, we'll know about it */
461 screen.text = drawn_text = swap.text = NULL;
462 screen.rend = drawn_rend = swap.rend = NULL;
463 screen.tlen = swap.tlen = NULL;
469 /* ------------------------------------------------------------------------- */
474 rxvt_term::scr_poweron ()
476 D_SCREEN ((stderr, "rxvt_scr_poweron ()"));
479 prev_nrow = prev_ncol = 0;
483 scr_refresh (SLOW_REFRESH);
486 /* ------------------------------------------------------------------------- *
487 * PROCESS SCREEN COMMANDS *
488 * ------------------------------------------------------------------------- */
490 * Save and Restore cursor
491 * XTERM_SEQ: Save cursor : ESC 7
492 * XTERM_SEQ: Restore cursor: ESC 8
495 rxvt_term::scr_cursor (int mode)
499 D_SCREEN ((stderr, "rxvt_scr_cursor (%c)", mode));
501 #if NSCREENS && !defined(NO_SECONDARY_SCREEN_CURSOR)
502 if (current_screen == SECONDARY)
511 s->s_cur.row = s->cur.row;
512 s->s_cur.col = s->cur.col;
513 s->s_rstyle = rstyle;
514 s->s_charset = s->charset;
515 s->s_charset_char = charsets[s->charset];
520 s->cur.row = s->s_cur.row;
521 s->cur.col = s->s_cur.col;
522 s->flags &= ~Screen_WrapNext;
523 rstyle = s->s_rstyle;
524 s->charset = s->s_charset;
525 charsets[s->charset] = s->s_charset_char;
530 /* boundary check in case screen size changed between SAVE and RESTORE */
531 MIN_IT (s->cur.row, TermWin.nrow - 1);
532 MIN_IT (s->cur.col, TermWin.ncol - 1);
534 assert (s->cur.row >= 0);
535 assert (s->cur.col >= 0);
536 #else /* drive with your eyes closed */
537 MAX_IT (s->cur.row, 0);
538 MAX_IT (s->cur.col, 0);
542 /* ------------------------------------------------------------------------- */
544 * Swap between primary and secondary screens
545 * XTERM_SEQ: Primary screen : ESC [ ? 4 7 h
546 * XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
549 rxvt_term::scr_change_screen (int scrn)
558 D_SCREEN ((stderr, "rxvt_scr_change_screen (%d)", scrn));
560 TermWin.view_start = 0;
562 if (current_screen == scrn)
565 selection_check (2); /* check for boundary cross */
567 SWAP_IT (current_screen, scrn, int);
569 if (Options & Opt_secondaryScreen)
572 offset = TermWin.saveLines;
573 for (i = prev_nrow; i--;)
575 SWAP_IT (screen.text[i + offset], swap.text[i], text_t *);
576 SWAP_IT (screen.tlen[i + offset], swap.tlen[i], int16_t);
577 SWAP_IT (screen.rend[i + offset], swap.rend[i], rend_t *);
579 SWAP_IT (screen.cur.row, swap.cur.row, int16_t);
580 SWAP_IT (screen.cur.col, swap.cur.col, int16_t);
582 assert ((screen.cur.row >= 0) && (screen.cur.row < prev_nrow));
583 assert ((screen.cur.col >= 0) && (screen.cur.col < prev_ncol));
584 # else /* drive with your eyes closed */
585 MAX_IT (screen.cur.row, 0);
586 MIN_IT (screen.cur.row, (int32_t)prev_nrow - 1);
587 MAX_IT (screen.cur.col, 0);
588 MIN_IT (screen.cur.col, (int32_t)prev_ncol - 1);
590 SWAP_IT (screen.charset, swap.charset, int16_t);
591 SWAP_IT (screen.flags, swap.flags, int);
592 screen.flags |= Screen_VisibleCursor;
593 swap.flags |= Screen_VisibleCursor;
597 if (Options & Opt_secondaryScroll)
598 //if (current_screen == PRIMARY)
599 scr_scroll_text (0, (prev_nrow - 1), prev_nrow, 0);
603 /* ------------------------------------------------------------------------- */
605 * Change the colour for following text
608 rxvt_term::scr_color (unsigned int color, int fgbg)
611 if (fgbg == Color_fg)
612 rstyle = SET_FGCOLOR (rstyle, color);
614 rstyle = SET_BGCOLOR (rstyle, color);
617 /* ------------------------------------------------------------------------- */
619 * Change the rendition style for following text
622 rxvt_term::scr_rendition (int set, int style)
626 else if (style == ~RS_None)
627 rstyle = DEFAULT_RSTYLE;
632 /* ------------------------------------------------------------------------- */
634 * Scroll text between <row1> and <row2> inclusive, by <count> lines
635 * count positive ==> scroll up
636 * count negative ==> scroll down
637 * spec == 0 for normal routines
640 rxvt_term::scr_scroll_text (int row1, int row2, int count, int spec)
645 if (count == 0 || (row1 > row2))
649 D_SCREEN ((stderr, "rxvt_scroll_text (%d,%d,%d,%d): %s", row1, row2, count, spec, (current_screen == PRIMARY) ? "Primary" : "Secondary"));
651 if (row1 == 0 && count > 0
652 && (current_screen == PRIMARY || Options & Opt_secondaryScroll))
654 nscrolled = (long)TermWin.nscrolled + (long)count;
656 if (nscrolled > (long)TermWin.saveLines)
657 TermWin.nscrolled = TermWin.saveLines;
659 TermWin.nscrolled = (uint16_t)nscrolled;
661 if ((Options & Opt_scrollWithBuffer)
662 && TermWin.view_start != 0
663 && TermWin.view_start != TermWin.saveLines)
664 scr_page (UP, count);
667 row1 += TermWin.saveLines;
669 row2 += TermWin.saveLines;
671 if (selection.op && current_screen == selection.screen)
673 i = selection.beg.row + TermWin.saveLines;
674 j = selection.end.row + TermWin.saveLines;
675 if ((i < row1 && j > row1)
676 || (i < row2 && j > row2)
677 || (i - count < row1 && i >= row1)
678 || (i - count > row2 && i <= row2)
679 || (j - count < row1 && j >= row1)
680 || (j - count > row2 && j <= row2))
682 CLEAR_ALL_SELECTION ();
683 selection.op = SELECTION_CLEAR; /* XXX: too aggressive? */
685 else if (j >= row1 && j <= row2)
687 /* move selected region too */
688 selection.beg.row -= count;
689 selection.end.row -= count;
690 selection.mark.row -= count;
694 selection_check (0); /* _after_ TermWin.nscrolled update */
709 /* A1: Copy lines that will get clobbered by the rotation */
710 for (i = 0, j = row1; i < count; i++, j++)
712 buf_text[i] = screen.text[j];
713 buf_rend[i] = screen.rend[j];
716 /* A2: Rotate lines */
717 for (j = row1, i = j + count; i <= row2; i++, j++)
719 screen.tlen[j] = screen.tlen[i];
720 screen.text[j] = screen.text[i];
721 screen.rend[j] = screen.rend[i];
724 j = row2 - count + 1, i = count;
726 else /* if (j < 0) */
730 /* B1: Copy lines that will get clobbered by the rotation */
731 for (i = 0, j = row2; i < count; i++, j--)
733 buf_text[i] = screen.text[j];
734 buf_rend[i] = screen.rend[j];
737 /* B2: Rotate lines */
738 for (j = row2, i = j - count; i >= row1; i--, j--)
740 screen.tlen[j] = screen.tlen[i];
741 screen.text[j] = screen.text[i];
742 screen.rend[j] = screen.rend[i];
749 /* C: Resurrect lines */
753 screen.text[j] = buf_text[i];
754 screen.rend[j] = buf_rend[i];
756 if (!spec) /* line length may not equal TermWin.ncol */
757 scr_blank_screen_mem (screen.text, screen.rend, (unsigned int)j, rstyle);
763 /* ------------------------------------------------------------------------- */
765 * Add text given in <str> of length <len> to screen struct
768 rxvt_term::scr_add_lines (const unicode_t *str, int nlines, int len)
770 unsigned char checksel, clearsel;
772 int i, row, last_col;
776 if (len <= 0) /* sanity */
780 last_col = TermWin.ncol;
782 D_SCREEN ((stderr, "rxvt_scr_add_lines (%d,%d)", nlines, len));
786 nlines += (screen.cur.row - screen.bscroll);
788 && (screen.tscroll == 0)
789 && (screen.bscroll == (TermWin.nrow - 1)))
791 /* _at least_ this many lines need to be scrolled */
792 scr_scroll_text (screen.tscroll, screen.bscroll, nlines, 0);
793 screen.cur.row -= nlines;
797 assert (screen.cur.col < last_col);
798 assert ((screen.cur.row < TermWin.nrow)
799 && (screen.cur.row >= - (int32_t)TermWin.nscrolled));
800 #else /* drive with your eyes closed */
801 MIN_IT (screen.cur.col, last_col - 1);
802 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
803 MAX_IT (screen.cur.row, - (int32_t)TermWin.nscrolled);
805 row = screen.cur.row + TermWin.saveLines;
807 checksel = selection.op && current_screen == selection.screen ? 1 : 0;
810 stp = screen.text[row];
811 srp = screen.rend[row];
825 if (screen.tlen[row] != -1) /* XXX: think about this */
826 MAX_IT (screen.tlen[row], screen.cur.col);
828 screen.flags &= ~Screen_WrapNext;
830 if (screen.cur.row == screen.bscroll)
831 scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
832 else if (screen.cur.row < (TermWin.nrow - 1))
833 row = (++screen.cur.row) + TermWin.saveLines;
835 stp = screen.text[row]; /* _must_ refresh */
836 srp = screen.rend[row]; /* _must_ refresh */
840 if (screen.tlen[row] != -1) /* XXX: think about this */
841 MAX_IT (screen.tlen[row], screen.cur.col);
843 screen.flags &= ~Screen_WrapNext;
848 if (checksel /* see if we're writing within selection */
849 && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
850 && ROWCOL_IS_BEFORE (screen.cur, selection.end))
856 if (screen.flags & Screen_WrapNext)
858 screen.tlen[row] = -1;
859 if (screen.cur.row == screen.bscroll)
860 scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
861 else if (screen.cur.row < (TermWin.nrow - 1))
862 row = (++screen.cur.row) + TermWin.saveLines;
864 stp = screen.text[row]; /* _must_ refresh */
865 srp = screen.rend[row]; /* _must_ refresh */
867 screen.flags &= ~Screen_WrapNext;
870 if (screen.flags & Screen_Insert)
871 scr_insdel_chars (1, INSERT);
873 // rely on wcwidth to tell us the character width, at least for non-latin1
874 // do wcwidth before further replacements, as wcwidth says that line-drawing
875 // characters have width -1 (DOH!) on GNU/Linux sometimes.
876 int width = c < 256 ? 1 : wcwidth (c);
878 if (charsets[screen.charset] == '0') // DEC SPECIAL
880 // vt100 special graphics and line drawing
881 static uint16_t vt100_0[32] = { // 5f .. 7e
882 0x0020, 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
883 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
884 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534,
885 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7,
888 if (c >= 0x5f && c <= 0x7e)
890 c = vt100_0[c - 0x5f];
897 #if !UNICODE_3 && ENABLE_COMBINING
898 // trim characters we can't store directly :(
900 c = rxvt_composite.compose (c); // map to lower 16 bits
902 bool bold = (Options & Opt_realBold) && ((rstyle & RS_Bold) != 0);
903 rend_t rend = SET_FONT (rstyle, TermWin.fontset->find_font (c, bold));
907 stp[screen.cur.col] = c;
908 srp[screen.cur.col] = rend;
910 if (screen.cur.col < last_col - 1)
914 screen.tlen[row] = last_col;
915 if (screen.flags & Screen_Autowrap)
916 screen.flags |= Screen_WrapNext;
924 // pad with spaces when overwriting wide character with smaller one
925 for (int c = screen.cur.col; c < last_col && stp[c] == NOCHAR; c++)
934 // handle combining characters
935 // we just tag the accent on the previous on-screen character.
936 // this is arguably not correct, but also arguably not wrong.
937 // we don't handle double-width characters nicely yet.
942 if (screen.cur.col > 0)
944 tp = stp + screen.cur.col - 1;
945 rp = srp + screen.cur.col - 1;
947 else if (screen.cur.row > 0
948 && screen.tlen [screen.cur.row - 1 + TermWin.saveLines] == -1)
950 tp = screen.text[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
951 rp = screen.rend[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
956 // handle double-width-chars by making them look extremely ugly
958 *tp = ' '; // hack //D //TODO //--tp, --rp;
960 // first try to find a precomposed character
961 unicode_t n = rxvt_compose (*tp, c);
963 n = rxvt_composite.compose (*tp, c);
966 *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
971 if (screen.tlen[row] != -1) /* XXX: think about this */
972 MAX_IT (screen.tlen[row], screen.cur.col);
975 * If we wrote anywhere in the selected area, kill the selection
976 * XXX: should we kill the mark too? Possibly, but maybe that
977 * should be a similar check.
983 assert (screen.cur.row >= 0);
984 #else /* drive with your eyes closed */
985 MAX_IT (screen.cur.row, 0);
989 /* ------------------------------------------------------------------------- */
991 * Process Backspace. Move back the cursor back a position, wrap if have to
995 rxvt_term::scr_backspace ()
999 if (screen.cur.col == 0)
1001 if (screen.cur.row > 0)
1003 #ifdef TERMCAP_HAS_BW
1004 screen.cur.col = TermWin.ncol - 1;
1011 else if ((screen.flags & Screen_WrapNext) == 0)
1012 scr_gotorc (0, -1, RELATIVE);
1014 screen.flags &= ~Screen_WrapNext;
1017 /* ------------------------------------------------------------------------- */
1019 * Process Horizontal Tab
1020 * count: +ve = forward; -ve = backwards
1024 rxvt_term::scr_tab (int count)
1028 D_SCREEN ((stderr, "rxvt_scr_tab (%d)", count));
1030 i = x = screen.cur.col;
1036 for (; ++i < TermWin.ncol; )
1045 x = TermWin.ncol - 1;
1047 else /* if (count < 0) */
1061 if (x != screen.cur.col)
1062 scr_gotorc (0, x, R_RELATIVE);
1065 /* ------------------------------------------------------------------------- */
1067 * Process DEC Back Index
1069 * Move cursor left in row. If we're at the left boundary, shift everything
1070 * in that row right. Clear left column.
1074 rxvt_term::scr_backindex ()
1076 if (screen.cur.col > 0)
1077 scr_gotorc (0, -1, R_RELATIVE | C_RELATIVE);
1080 if (screen.tlen[screen.cur.row + TermWin.saveLines] == 0)
1081 return; /* um, yeah? */
1082 scr_insdel_chars (1, INSERT);
1086 /* ------------------------------------------------------------------------- */
1088 * Process DEC Forward Index
1090 * Move cursor right in row. If we're at the right boundary, shift everything
1091 * in that row left. Clear right column.
1095 rxvt_term::scr_forwardindex ()
1099 if (screen.cur.col < TermWin.ncol - 1)
1100 scr_gotorc (0, 1, R_RELATIVE | C_RELATIVE);
1103 row = screen.cur.row + TermWin.saveLines;
1104 if (screen.tlen[row] == 0)
1105 return; /* um, yeah? */
1106 else if (screen.tlen[row] == -1)
1107 screen.tlen[row] = TermWin.ncol;
1108 scr_gotorc (0, 0, R_RELATIVE);
1109 scr_insdel_chars (1, DELETE);
1110 scr_gotorc (0, TermWin.ncol - 1, R_RELATIVE);
1115 /* ------------------------------------------------------------------------- */
1120 rxvt_term::scr_gotorc (int row, int col, int relative)
1125 D_SCREEN ((stderr, "rxvt_scr_gotorc (r:%s%d,c:%s%d): from (r:%d,c:%d)", (relative & R_RELATIVE ? "+" : ""), row, (relative & C_RELATIVE ? "+" : ""), col, screen.cur.row, screen.cur.col));
1127 screen.cur.col = relative & C_RELATIVE ? screen.cur.col + col : col;
1128 MAX_IT (screen.cur.col, 0);
1129 MIN_IT (screen.cur.col, (int32_t)TermWin.ncol - 1);
1131 screen.flags &= ~Screen_WrapNext;
1132 if (relative & R_RELATIVE)
1136 if (screen.cur.row <= screen.bscroll
1137 && (screen.cur.row + row) > screen.bscroll)
1138 screen.cur.row = screen.bscroll;
1140 screen.cur.row += row;
1144 if (screen.cur.row >= screen.tscroll
1145 && (screen.cur.row + row) < screen.tscroll)
1146 screen.cur.row = screen.tscroll;
1148 screen.cur.row += row;
1153 if (screen.flags & Screen_Relative)
1154 { /* relative origin mode */
1155 screen.cur.row = row + screen.tscroll;
1156 MIN_IT (screen.cur.row, screen.bscroll);
1159 screen.cur.row = row;
1161 MAX_IT (screen.cur.row, 0);
1162 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1164 while (screen.cur.col > 0
1165 && screen.text[screen.cur.row + TermWin.saveLines][screen.cur.col] == NOCHAR)
1169 /* ------------------------------------------------------------------------- */
1171 * direction should be UP or DN
1174 rxvt_term::scr_index (enum page_dirn direction)
1179 dirn = ((direction == UP) ? 1 : -1);
1180 D_SCREEN ((stderr, "rxvt_scr_index (%d)", dirn));
1184 screen.flags &= ~Screen_WrapNext;
1185 if ((screen.cur.row == screen.bscroll && direction == UP)
1186 || (screen.cur.row == screen.tscroll && direction == DN))
1187 scr_scroll_text (screen.tscroll, screen.bscroll, dirn, 0);
1189 screen.cur.row += dirn;
1190 MAX_IT (screen.cur.row, 0);
1191 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1192 selection_check (0);
1195 /* ------------------------------------------------------------------------- */
1197 * Erase part or whole of a line
1198 * XTERM_SEQ: Clear line to right: ESC [ 0 K
1199 * XTERM_SEQ: Clear line to left : ESC [ 1 K
1200 * XTERM_SEQ: Clear whole line : ESC [ 2 K
1203 rxvt_term::scr_erase_line (int mode)
1205 unsigned int row, col, num;
1208 D_SCREEN ((stderr, "rxvt_scr_erase_line (%d) at screen row: %d", mode, screen.cur.row));
1211 selection_check (1);
1213 screen.flags &= ~Screen_WrapNext;
1215 row = TermWin.saveLines + screen.cur.row;
1218 case 0: /* erase to end of line */
1219 col = screen.cur.col;
1220 num = TermWin.ncol - col;
1221 MIN_IT (screen.tlen[row], (int16_t)col);
1222 if (ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur)
1223 || ROWCOL_IN_ROW_AT_OR_AFTER (selection.end, screen.cur))
1226 case 1: /* erase to beginning of line */
1228 num = screen.cur.col + 1;
1229 if (ROWCOL_IN_ROW_AT_OR_BEFORE (selection.beg, screen.cur)
1230 || ROWCOL_IN_ROW_AT_OR_BEFORE (selection.end, screen.cur))
1233 case 2: /* erase whole line */
1236 screen.tlen[row] = 0;
1237 if (selection.beg.row <= screen.cur.row
1238 && selection.end.row >= screen.cur.row)
1245 if (screen.text[row])
1246 scr_blank_line (&screen.text[row][col], &screen.rend[row][col], num, rstyle);
1248 scr_blank_screen_mem (screen.text, screen.rend, row, rstyle);
1251 /* ------------------------------------------------------------------------- */
1253 * Erase part of whole of the screen
1254 * XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
1255 * XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
1256 * XTERM_SEQ: Clear whole screen : ESC [ 2 J
1259 rxvt_term::scr_erase_screen (int mode)
1262 int32_t row, row_offset;
1267 D_SCREEN ((stderr, "rxvt_scr_erase_screen (%d) at screen row: %d", mode, screen.cur.row));
1269 row_offset = (int32_t)TermWin.saveLines;
1273 case 0: /* erase to end of screen */
1274 selection_check (1);
1276 row = screen.cur.row + 1; /* possible OOB */
1277 num = TermWin.nrow - row;
1279 case 1: /* erase to beginning of screen */
1280 selection_check (3);
1283 num = screen.cur.row;
1285 case 2: /* erase whole screen */
1286 selection_check (3);
1294 refresh_type |= REFRESH_BOUNDS;
1296 if (selection.op && current_screen == selection.screen
1297 && ((selection.beg.row >= row && selection.beg.row <= row + num)
1298 || (selection.end.row >= row
1299 && selection.end.row <= row + num)))
1302 if (row >= TermWin.nrow) /* Out Of Bounds */
1305 MIN_IT (num, (TermWin.nrow - row));
1307 if (rstyle & (RS_RVid | RS_Uline))
1308 ren = (rend_t) ~RS_None;
1309 else if (GET_BASEBG (rstyle) == Color_bg)
1311 ren = DEFAULT_RSTYLE;
1312 CLEAR_ROWS (row, num);
1316 ren = (rstyle & (RS_fgMask | RS_bgMask));
1317 gcvalue.foreground = PixColors[GET_BGCOLOR (rstyle)];
1318 XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1319 ERASE_ROWS (row, num);
1320 gcvalue.foreground = PixColors[Color_fg];
1321 XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1324 for (; num--; row++)
1326 scr_blank_screen_mem (screen.text, screen.rend,
1327 (unsigned int) (row + row_offset), rstyle);
1328 screen.tlen[row + row_offset] = 0;
1329 scr_blank_line (drawn_text[row], drawn_rend[row],
1330 (unsigned int)TermWin.ncol, ren);
1334 /* ------------------------------------------------------------------------- */
1336 * Fill the screen with `E's
1337 * XTERM_SEQ: Screen Alignment Test: ESC # 8
1348 selection_check (3);
1350 fs = SET_FONT (rstyle, TermWin.fontset->find_font ('E'));
1351 for (k = TermWin.saveLines, i = TermWin.nrow; i--; k++)
1353 screen.tlen[k] = TermWin.ncol; /* make the `E's selectable */
1354 fill_text (screen.text[k], 'E', TermWin.ncol);
1355 for (r1 = screen.rend[k], j = TermWin.ncol; j--; )
1360 /* ------------------------------------------------------------------------- */
1362 * Insert/Delete <count> lines
1365 rxvt_term::scr_insdel_lines (int count, int insdel)
1371 selection_check (1);
1373 if (screen.cur.row > screen.bscroll)
1376 end = screen.bscroll - screen.cur.row + 1;
1379 if (insdel == DELETE)
1381 else if (insdel == INSERT)
1384 screen.flags &= ~Screen_WrapNext;
1386 scr_scroll_text (screen.cur.row, screen.bscroll, insdel * count, 0);
1389 /* ------------------------------------------------------------------------- */
1391 * Insert/Delete <count> characters from the current position
1394 rxvt_term::scr_insdel_chars (int count, int insdel)
1408 selection_check (1);
1409 MIN_IT (count, (TermWin.ncol - screen.cur.col));
1411 row = screen.cur.row + TermWin.saveLines;
1412 screen.flags &= ~Screen_WrapNext;
1414 stp = screen.text[row];
1415 srp = screen.rend[row];
1416 slp = & (screen.tlen[row]);
1421 for (col = TermWin.ncol - 1; (col - count) >= screen.cur.col;
1424 stp[col] = stp[col - count];
1425 srp[col] = srp[col - count];
1431 MIN_IT (*slp, TermWin.ncol);
1434 if (selection.op && current_screen == selection.screen
1435 && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1437 if (selection.end.row != screen.cur.row
1438 || (selection.end.col + count >= TermWin.ncol))
1441 { /* shift selection */
1442 selection.beg.col += count;
1443 selection.mark.col += count; /* XXX: yes? */
1444 selection.end.col += count;
1448 scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1449 (unsigned int)count, rstyle);
1453 screen.cur.col += count; /* don't worry if > TermWin.ncol */
1454 selection_check (1);
1455 screen.cur.col -= count;
1456 scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1457 (unsigned int)count, rstyle);
1461 tr = srp[TermWin.ncol - 1] & (RS_fgMask | RS_bgMask | RS_baseattrMask);
1463 for (col = screen.cur.col; (col + count) < TermWin.ncol; col++)
1465 stp[col] = stp[col + count];
1466 srp[col] = srp[col + count];
1469 scr_blank_line (& (stp[TermWin.ncol - count]),
1470 & (srp[TermWin.ncol - count]),
1471 (unsigned int)count, tr);
1473 if (*slp == -1) /* break line continuation */
1474 *slp = TermWin.ncol;
1479 if (selection.op && current_screen == selection.screen
1480 && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1482 if (selection.end.row != screen.cur.row
1483 || (screen.cur.col >= selection.beg.col - count)
1484 || selection.end.col >= TermWin.ncol)
1488 /* shift selection */
1489 selection.beg.col -= count;
1490 selection.mark.col -= count; /* XXX: yes? */
1491 selection.end.col -= count;
1499 /* ------------------------------------------------------------------------- */
1501 * Set the scrolling region
1502 * XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
1505 rxvt_term::scr_scroll_region (int top, int bot)
1508 MIN_IT (bot, (int)TermWin.nrow - 1);
1513 screen.tscroll = top;
1514 screen.bscroll = bot;
1515 scr_gotorc (0, 0, 0);
1518 /* ------------------------------------------------------------------------- */
1520 * Make the cursor visible/invisible
1521 * XTERM_SEQ: Make cursor visible : ESC [ ? 25 h
1522 * XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
1525 rxvt_term::scr_cursor_visible (int mode)
1530 screen.flags |= Screen_VisibleCursor;
1532 screen.flags &= ~Screen_VisibleCursor;
1535 /* ------------------------------------------------------------------------- */
1537 * Set/unset automatic wrapping
1538 * XTERM_SEQ: Set Wraparound : ESC [ ? 7 h
1539 * XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
1542 rxvt_term::scr_autowrap (int mode)
1545 screen.flags |= Screen_Autowrap;
1547 screen.flags &= ~ (Screen_Autowrap | Screen_WrapNext);
1550 /* ------------------------------------------------------------------------- */
1552 * Set/unset margin origin mode
1553 * Absolute mode: line numbers are counted relative to top margin of screen
1554 * and the cursor can be moved outside the scrolling region.
1555 * Relative mode: line numbers are relative to top margin of scrolling region
1556 * and the cursor cannot be moved outside.
1557 * XTERM_SEQ: Set Absolute: ESC [ ? 6 h
1558 * XTERM_SEQ: Set Relative: ESC [ ? 6 l
1561 rxvt_term::scr_relative_origin (int mode)
1564 screen.flags |= Screen_Relative;
1566 screen.flags &= ~Screen_Relative;
1567 scr_gotorc (0, 0, 0);
1570 /* ------------------------------------------------------------------------- */
1572 * Set insert/replace mode
1573 * XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
1574 * XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
1577 rxvt_term::scr_insert_mode (int mode)
1580 screen.flags |= Screen_Insert;
1582 screen.flags &= ~Screen_Insert;
1585 /* ------------------------------------------------------------------------- */
1588 * XTERM_SEQ: Set tab at current column : ESC H
1589 * XTERM_SEQ: Clear tab at current column: ESC [ 0 g
1590 * XTERM_SEQ: Clear all tabs : ESC [ 3 g
1593 rxvt_term::scr_set_tab (int mode)
1596 MEMSET (tabs, 0, TermWin.ncol * sizeof (char));
1597 else if (screen.cur.col < TermWin.ncol)
1598 tabs[screen.cur.col] = (mode ? 1 : 0);
1601 /* ------------------------------------------------------------------------- */
1603 * Set reverse/normal video
1604 * XTERM_SEQ: Reverse video: ESC [ ? 5 h
1605 * XTERM_SEQ: Normal video : ESC [ ? 5 l
1608 rxvt_term::scr_rvideo_mode (int mode)
1615 SWAP_IT (PixColors[Color_fg], PixColors[Color_bg], rxvt_color);
1616 #if defined(XPM_BACKGROUND)
1617 if (bgPixmap.pixmap == None)
1619 #if defined(TRANSPARENT)
1620 if (! (Options & Opt_transparent) || am_transparent == 0)
1622 XSetWindowBackground (display->display, TermWin.vt,
1623 PixColors[Color_bg]);
1625 gcvalue.foreground = PixColors[Color_fg];
1626 gcvalue.background = PixColors[Color_bg];
1627 XChangeGC (display->display, TermWin.gc, GCBackground | GCForeground,
1634 /* ------------------------------------------------------------------------- */
1636 * Report current cursor position
1637 * XTERM_SEQ: Report position: ESC [ 6 n
1640 rxvt_term::scr_report_position ()
1642 tt_printf ("\033[%d;%dR", screen.cur.row + 1, screen.cur.col + 1);
1645 /* ------------------------------------------------------------------------- *
1647 * ------------------------------------------------------------------------- */
1653 rxvt_term::set_font_style ()
1655 switch (charsets[screen.charset])
1657 case '0': /* DEC Special Character & Line Drawing Set */
1659 case 'A': /* United Kingdom (UK) */
1661 case 'B': /* United States (USASCII) */
1663 case '<': /* Multinational character set */
1665 case '5': /* Finnish character set */
1667 case 'C': /* Finnish character set */
1669 case 'K': /* German character set */
1674 /* ------------------------------------------------------------------------- */
1677 * XTERM_SEQ: Invoke G0 character set: CTRL-O
1678 * XTERM_SEQ: Invoke G1 character set: CTRL-N
1679 * XTERM_SEQ: Invoke G2 character set: ESC N
1680 * XTERM_SEQ: Invoke G3 character set: ESC O
1683 rxvt_term::scr_charset_choose (int set)
1685 screen.charset = set;
1689 /* ------------------------------------------------------------------------- */
1692 * XTERM_SEQ: Set G0 character set: ESC ( <C>
1693 * XTERM_SEQ: Set G1 character set: ESC ) <C>
1694 * XTERM_SEQ: Set G2 character set: ESC * <C>
1695 * XTERM_SEQ: Set G3 character set: ESC + <C>
1696 * See set_font_style for possible values for <C>
1699 rxvt_term::scr_charset_set (int set, unsigned int ch)
1701 charsets[set] = (unsigned char)ch;
1706 /* ------------------------------------------------------------------------- *
1707 * MAJOR SCREEN MANIPULATION *
1708 * ------------------------------------------------------------------------- */
1711 * refresh matching text.
1714 rxvt_term::scr_refresh_rend (rend_t mask, rend_t value)
1718 for (int i = 0; i < TermWin.nrow; i++)
1721 rend_t *drp = drawn_rend [i];
1723 for (; col < TermWin.ncol; col++, drp++)
1724 if ((*drp & mask) == value)
1744 rxvt_term::scr_expose (int x, int y, int width, int height, bool refresh)
1747 row_col_t rc[RC_COUNT];
1749 if (drawn_text == NULL) /* sanity check */
1754 x = min (x, (int)TermWin.width);
1756 y = min (y, (int)TermWin.height);
1760 rc[PART_BEG].col = Pixel2Col (x);
1761 rc[PART_BEG].row = Pixel2Row (y);
1763 rc[PART_END].col = Pixel2Width (x + width + TermWin.fwidth - 1);
1764 rc[PART_END].row = Pixel2Row (y + height + TermWin.fheight - 1);
1767 for (i = PART_BEG; i < RC_COUNT; i++)
1769 MIN_IT (rc[i].col, TermWin.ncol - 1);
1770 MIN_IT (rc[i].row, TermWin.nrow - 1);
1773 D_SCREEN ((stderr, "rxvt_scr_expose (x:%d, y:%d, w:%d, h:%d) area (c:%d,r:%d)- (c:%d,r:%d)", x, y, width, height, rc[PART_BEG].col, rc[PART_BEG].row, rc[PART_END].col, rc[PART_END].row));
1775 for (i = rc[PART_BEG].row; i <= rc[PART_END].row; i++)
1776 fill_text (&drawn_text[i][rc[PART_BEG].col], 0, rc[PART_END].col - rc[PART_BEG].col + 1);
1779 scr_refresh (SLOW_REFRESH | REFRESH_BOUNDS);
1782 /* ------------------------------------------------------------------------- */
1784 * Refresh the entire screen
1787 rxvt_term::scr_touch (bool refresh)
1789 scr_expose (0, 0, TermWin.width, TermWin.height, refresh);
1792 /* ------------------------------------------------------------------------- */
1794 * Move the display so that the line represented by scrollbar value Y is at
1795 * the top of the screen
1798 rxvt_term::scr_move_to (int y, int len)
1801 uint16_t oldviewstart;
1803 oldviewstart = TermWin.view_start;
1806 p = (TermWin.nrow + TermWin.nscrolled) * (len - y) / len;
1807 p -= (long) (TermWin.nrow - 1);
1810 TermWin.view_start = (uint16_t)min (p, TermWin.nscrolled);
1811 D_SCREEN ((stderr, "rxvt_scr_move_to (%d, %d) view_start:%d", y, len, TermWin.view_start));
1813 return scr_changeview (oldviewstart);
1816 /* ------------------------------------------------------------------------- */
1818 * Page the screen up/down nlines
1819 * direction should be UP or DN
1822 rxvt_term::scr_page (enum page_dirn direction, int nlines)
1825 uint16_t oldviewstart;
1827 D_SCREEN ((stderr, "rxvt_scr_page (%s, %d) view_start:%d", ((direction == UP) ? "UP" : "DN"), nlines, TermWin.view_start));
1829 assert ((nlines >= 0) && (nlines <= TermWin.nrow));
1831 oldviewstart = TermWin.view_start;
1832 if (direction == UP)
1834 n = TermWin.view_start + nlines;
1835 TermWin.view_start = min (n, TermWin.nscrolled);
1839 n = TermWin.view_start - nlines;
1840 TermWin.view_start = max (n, 0);
1842 return scr_changeview (oldviewstart);
1846 rxvt_term::scr_changeview (uint16_t oldviewstart)
1848 if (TermWin.view_start != oldviewstart)
1851 num_scr -= (TermWin.view_start - oldviewstart);
1854 return (int) (TermWin.view_start - oldviewstart);
1857 /* ------------------------------------------------------------------------- */
1859 rxvt_term::scr_bell ()
1862 # ifndef NO_MAPALERT
1863 # ifdef MAPALERT_OPTION
1864 if (Options & Opt_mapAlert)
1866 XMapWindow (display->display, TermWin.parent[0]);
1868 if (Options & Opt_visualBell)
1870 scr_rvideo_mode (!rvideo); /* refresh also done */
1871 scr_rvideo_mode (!rvideo); /* refresh also done */
1874 XBell (display->display, 0);
1878 /* ------------------------------------------------------------------------- */
1881 rxvt_term::scr_printscreen (int fullhist)
1884 int i, r1, nrows, row_offset;
1888 if ((fd = popen_printer ()) == NULL)
1890 nrows = TermWin.nrow;
1891 row_offset = TermWin.saveLines;
1893 row_offset -= TermWin.view_start;
1896 nrows += TermWin.nscrolled;
1897 row_offset -= TermWin.nscrolled;
1900 for (r1 = 0; r1 < nrows; r1++)
1902 t = screen.text[r1 + row_offset];
1903 for (i = TermWin.ncol - 1; i >= 0; i--)
1904 if (!isspace (t[i]))
1906 fprintf (fd, "%.*s\n", (i + 1), t);
1908 pclose_printer (fd);
1912 /* ------------------------------------------------------------------------- */
1914 * Refresh the screen
1915 * drawn_text/drawn_rend contain the screen information before the update.
1916 * screen.text/screen.rend contain what the screen will change to.
1919 #define FONT_WIDTH(X, Y) \
1920 (X)->per_char[ (Y) - (X)->min_char_or_byte2].width
1921 #define FONT_RBEAR(X, Y) \
1922 (X)->per_char[ (Y) - (X)->min_char_or_byte2].rbearing
1923 #define FONT_LBEAR(X, Y) \
1924 (X)->per_char[ (Y) - (X)->min_char_or_byte2].lbearing
1925 #define IS_FONT_CHAR(X, Y) \
1926 ((Y) >= (X)->min_char_or_byte2 && (Y) <= (X)->max_char_or_byte2)
1929 rxvt_term::scr_refresh (unsigned char refresh_type)
1931 unsigned char clearfirst, /* first character writes before cell */
1932 clearlast, /* last character writes beyond cell */
1933 must_clear, /* use draw_string not draw_image_string */
1934 rvid, /* reverse video this position */
1935 showcursor; /* show the cursor */
1936 int16_t col, row, /* column/row we're processing */
1937 ocrow; /* old cursor row */
1939 row_offset; /* basic offset in screen structure */
1940 #ifndef NO_CURSORCOLOR
1941 rend_t cc1; /* store colours at cursor position (s) */
1943 rend_t *drp, *srp; /* drawn-rend-pointer, screen-rend-pointer */
1944 text_t *dtp, *stp; /* drawn-text-pointer, screen-text-pointer */
1946 if (refresh_type == NO_REFRESH || !TermWin.mapped)
1952 clearfirst = clearlast = must_clear = 0;
1956 row_offset = TermWin.saveLines - TermWin.view_start;
1958 if ((refresh_type & REFRESH_BOUNDS))
1960 clearfirst = clearlast = 1;
1961 refresh_type &= ~REFRESH_BOUNDS;
1964 #ifdef XPM_BACKGROUND
1965 must_clear |= (bgPixmap.pixmap != None);
1968 must_clear |= ((Options & Opt_transparent) && am_transparent);
1970 ocrow = oldcursor.row; /* is there an old outline cursor on screen? */
1973 * B: reverse any characters which are selected
1975 scr_reverse_selection ();
1978 * C: set the cursor character (s)
1981 unsigned char setoldcursor;
1982 rend_t ccol1, /* Cursor colour */
1983 ccol2; /* Cursor colour2 */
1985 showcursor = (screen.flags & Screen_VisibleCursor);
1993 srp = &(screen.rend[screen.cur.row + TermWin.saveLines][screen.cur.col]);
1995 if (showcursor && TermWin.focus)
1998 #ifndef NO_CURSORCOLOR
1999 cc1 = *srp & (RS_fgMask | RS_bgMask);
2000 if (ISSET_PIXCOLOR (Color_cursor))
2001 ccol1 = Color_cursor;
2003 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2004 ccol1 = GET_FGCOLOR (rstyle);
2008 if (ISSET_PIXCOLOR (Color_cursor2))
2009 ccol2 = Color_cursor2;
2011 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2012 ccol2 = GET_BGCOLOR (rstyle);
2016 *srp = SET_FGCOLOR (*srp, ccol1);
2017 *srp = SET_BGCOLOR (*srp, ccol2);
2022 /* make sure no outline cursor is left around */
2026 if (screen.cur.row + TermWin.view_start != ocrow
2027 || screen.cur.col != oldcursor.col)
2029 if (ocrow < TermWin.nrow
2030 && oldcursor.col < TermWin.ncol)
2031 drawn_rend[ocrow][oldcursor.col] ^= (RS_RVid | RS_Uline);
2033 if (TermWin.focus || !showcursor)
2039 else if (!TermWin.focus)
2044 if (screen.cur.row + TermWin.view_start >= TermWin.nrow)
2048 oldcursor.row = screen.cur.row + TermWin.view_start;
2049 oldcursor.col = screen.cur.col;
2054 #ifndef NO_SLOW_LINK_SUPPORT
2056 * D: CopyArea pass - very useful for slower links
2057 * This has been deliberately kept simple.
2060 if (refresh_type == FAST_REFRESH && num_scr_allow && i
2061 && abs (i) < TermWin.nrow && !must_clear)
2071 row = i > 0 ? 0 : j - 1;
2072 for (; j-- >= 0; row += (i > 0 ? 1 : -1))
2074 if (row + i >= 0 && row + i < TermWin.nrow && row + i != ocrow)
2076 stp = screen.text[row + row_offset];
2077 srp = screen.rend[row + row_offset];
2078 dtp = drawn_text[row];
2079 dtp2 = drawn_text[row + i];
2080 drp = drawn_rend[row];
2081 drp2 = drawn_rend[row + i];
2083 for (nits = 0, col = TermWin.ncol; col--; )
2084 if (stp[col] != dtp2[col] || srp[col] != drp2[col])
2086 else if (stp[col] != dtp[col] || srp[col] != drp[col])
2089 if (nits > 8) /* XXX: arbitrary choice */
2091 for (col = TermWin.ncol; col--; )
2107 /* also comes here at end if needed because of >= above */
2109 SWAP_IT (wlen, len, int);
2111 D_SCREEN ((stderr, "rxvt_scr_refresh (): XCopyArea: %d -> %d (height: %d)", len + i, len, wlen - len + 1));
2112 XCopyArea (display->display, TermWin.vt, TermWin.vt,
2113 TermWin.gc, 0, Row2Pixel (len + i),
2114 (unsigned int)TermWin_TotalWidth (),
2115 (unsigned int)Height2Pixel (wlen - len + 1),
2116 0, Row2Pixel (len));
2124 * E: main pass across every character
2126 for (row = 0; row < TermWin.nrow; row++)
2128 stp = screen.text[row + row_offset];
2129 srp = screen.rend[row + row_offset];
2130 dtp = drawn_text[row];
2131 drp = drawn_rend[row];
2134 * E2: OK, now the real pass
2136 int ypixel = (int)Row2Pixel (row);
2138 for (col = 0; col < TermWin.ncol; col++)
2140 /* compare new text with old - if exactly the same then continue */
2141 if (stp[col] == dtp[col] /* Must match characters to skip. */
2142 && (srp[col] == drp[col] /* Either rendition the same or */
2143 || (stp[col] == ' ' /* space w/ no background change */
2144 && GET_BGATTR (srp[col]) == GET_BGATTR (drp[col]))))
2147 // redraw one or more characters
2149 // seek to the beginning if wide characters
2150 while (stp[col] == NOCHAR && col > 0)
2153 rend_t rend = srp[col]; /* screen rendition (target rendtion) */
2154 text_t *text = stp + col;
2157 dtp[col] = stp[col];
2160 int xpixel = Col2Pixel (col);
2162 // this loop looks very messy, it can probably be optimized
2163 // and cleaned a bit by you?
2164 for (i = 0; ++col < TermWin.ncol; )
2166 if (stp[col] == NOCHAR)
2168 dtp[col] = stp[col];
2172 if (i) // only possible skip if char unchanged
2178 if (rend != srp[col])
2183 if (stp[col] != dtp[col]
2184 || srp[col] != drp[col])
2186 if (must_clear && (i++ > (count / 2)))
2189 dtp[col] = stp[col];
2193 else if (must_clear || (stp[col] != ' ' && ++i >= 32))
2197 col--; /* went one too far. move back */
2198 count -= i; /* dump any matching trailing chars */
2201 * Determine the attributes for the string
2203 int fid = GET_FONT (rend);
2204 int fore = GET_FGCOLOR (rend); // desired foreground
2205 int back = GET_BGCOLOR (rend); // desired background
2207 rend = GET_ATTR (rend);
2209 rvid = !!(rend & RS_RVid);
2211 #ifndef NO_BOLD_UNDERLINE_REVERSE
2212 if (rend & RS_Bold && fore == Color_fg && !(Options & Opt_realBold))
2214 if (ISSET_PIXCOLOR (Color_BD))
2220 if (rend & RS_Uline)
2221 if (ISSET_PIXCOLOR (Color_UL))
2227 SWAP_IT (fore, back, int);
2229 #ifndef NO_BOLD_UNDERLINE_REVERSE
2230 if (ISSET_PIXCOLOR (Color_RV)
2231 # ifndef NO_CURSORCOLOR
2232 && !ISSET_PIXCOLOR (Color_cursor)
2240 if (rend & RS_Blink && back == Color_bg)
2242 if (!text_blink_ev.active)
2244 text_blink_ev.start (NOW + TEXT_BLINK_INTERVAL);
2247 else if (hidden_text)
2253 * Actually do the drawing of the string here
2255 rxvt_font *font = (*TermWin.fontset)[fid];
2258 font->clear_rect (*TermWin.drawable, xpixel, ypixel,
2259 TermWin.fwidth * count, TermWin.fheight,
2261 else if (back == Color_bg)
2265 CLEAR_CHARS (xpixel, ypixel, count);
2267 for (i = 0; i < count; i++) /* don't draw empty strings */
2270 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, -1);
2275 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, Color_bg);
2278 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, back);
2280 if ((rend & RS_Uline) && (font->descent > 1))
2281 XDrawLine (display->display, drawBuffer, TermWin.gc,
2282 xpixel, ypixel + font->ascent + 1,
2283 xpixel + Width2Pixel (count) - 1, ypixel + font->ascent + 1);
2284 } /* for (col....) */
2285 } /* for (row....) */
2288 * G: cleanup cursor and display outline cursor if necessary
2294 srp = & (screen.rend[screen.cur.row + TermWin.saveLines]
2297 #ifndef NO_CURSORCOLOR
2298 *srp = (*srp & ~ (RS_fgMask | RS_bgMask)) | cc1;
2301 else if (oldcursor.row >= 0)
2303 #ifndef NO_CURSORCOLOR
2304 if (ISSET_PIXCOLOR (Color_cursor))
2305 XSetForeground (display->display, TermWin.gc, PixColors[Color_cursor]);
2307 int cursorwidth = 1;
2308 while (oldcursor.col + cursorwidth < TermWin.ncol
2309 && drawn_text[oldcursor.row][oldcursor.col + cursorwidth] == NOCHAR)
2312 XDrawRectangle (display->display, drawBuffer, TermWin.gc,
2313 Col2Pixel (oldcursor.col),
2314 Row2Pixel (oldcursor.row),
2315 (unsigned int) (Width2Pixel (cursorwidth) - 1),
2316 (unsigned int) (Height2Pixel (1) - TermWin.lineSpace - 1));
2321 * H: cleanup selection
2323 scr_reverse_selection ();
2326 * I: other general cleanup
2329 if (clearfirst && TermWin.int_bwidth)
2331 * clear the whole screen height, note that width == 0 is treated
2332 * specially by XClearArea
2334 XClearArea (display->display, TermWin.vt, 0, 0,
2335 (unsigned int)TermWin.int_bwidth,
2336 (unsigned int)TermWin_TotalHeight (), False);
2338 if (clearlast && TermWin.int_bwidth)
2340 * clear the whole screen height, note that width == 0 is treated
2341 * specially by XClearArea
2343 XClearArea (display->display, TermWin.vt,
2344 TermWin.width + TermWin.int_bwidth, 0,
2345 (unsigned int)TermWin.int_bwidth,
2346 (unsigned int)TermWin_TotalHeight (), False);
2349 if (refresh_type & SMOOTH_REFRESH)
2350 XSync (display->display, False);
2354 want_refresh = 0; /* screen is current */
2358 rxvt_term::scr_remap_chars (text_t *tp, rend_t *rp)
2363 for (int i = TermWin.ncol; i; i--, rp++, tp++)
2364 *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
2368 rxvt_term::scr_remap_chars ()
2370 for (int i = TermWin.nrow + TermWin.saveLines; i--; )
2371 scr_remap_chars (screen.text[i], screen.rend[i]);
2373 for (int i = TermWin.nrow; i--; )
2375 scr_remap_chars (drawn_text[i], drawn_rend[i]);
2376 scr_remap_chars (swap.text[i], swap.rend[i]);
2380 /* ------------------------------------------------------------------------- */
2382 rxvt_term::scr_clear (bool really)
2384 if (!TermWin.mapped)
2391 if ((Options & Opt_transparent) && (am_pixmap_trans == 0))
2395 if (! (Options & Opt_transparent_all))
2398 i = (int) (sizeof (TermWin.parent) / sizeof (Window));
2401 if (TermWin.parent[i] != None)
2402 XClearWindow (display->display, TermWin.parent[i]);
2407 XClearWindow (display->display, TermWin.vt);
2410 /* ------------------------------------------------------------------------- */
2412 rxvt_term::scr_reverse_selection ()
2414 if (selection.op && current_screen == selection.screen)
2416 int end_row = TermWin.saveLines - TermWin.view_start;
2417 int i = selection.beg.row + TermWin.saveLines;
2418 int col, row = selection.end.row + TermWin.saveLines;
2422 col = selection.beg.col;
2429 end_row += TermWin.nrow;
2430 for (; i < row && i < end_row; i++, col = 0)
2431 for (srp = screen.rend[i]; col < TermWin.ncol; col++)
2432 srp[col] ^= RS_RVid;
2434 if (i == row && i < end_row)
2435 for (srp = screen.rend[i]; col < selection.end.col; col++)
2436 srp[col] ^= RS_RVid;
2440 /* ------------------------------------------------------------------------- */
2442 * Dump the whole scrollback and screen to the passed filedescriptor. The
2443 * invoking routine must close the fd.
2447 rxvt_term::scr_dump (int fd)
2450 unsigned int width, towrite;
2453 for (row = TermWin.saveLines - TermWin.nscrolled;
2454 row < TermWin.saveLines + TermWin.nrow - 1; row++)
2456 width = screen.tlen[row] >= 0 ? screen.tlen[row]
2458 for (towrite = width; towrite; towrite -= wrote)
2460 wrote = write (fd, & (screen.text[row][width - towrite]),
2463 return; /* XXX: death, no report */
2465 if (screen.tlen[row] >= 0)
2466 if (write (fd, r1, 1) <= 0)
2467 return; /* XXX: death, no report */
2472 /* ------------------------------------------------------------------------- *
2473 * CHARACTER SELECTION *
2474 * ------------------------------------------------------------------------- */
2477 * -TermWin.nscrolled <= (selection row) <= TermWin.nrow - 1
2480 rxvt_term::selection_check (int check_more)
2487 pos.row = pos.col = 0;
2488 if ((selection.beg.row < - (int32_t)TermWin.nscrolled)
2489 || (selection.beg.row >= TermWin.nrow)
2490 || (selection.mark.row < - (int32_t)TermWin.nscrolled)
2491 || (selection.mark.row >= TermWin.nrow)
2492 || (selection.end.row < - (int32_t)TermWin.nscrolled)
2493 || (selection.end.row >= TermWin.nrow)
2495 && current_screen == selection.screen
2496 && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
2497 && ROWCOL_IS_BEFORE (screen.cur, selection.end))
2499 && ROWCOL_IS_BEFORE (selection.beg, pos)
2500 && ROWCOL_IS_AFTER (selection.end, pos))
2502 && ROWCOL_IS_AFTER (selection.end, pos))
2503 || (check_more == 4 /* screen width change */
2504 && (selection.beg.row != selection.end.row
2505 || selection.end.col > TermWin.ncol)))
2509 /* ------------------------------------------------------------------------- */
2511 * Paste a selection direct to the command fd
2514 rxvt_term::paste (const unsigned char *data, unsigned int len)
2516 unsigned int i, j, n;
2517 unsigned char *ds = (unsigned char *)rxvt_malloc (PROP_SIZE);
2520 /* a paste should act like the user is typing, so check scrollTtyKeypress */
2521 ZERO_SCROLLBACK (r);
2524 /* convert normal newline chars into common keyboard Return key sequence */
2525 for (i = 0; i < len; i += PROP_SIZE)
2527 n = min (len - i, PROP_SIZE);
2528 MEMCPY (ds, data + i, n);
2530 for (j = 0; j < n; j++)
2534 tt_write (ds, (int)n);
2540 /* ------------------------------------------------------------------------- */
2542 * Respond to a notification that a primary selection has been sent
2543 * EXT: SelectionNotify
2546 rxvt_term::selection_paste (Window win, Atom prop, bool delete_prop)
2549 unsigned long bytes_after;
2552 D_SELECT ((stderr, "rxvt_selection_paste (%08lx, %lu, %d), wait=%2x", win, (unsigned long)prop, (int)delete_prop, selection_wait));
2554 if (prop == None) /* check for failed XConvertSelection */
2556 if ((selection_type & Sel_CompoundText))
2558 int selnum = selection_type & Sel_whereMask;
2561 if (selnum != Sel_direct)
2562 selection_request_other (XA_STRING, selnum);
2565 if ((selection_type & Sel_UTF8String))
2567 int selnum = selection_type & Sel_whereMask;
2569 selection_type = Sel_CompoundText;
2570 if (selnum != Sel_direct)
2571 selection_request_other (xa[XA_COMPOUND_TEXT], selnum);
2581 if (XGetWindowProperty (display->display, win, prop, (long) (nread / 4),
2582 (long) (PROP_SIZE / 4), delete_prop,
2583 AnyPropertyType, &ct.encoding, &ct.format,
2584 &ct.nitems, &bytes_after,
2585 &ct.value) != Success)
2588 if (ct.encoding == 0)
2590 D_SELECT ((stderr, "rxvt_selection_paste: property didn't exist!"));
2594 if (ct.value == NULL)
2596 D_SELECT ((stderr, "rxvt_selection_paste: property shooting blanks!"));
2602 D_SELECT ((stderr, "rxvt_selection_paste: property empty - also INCR end"));
2604 if (selection_wait == Sel_normal && nread == 0
2605 && (win != display->root || prop != XA_CUT_BUFFER0)) // avoid recursion
2608 * pass through again trying CUT_BUFFER0 if we've come from
2609 * XConvertSelection () but nothing was presented
2611 D_SELECT ((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2612 selection_paste (display->root, XA_CUT_BUFFER0, False);
2615 nread = -1; /* discount any previous stuff */
2623 if (XmbTextPropertyToTextList (display->display, &ct, &cl, &cr) >= 0 && cl)
2625 for (int i = 0; i < cr; i++)
2626 paste ((unsigned char *)cl[i], STRLEN (cl[i]));
2628 XFreeStringList (cl);
2631 paste (ct.value, ct.nitems);
2633 if (bytes_after == 0)
2642 if (selection_wait == Sel_normal)
2643 selection_wait = Sel_none;
2645 D_SELECT ((stderr, "rxvt_selection_paste: bytes written: %ld", nread));
2650 rxvt_term::incr_cb (time_watcher &w)
2652 selection_wait = Sel_none;
2654 rxvt_warn ("data loss: timeout on INCR selection paste, ignoring.\n");
2658 * INCR support originally provided by Paul Sheer <psheer@obsidian.co.za>
2661 rxvt_term::selection_property (Window win, Atom prop)
2668 D_SELECT ((stderr, "rxvt_selection_property (%08lx, %lu)", win, (unsigned long)prop));
2669 if (selection_wait == Sel_normal)
2673 unsigned long bytes_after, nitems;
2674 unsigned char *s = NULL;
2676 a = XGetWindowProperty (display->display, win, prop, 0L, 1L, False,
2677 xa[XA_INCR], &atype, &afmt, &nitems,
2684 #ifndef __CYGWIN32__
2685 if (atype == xa[XA_INCR])
2686 { /* start an INCR transfer */
2687 D_SELECT ((stderr, "rxvt_selection_property: INCR: starting transfer"));
2688 XDeleteProperty (display->display, win, prop);
2689 XFlush (display->display);
2691 selection_wait = Sel_incr;
2696 else if (selection_wait == Sel_incr)
2700 if (selection_paste (win, prop, True) == -1)
2702 D_SELECT ((stderr, "rxvt_selection_property: INCR: clean end"));
2703 selection_wait = Sel_none;
2707 if (reget_time) /* received more data so reget time */
2708 incr_ev.start (NOW + 10);
2711 /* ------------------------------------------------------------------------- */
2713 * Request the current selection:
2714 * Order: > internal selection if available
2715 * > PRIMARY, SECONDARY, CLIPBOARD if ownership is claimed (+)
2717 * (+) if ownership is claimed but property is empty, rxvt_selection_paste ()
2718 * will auto fallback to CUT_BUFFER0
2719 * EXT: button 2 release
2722 rxvt_term::selection_request (Time tm, int x, int y)
2724 D_SELECT ((stderr, "rxvt_selection_request (%lu, %d, %d)", tm, x, y));
2726 if (x < 0 || x >= TermWin.width || y < 0 || y >= TermWin.height)
2727 return; /* outside window */
2730 { /* internal selection */
2731 D_SELECT ((stderr, "rxvt_selection_request: pasting internal"));
2732 char *str = rxvt_wcstombs (selection.text, selection.len);
2733 paste ((unsigned char *)str, strlen (str));
2741 selection_request_time = tm;
2742 selection_wait = Sel_normal;
2744 for (i = Sel_Primary; i <= Sel_Clipboard; i++)
2746 #if X_HAVE_UTF8_STRING
2747 selection_type = Sel_UTF8String;
2748 if (selection_request_other (xa[XA_UTF8_STRING], i))
2751 selection_type = Sel_CompoundText;
2752 if (selection_request_other (xa[XA_COMPOUND_TEXT], i))
2759 selection_wait = Sel_none; /* don't loop in rxvt_selection_paste () */
2760 D_SELECT ((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2761 selection_paste (display->root, XA_CUT_BUFFER0, False);
2765 rxvt_term::selection_request_other (Atom target, int selnum)
2769 char *debug_xa_names[] = { "PRIMARY", "SECONDARY", "CLIPBOARD" };
2772 selection_type |= selnum;
2774 if (selnum == Sel_Primary)
2776 else if (selnum == Sel_Secondary)
2779 sel = xa[XA_CLIPBOARD];
2781 if (XGetSelectionOwner (display->display, sel) != None)
2783 D_SELECT ((stderr, "rxvt_selection_request_other: pasting %s", debug_xa_names[selnum]));
2784 XConvertSelection (display->display, sel, target, xa[XA_VT_SELECTION],
2785 TermWin.vt, selection_request_time);
2792 /* ------------------------------------------------------------------------- */
2794 * Clear all selected text
2795 * EXT: SelectionClear
2798 rxvt_term::selection_clear ()
2800 D_SELECT ((stderr, "rxvt_selection_clear ()"));
2803 free (selection.text);
2804 selection.text = NULL;
2808 if (display->selection_owner == this)
2809 display->selection_owner = 0;
2812 /* ------------------------------------------------------------------------- */
2814 * Copy a selection into the cut buffer
2815 * EXT: button 1 or 3 release
2818 rxvt_term::selection_make (Time tm)
2820 int i, col, end_col, row, end_row;
2821 wchar_t *new_selection_text;
2824 D_SELECT ((stderr, "rxvt_selection_make (): selection.op=%d, selection.clicks=%d", selection.op, selection.clicks));
2825 switch (selection.op)
2827 case SELECTION_CONT:
2829 case SELECTION_INIT:
2832 case SELECTION_BEGIN:
2833 selection.op = SELECTION_DONE;
2839 selection.op = SELECTION_DONE;
2841 if (selection.clicks == 4)
2842 return; /* nothing selected, go away */
2844 i = (selection.end.row - selection.beg.row + 1) * (TermWin.ncol + 1);
2845 new_selection_text = (wchar_t *)rxvt_malloc ((i + 4) * sizeof (wchar_t));
2847 col = selection.beg.col;
2849 row = selection.beg.row + TermWin.saveLines;
2850 end_row = selection.end.row + TermWin.saveLines;
2854 for (; row <= end_row; row++, col = 0)
2856 t = &(screen.text[row][col]);
2858 end_col = screen.tlen[row];
2861 end_col = TermWin.ncol;
2864 MIN_IT (end_col, selection.end.col);
2866 for (; col < end_col; col++)
2870 #if ENABLE_COMBINING
2871 else if (IS_COMPOSE (*t))
2873 int len = rxvt_composite.expand (*t, 0);
2881 new_selection_text = (wchar_t *)rxvt_realloc (new_selection_text, (i + 4) * sizeof (wchar_t));
2884 ofs += rxvt_composite.expand (*t++, new_selection_text + ofs);
2888 new_selection_text[ofs++] = *t++;
2891 if (screen.tlen[row] != -1 && row != end_row)
2892 new_selection_text[ofs++] = C0_LF;
2895 if (end_col != selection.end.col)
2896 new_selection_text[ofs++] = C0_LF;
2898 new_selection_text[ofs] = 0;
2902 free (new_selection_text);
2906 free (selection.text);
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));
2912 XSetSelectionOwner (display->display, XA_PRIMARY, TermWin.vt, tm);
2913 if (XGetSelectionOwner (display->display, XA_PRIMARY) == TermWin.vt)
2914 display->set_selection_owner (this);
2916 rxvt_warn ("can't get primary selection, ignoring.\n");
2921 if (XwcTextListToTextProperty (display->display, &selection.text, 1, XStringStyle, &ct) >= 0)
2923 XChangeProperty (display->display, display->root, XA_CUT_BUFFER0, XA_STRING, 8,
2924 PropModeReplace, ct.value, ct.nitems);
2929 selection_time = tm;
2930 D_SELECT ((stderr, "rxvt_selection_make (): selection.len=%d", selection.len));
2933 /* ------------------------------------------------------------------------- */
2935 * Mark or select text based upon number of clicks: 1, 2, or 3
2936 * EXT: button 1 press
2939 rxvt_term::selection_click (int clicks, int x, int y)
2941 D_SELECT ((stderr, "rxvt_selection_click (%d, %d, %d)", clicks, x, y));
2943 clicks = ((clicks - 1) % 3) + 1;
2944 selection.clicks = clicks; /* save clicks so extend will work */
2946 selection_start_colrow (Pixel2Col (x), Pixel2Row (y));
2948 if (clicks == 2 || clicks == 3)
2949 selection_extend_colrow (selection.mark.col,
2950 selection.mark.row + TermWin.view_start,
2952 1, /* button press */
2953 0); /* click change */
2956 /* ------------------------------------------------------------------------- */
2958 * Mark a selection at the specified col/row
2961 rxvt_term::selection_start_colrow (int col, int row)
2964 selection.mark.col = col;
2965 selection.mark.row = row - TermWin.view_start;
2967 MAX_IT (selection.mark.row, - (int32_t)TermWin.nscrolled);
2968 MIN_IT (selection.mark.row, (int32_t)TermWin.nrow - 1);
2969 MAX_IT (selection.mark.col, 0);
2970 MIN_IT (selection.mark.col, (int32_t)TermWin.ncol - 1);
2972 while (selection.mark.col > 0
2973 && screen.text[selection.mark.row + TermWin.saveLines][selection.mark.col] == NOCHAR)
2974 --selection.mark.col;
2977 { /* clear the old selection */
2978 selection.beg.row = selection.end.row = selection.mark.row;
2979 selection.beg.col = selection.end.col = selection.mark.col;
2982 selection.op = SELECTION_INIT;
2983 selection.screen = current_screen;
2986 /* ------------------------------------------------------------------------- */
2988 * Word select: select text for 2 clicks
2989 * We now only find out the boundary in one direction
2992 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
2993 #define DELIMIT_TEXT(x) \
2994 (unicode::is_space (x) ? 2 : (x) <= 0xff && !!STRCHR (rs[Rs_cutchars], (x)))
2995 #define DELIMIT_REND(x) 1
2998 rxvt_term::selection_delimit_word (enum page_dirn dirn, const row_col_t *mark, row_col_t *ret)
3000 int col, row, dirnadd, tcol, trow, w1, w2;
3007 bound.row = TermWin.saveLines - TermWin.nscrolled - 1;
3013 bound.row = TermWin.saveLines + TermWin.nrow;
3014 bound.col = TermWin.ncol - 1;
3018 row = mark->row + TermWin.saveLines;
3021 /* find the edge of a word */
3022 stp = & (screen.text[row][col]);
3023 w1 = DELIMIT_TEXT (*stp);
3025 srp = (&screen.rend[row][col]);
3026 w2 = DELIMIT_REND (*srp);
3030 for (; col != bound.col; col += dirnadd)
3038 if (DELIMIT_TEXT (*stp) != w1)
3040 if (DELIMIT_REND (*srp) != w2)
3044 if ((col == bound.col) && (row != bound.row))
3046 if (screen.tlen[ (row - (dirn == UP ? 1 : 0))] == -1)
3048 trow = row + dirnadd;
3049 tcol = dirn == UP ? TermWin.ncol - 1 : 0;
3051 if (screen.text[trow] == NULL)
3054 stp = & (screen.text[trow][tcol]);
3055 srp = & (screen.rend[trow][tcol]);
3057 if (DELIMIT_TEXT (*stp) != w1 || DELIMIT_REND (*srp) != w2)
3068 Old_Word_Selection_You_Die:
3069 D_SELECT ((stderr, "rxvt_selection_delimit_word (%s,...) @ (r:%3d, c:%3d) has boundary (r:%3d, c:%3d)", (dirn == UP ? "up " : "down"), mark->row, mark->col, row - TermWin.saveLines, col));
3072 col++; /* put us on one past the end */
3074 /* Poke the values back in */
3075 ret->row = row - TermWin.saveLines;
3079 /* ------------------------------------------------------------------------- */
3081 * Extend the selection to the specified x/y pixel location
3082 * EXT: button 3 press; button 1 or 3 drag
3083 * flag == 0 ==> button 1
3084 * flag == 1 ==> button 3 press
3085 * flag == 2 ==> button 3 motion
3088 rxvt_term::selection_extend (int x, int y, int flag)
3092 col = Pixel2Col (x);
3093 row = Pixel2Row (y);
3095 MIN_IT (row, (int)TermWin.nrow - 1);
3097 MIN_IT (col, (int)TermWin.ncol);
3100 * If we're selecting characters (single click) then we must check first
3101 * if we are at the same place as the original mark. If we are then
3102 * select nothing. Otherwise, if we're to the right of the mark, you have to
3103 * be _past_ a character for it to be selected.
3105 if (((selection.clicks % 3) == 1) && !flag
3106 && (col == selection.mark.col
3107 && (row == selection.mark.row + TermWin.view_start)))
3109 /* select nothing */
3110 selection.beg.row = selection.end.row = 0;
3111 selection.beg.col = selection.end.col = 0;
3112 selection.clicks = 4;
3114 D_SELECT ((stderr, "rxvt_selection_extend () selection.clicks = 4"));
3118 if (selection.clicks == 4)
3119 selection.clicks = 1;
3121 selection_extend_colrow (col, row, !!flag, /* ? button 3 */
3122 flag == 1 ? 1 : 0, /* ? button press */
3123 0); /* no click change */
3126 /* ------------------------------------------------------------------------- */
3128 * Extend the selection to the specified col/row
3131 rxvt_term::selection_extend_colrow (int32_t col, int32_t row, int button3, int buttonpress, int clickchange)
3133 int16_t ncol = TermWin.ncol;
3140 D_SELECT ((stderr, "rxvt_selection_extend_colrow (c:%d, r:%d, %d, %d) clicks:%d, op:%d", col, row, button3, buttonpress, selection.clicks, selection.op));
3141 D_SELECT ((stderr, "rxvt_selection_extend_colrow () ENT b: (r:%d,c:%d) m: (r:%d,c:%d), e: (r:%d,c:%d)", selection.beg.row, selection.beg.col, selection.mark.row, selection.mark.col, selection.end.row, selection.end.col));
3144 switch (selection.op)
3146 case SELECTION_INIT:
3148 selection.op = SELECTION_BEGIN;
3150 case SELECTION_BEGIN:
3151 if (row != selection.mark.row || col != selection.mark.col
3152 || (!button3 && buttonpress))
3153 selection.op = SELECTION_CONT;
3155 case SELECTION_DONE:
3156 selection.op = SELECTION_CONT;
3158 case SELECTION_CONT:
3160 case SELECTION_CLEAR:
3161 selection_start_colrow (col, row);
3166 if (selection.beg.col == selection.end.col
3167 && selection.beg.col != selection.mark.col
3168 && selection.beg.row == selection.end.row
3169 && selection.beg.row != selection.mark.row)
3171 selection.beg.col = selection.end.col = selection.mark.col;
3172 selection.beg.row = selection.end.row = selection.mark.row;
3173 D_SELECT ((stderr, "rxvt_selection_extend_colrow () ENT2 b: (r:%d,c:%d) m: (r:%d,c:%d), e: (r:%d,c:%d)", selection.beg.row, selection.beg.col, selection.mark.row, selection.mark.col, selection.end.row, selection.end.col));
3179 pos.row -= TermWin.view_start; /* adjust for scroll */
3182 * This is mainly xterm style selection with a couple of differences, mainly
3183 * in the way button3 drag extension works.
3184 * We're either doing: button1 drag; button3 press; or button3 drag
3185 * a) button1 drag : select around a midpoint/word/line - that point/word/line
3186 * is always at the left/right edge of the selection.
3187 * b) button3 press: extend/contract character/word/line at whichever edge of
3188 * the selection we are closest to.
3189 * c) button3 drag : extend/contract character/word/line - we select around
3190 * a point/word/line which is either the start or end of the selection
3191 * and it was decided by whichever point/word/line was `fixed' at the
3192 * time of the most recent button3 press
3194 if (button3 && buttonpress)
3195 { /* button3 press */
3197 * first determine which edge of the selection we are closest to
3199 if (ROWCOL_IS_BEFORE (pos, selection.beg)
3200 || (!ROWCOL_IS_AFTER (pos, selection.end)
3201 && (((pos.col - selection.beg.col)
3202 + ((pos.row - selection.beg.row) * ncol))
3203 < ((selection.end.col - pos.col)
3204 + ((selection.end.row - pos.row) * ncol)))))
3207 if (closeto == LEFT)
3209 selection.beg.row = pos.row;
3210 selection.beg.col = pos.col;
3211 selection.mark.row = selection.end.row;
3212 selection.mark.col = selection.end.col - (selection.clicks == 2);
3216 selection.end.row = pos.row;
3217 selection.end.col = pos.col;
3218 selection.mark.row = selection.beg.row;
3219 selection.mark.col = selection.beg.col;
3223 { /* button1 drag or button3 drag */
3224 if (ROWCOL_IS_AFTER (selection.mark, pos))
3226 if ((selection.mark.row == selection.end.row)
3227 && (selection.mark.col == selection.end.col)
3228 && clickchange && selection.clicks == 2)
3229 selection.mark.col--;
3231 selection.beg.row = pos.row;
3232 selection.beg.col = pos.col;
3233 selection.end.row = selection.mark.row;
3234 selection.end.col = selection.mark.col + (selection.clicks == 2);
3238 selection.beg.row = selection.mark.row;
3239 selection.beg.col = selection.mark.col;
3240 selection.end.row = pos.row;
3241 selection.end.col = pos.col;
3245 if (selection.clicks == 1)
3247 end_col = screen.tlen[selection.beg.row + TermWin.saveLines];
3249 if (end_col != -1 && selection.beg.col > end_col)
3252 selection.beg.col = ncol;
3254 if (selection.beg.row != selection.end.row)
3255 selection.beg.col = ncol;
3257 selection.beg.col = selection.mark.col;
3261 end_col = screen.tlen[selection.end.row + TermWin.saveLines];
3263 if (end_col != -1 && selection.end.col > end_col)
3264 selection.end.col = ncol;
3266 else if (selection.clicks == 2)
3268 if (ROWCOL_IS_AFTER (selection.end, selection.beg))
3269 selection.end.col--;
3271 selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3272 selection_delimit_word (DN, & (selection.end), & (selection.end));
3274 else if (selection.clicks == 3)
3277 if ((Options & Opt_tripleclickwords))
3281 selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3282 end_row = screen.tlen[selection.mark.row + TermWin.saveLines];
3284 for (end_row = selection.mark.row; end_row < TermWin.nrow; end_row++)
3286 end_col = screen.tlen[end_row + TermWin.saveLines];
3290 selection.end.row = end_row;
3291 selection.end.col = end_col;
3292 selection_remove_trailing_spaces ();
3300 if (ROWCOL_IS_AFTER (selection.mark, selection.beg))
3301 selection.mark.col++;
3302 selection.beg.col = 0;
3303 selection.end.col = ncol;
3307 if (button3 && buttonpress)
3308 { /* mark may need to be changed */
3309 if (closeto == LEFT)
3311 selection.mark.row = selection.end.row;
3312 selection.mark.col = selection.end.col
3313 - (selection.clicks == 2);
3317 selection.mark.row = selection.beg.row;
3318 selection.mark.col = selection.beg.col;
3321 D_SELECT ((stderr, "rxvt_selection_extend_colrow () EXIT b: (r:%d,c:%d) m: (r:%d,c:%d), e: (r:%d,c:%d)", selection.beg.row, selection.beg.col, selection.mark.row, selection.mark.col, selection.end.row, selection.end.col));
3326 rxvt_term::selection_remove_trailing_spaces ()
3328 int32_t end_col, end_row;
3331 end_col = selection.end.col;
3332 end_row = selection.end.row;
3333 for ( ; end_row >= selection.beg.row; )
3335 stp = screen.text[end_row + TermWin.saveLines];
3336 while (--end_col >= 0)
3338 if (stp[end_col] != ' ' && stp[end_col] != '\t')
3342 || screen.tlen[end_row - 1 + TermWin.saveLines] != -1)
3344 selection.end.col = end_col + 1;
3345 selection.end.row = end_row;
3349 end_col = TermWin.ncol;
3351 if (selection.mark.row > selection.end.row)
3353 selection.mark.row = selection.end.row;
3354 selection.mark.col = selection.end.col;
3356 else if (selection.mark.row == selection.end.row
3357 && selection.mark.col > selection.end.col)
3358 selection.mark.col = selection.end.col;
3362 /* ------------------------------------------------------------------------- */
3364 * Double click on button 3 when already selected
3365 * EXT: button 3 double click
3368 rxvt_term::selection_rotate (int x, int y)
3370 selection.clicks = selection.clicks % 3 + 1;
3371 selection_extend_colrow (Pixel2Col (x), Pixel2Row (y), 1, 0, 1);
3374 /* ------------------------------------------------------------------------- */
3376 * On some systems, the Atom typedef is 64 bits wide. We need to have a type
3377 * that is exactly 32 bits wide, because a format of 64 is not allowed by
3380 typedef CARD32 Atom32;
3382 /* ------------------------------------------------------------------------- */
3384 * Respond to a request for our current selection
3385 * EXT: SelectionRequest
3388 rxvt_term::selection_send (const XSelectionRequestEvent &rq)
3392 XICCEncodingStyle style;
3395 ev.type = SelectionNotify;
3397 ev.display = rq.display;
3398 ev.requestor = rq.requestor;
3399 ev.selection = rq.selection;
3400 ev.target = rq.target;
3403 if (rq.target == xa[XA_TARGETS])
3405 Atom32 target_list[6];
3406 Atom32 *target = target_list;
3408 *target++ = (Atom32) xa[XA_TARGETS];
3409 *target++ = (Atom32) xa[XA_TIMESTAMP];
3410 *target++ = (Atom32) XA_STRING;
3411 *target++ = (Atom32) xa[XA_TEXT];
3412 *target++ = (Atom32) xa[XA_COMPOUND_TEXT];
3413 #if X_HAVE_UTF8_STRING
3414 *target++ = (Atom32) xa[XA_UTF8_STRING];
3417 XChangeProperty (display->display, rq.requestor, rq.property, XA_ATOM,
3418 (8 * sizeof (target_list[0])), PropModeReplace,
3419 (unsigned char *)target_list,
3420 target - target_list);
3421 ev.property = rq.property;
3423 else if (rq.target == xa[XA_MULTIPLE])
3425 /* TODO: Handle MULTIPLE */
3427 else if (rq.target == xa[XA_TIMESTAMP] && selection.text)
3429 XChangeProperty (display->display, rq.requestor, rq.property, XA_INTEGER,
3430 (8 * sizeof (Time)), PropModeReplace,
3431 (unsigned char *)&selection_time, 1);
3432 ev.property = rq.property;
3434 else if (rq.target == XA_STRING
3435 || rq.target == xa[XA_TEXT]
3436 || rq.target == xa[XA_COMPOUND_TEXT]
3437 || rq.target == xa[XA_UTF8_STRING]
3446 if (target == XA_STRING)
3447 // we actually don't do XA_STRING, but who cares, as i18n clients
3448 // will ask for another format anyways.
3449 style = XStringStyle;
3450 else if (target == xa[XA_TEXT])
3451 style = XStdICCTextStyle;
3452 else if (target == xa[XA_COMPOUND_TEXT])
3453 style = XCompoundTextStyle;
3454 #if X_HAVE_UTF8_STRING
3455 else if (target == xa[XA_UTF8_STRING])
3456 style = XUTF8StringStyle;
3460 target = xa[XA_COMPOUND_TEXT];
3461 style = XCompoundTextStyle;
3466 cl = selection.text;
3467 selectlen = selection.len;
3475 // Xwc doesn't handle iso-10646 in wchar_t gracefully, so maybe recode it
3476 // manually for XUTF8StringStyle.
3477 if (XwcTextListToTextProperty (display->display, &cl, 1, style, &ct) >= 0)
3481 /* if we failed to convert then send it raw */
3482 ct.value = (unsigned char *)cl;
3483 ct.nitems = selectlen;
3486 XChangeProperty (display->display, rq.requestor, rq.property,
3487 target, 8, PropModeReplace,
3488 ct.value, (int)ct.nitems);
3489 ev.property = rq.property;
3495 XSendEvent (display->display, rq.requestor, False, 0L, (XEvent *)&ev);
3498 /* ------------------------------------------------------------------------- *
3500 * ------------------------------------------------------------------------- */
3503 * return col/row values corresponding to x/y pixel values
3506 rxvt_term::pixel_position (int *x, int *y)
3508 *x = Pixel2Col (*x);
3509 /* MAX_IT (*x, 0); MIN_IT (*x, (int)TermWin.ncol - 1); */
3510 *y = Pixel2Row (*y);
3511 /* MAX_IT (*y, 0); MIN_IT (*y, (int)TermWin.nrow - 1); */
3514 /* ------------------------------------------------------------------------- */
3517 rxvt_term::im_set_position (XPoint *pos)
3519 XWindowAttributes xwa;
3521 XGetWindowAttributes (display->display, TermWin.vt, &xwa);
3522 pos->x = Col2Pixel (screen.cur.col) + xwa.x;
3523 pos->y = Height2Pixel ((screen.cur.row + 1)) + xwa.y - TermWin.lineSpace;
3527 /* ------------------------------------------------------------------------- */