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) == 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, TermWin.int_bwidth, \
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 TermWin.int_bwidth, 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;
264 assert (screen.rend[q]);
266 talloc->free (screen.text[q]);
267 ralloc->free (screen.rend[q]);
272 assert (swap.rend[p]);
274 talloc->free (swap.text[p]);
275 ralloc->free (swap.rend[p]);
278 assert (drawn_text[p] && drawn_rend[p]);
280 talloc->free (drawn_text[p]);
281 ralloc->free (drawn_rend[p]);
284 /* we have fewer rows so fix up cursor position */
285 MIN_IT (screen.cur.row, (int32_t)nrow - 1);
286 MIN_IT (swap.cur.row, (int32_t)nrow - 1);
288 scr_reset_realloc (); /* realloc _last_ */
290 else if (nrow > prev_nrow)
293 scr_reset_realloc (); /* realloc _first_ */
295 TermWin.ncol = prev_ncol; // save b/c scr_blank_screen_mem uses this
297 k = min (TermWin.nscrolled, nrow - prev_nrow);
299 for (p = prev_total_rows; p < total_rows; p++)
302 screen.text[p] = NULL;
303 screen.rend[p] = NULL;
306 for (p = prev_total_rows; p < total_rows - k; p++)
307 scr_blank_screen_mem (screen.text, screen.rend, p, setrstyle);
309 for (p = prev_nrow; p < nrow; p++)
314 drawn_text[p] = NULL;
315 drawn_rend[p] = NULL;
316 scr_blank_screen_mem (swap.text, swap.rend, p, setrstyle);
317 scr_blank_screen_mem (drawn_text, drawn_rend, p, setrstyle);
322 scr_scroll_text (0, (int)nrow - 1, -k, 1);
324 screen.s_cur.row += k;
325 TermWin.nscrolled -= k;
328 assert (screen.cur.row < TermWin.nrow);
329 assert (swap.cur.row < TermWin.nrow);
330 #else /* drive with your eyes closed */
332 MIN_IT (screen.cur.row, nrow - 1);
333 MIN_IT (swap.cur.row, nrow - 1);
335 TermWin.ncol = ncol; // save b/c scr_blank_screen_mem uses this
339 if (ncol != prev_ncol)
341 int common = min (prev_ncol, ncol);
342 rxvt_salloc *ta = new rxvt_salloc (ncol * sizeof (text_t));
343 rxvt_salloc *ra = new rxvt_salloc (ncol * sizeof (rend_t));
345 for (p = 0; p < total_rows; p++)
349 text_t *t = (text_t *)ta->alloc (); memcpy (t, screen.text[p], common * sizeof (text_t)); screen.text[p] = t;
350 rend_t *r = (rend_t *)ra->alloc (); memcpy (r, screen.rend[p], common * sizeof (rend_t)); screen.rend[p] = r;
352 MIN_IT (screen.tlen[p], (int16_t)ncol);
354 if (ncol > prev_ncol)
355 scr_blank_line (& (screen.text[p][prev_ncol]),
356 & (screen.rend[p][prev_ncol]),
362 for (p = 0; p < nrow; p++)
364 text_t *t = (text_t *)ta->alloc (); memcpy (t, drawn_text[p], common * sizeof (text_t)); drawn_text[p] = t;
365 rend_t *r = (rend_t *)ra->alloc (); memcpy (r, drawn_rend[p], common * sizeof (rend_t)); drawn_rend[p] = r;
367 if (ncol > prev_ncol)
368 scr_blank_line (& (drawn_text[p][prev_ncol]),
369 & (drawn_rend[p][prev_ncol]),
370 ncol - prev_ncol, setrstyle);
374 text_t *t = (text_t *)ta->alloc (); memcpy (t, swap.text[p], common * sizeof (text_t)); swap.text[p] = t;
375 rend_t *r = (rend_t *)ra->alloc (); memcpy (r, swap.rend[p], common * sizeof (rend_t)); swap.rend[p] = r;
377 MIN_IT (swap.tlen[p], (int16_t)ncol);
379 if (ncol > prev_ncol)
380 scr_blank_line (& (swap.text[p][prev_ncol]),
381 & (swap.rend[p][prev_ncol]),
382 ncol - prev_ncol, setrstyle);
387 MIN_IT (screen.cur.col, (int16_t)ncol - 1);
388 MIN_IT (swap.cur.col, (int16_t)ncol - 1);
390 delete talloc; talloc = ta;
391 delete ralloc; ralloc = ra;
401 tabs = (char *)rxvt_malloc (ncol * sizeof (char));
403 for (p = 0; p < ncol; p++)
404 tabs[p] = (p % TABSIZE == 0) ? 1 : 0;
410 rxvt_term::scr_reset_realloc ()
412 uint16_t total_rows, nrow;
415 total_rows = nrow + TermWin.saveLines;
417 screen.text = (text_t **)rxvt_realloc (screen.text, total_rows * sizeof (text_t *));
418 buf_text = (text_t **)rxvt_realloc (buf_text , total_rows * sizeof (text_t *));
419 drawn_text = (text_t **)rxvt_realloc (drawn_text , nrow * sizeof (text_t *));
420 swap.text = (text_t **)rxvt_realloc (swap.text , nrow * sizeof (text_t *));
422 screen.tlen = (int16_t *)rxvt_realloc (screen.tlen, total_rows * sizeof (int16_t));
423 swap.tlen = (int16_t *)rxvt_realloc (swap.tlen , total_rows * sizeof (int16_t));
425 screen.rend = (rend_t **)rxvt_realloc (screen.rend, total_rows * sizeof (rend_t *));
426 buf_rend = (rend_t **)rxvt_realloc (buf_rend , total_rows * sizeof (rend_t *));
427 drawn_rend = (rend_t **)rxvt_realloc (drawn_rend , nrow * sizeof (rend_t *));
428 swap.rend = (rend_t **)rxvt_realloc (swap.rend , nrow * sizeof (rend_t *));
432 /* ------------------------------------------------------------------------- */
434 * Free everything. That way malloc debugging can find leakage.
437 rxvt_term::scr_release ()
442 total_rows = TermWin.nrow + TermWin.saveLines;
444 delete talloc; talloc = 0;
445 delete ralloc; ralloc = 0;
459 /* NULL these so if anything tries to use them, we'll know about it */
460 screen.text = drawn_text = swap.text = NULL;
461 screen.rend = drawn_rend = swap.rend = NULL;
462 screen.tlen = swap.tlen = NULL;
468 /* ------------------------------------------------------------------------- */
473 rxvt_term::scr_poweron ()
475 D_SCREEN ((stderr, "rxvt_scr_poweron ()"));
478 prev_nrow = prev_ncol = 0;
482 scr_refresh (SLOW_REFRESH);
485 /* ------------------------------------------------------------------------- *
486 * PROCESS SCREEN COMMANDS *
487 * ------------------------------------------------------------------------- */
489 * Save and Restore cursor
490 * XTERM_SEQ: Save cursor : ESC 7
491 * XTERM_SEQ: Restore cursor: ESC 8
494 rxvt_term::scr_cursor (int mode)
498 D_SCREEN ((stderr, "rxvt_scr_cursor (%c)", mode));
500 #if NSCREENS && !defined(NO_SECONDARY_SCREEN_CURSOR)
501 if (current_screen == SECONDARY)
510 s->s_cur.row = s->cur.row;
511 s->s_cur.col = s->cur.col;
512 s->s_rstyle = rstyle;
513 s->s_charset = s->charset;
514 s->s_charset_char = charsets[s->charset];
518 s->cur.row = s->s_cur.row;
519 s->cur.col = s->s_cur.col;
520 s->flags &= ~Screen_WrapNext;
521 rstyle = s->s_rstyle;
522 s->charset = s->s_charset;
523 charsets[s->charset] = s->s_charset_char;
528 /* boundary check in case screen size changed between SAVE and RESTORE */
529 MIN_IT (s->cur.row, TermWin.nrow - 1);
530 MIN_IT (s->cur.col, TermWin.ncol - 1);
532 assert (s->cur.row >= 0);
533 assert (s->cur.col >= 0);
534 #else /* drive with your eyes closed */
535 MAX_IT (s->cur.row, 0);
536 MAX_IT (s->cur.col, 0);
540 /* ------------------------------------------------------------------------- */
542 * Swap between primary and secondary screens
543 * XTERM_SEQ: Primary screen : ESC [ ? 4 7 h
544 * XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
547 rxvt_term::scr_change_screen (int scrn)
556 D_SCREEN ((stderr, "rxvt_scr_change_screen (%d)", scrn));
558 TermWin.view_start = 0;
560 if (current_screen == scrn)
563 selection_check (2); /* check for boundary cross */
565 SWAP_IT (current_screen, scrn, int);
567 if (Options & Opt_secondaryScreen)
570 offset = TermWin.saveLines;
571 for (i = prev_nrow; i--;)
573 SWAP_IT (screen.text[i + offset], swap.text[i], text_t *);
574 SWAP_IT (screen.tlen[i + offset], swap.tlen[i], int16_t);
575 SWAP_IT (screen.rend[i + offset], swap.rend[i], rend_t *);
577 SWAP_IT (screen.cur.row, swap.cur.row, int16_t);
578 SWAP_IT (screen.cur.col, swap.cur.col, int16_t);
580 assert ((screen.cur.row >= 0) && (screen.cur.row < prev_nrow));
581 assert ((screen.cur.col >= 0) && (screen.cur.col < prev_ncol));
582 # else /* drive with your eyes closed */
583 MAX_IT (screen.cur.row, 0);
584 MIN_IT (screen.cur.row, (int32_t)prev_nrow - 1);
585 MAX_IT (screen.cur.col, 0);
586 MIN_IT (screen.cur.col, (int32_t)prev_ncol - 1);
588 SWAP_IT (screen.charset, swap.charset, int16_t);
589 SWAP_IT (screen.flags, swap.flags, int);
590 screen.flags |= Screen_VisibleCursor;
591 swap.flags |= Screen_VisibleCursor;
595 if (Options & Opt_secondaryScroll)
596 //if (current_screen == PRIMARY)
597 scr_scroll_text (0, (prev_nrow - 1), prev_nrow, 0);
601 /* ------------------------------------------------------------------------- */
603 * Change the colour for following text
606 rxvt_term::scr_color (unsigned int color, int fgbg)
609 if (fgbg == Color_fg)
610 rstyle = SET_FGCOLOR (rstyle, color);
612 rstyle = SET_BGCOLOR (rstyle, color);
615 /* ------------------------------------------------------------------------- */
617 * Change the rendition style for following text
620 rxvt_term::scr_rendition (int set, int style)
624 else if (style == ~RS_None)
625 rstyle = DEFAULT_RSTYLE;
630 /* ------------------------------------------------------------------------- */
632 * Scroll text between <row1> and <row2> inclusive, by <count> lines
633 * count positive ==> scroll up
634 * count negative ==> scroll down
635 * spec == 0 for normal routines
638 rxvt_term::scr_scroll_text (int row1, int row2, int count, int spec)
643 if (count == 0 || (row1 > row2))
647 D_SCREEN ((stderr, "rxvt_scroll_text (%d,%d,%d,%d): %s", row1, row2, count, spec, (current_screen == PRIMARY) ? "Primary" : "Secondary"));
649 if (row1 == 0 && count > 0
650 && (current_screen == PRIMARY || Options & Opt_secondaryScroll))
652 nscrolled = (long)TermWin.nscrolled + (long)count;
654 if (nscrolled > (long)TermWin.saveLines)
655 TermWin.nscrolled = TermWin.saveLines;
657 TermWin.nscrolled = (uint16_t)nscrolled;
659 if ((Options & Opt_scrollWithBuffer)
660 && TermWin.view_start != 0
661 && TermWin.view_start != TermWin.saveLines)
662 scr_page (UP, count);
665 row1 += TermWin.saveLines;
667 row2 += TermWin.saveLines;
669 if (selection.op && current_screen == selection.screen)
671 i = selection.beg.row + TermWin.saveLines;
672 j = selection.end.row + TermWin.saveLines;
673 if ((i < row1 && j > row1)
674 || (i < row2 && j > row2)
675 || (i - count < row1 && i >= row1)
676 || (i - count > row2 && i <= row2)
677 || (j - count < row1 && j >= row1)
678 || (j - count > row2 && j <= row2))
680 CLEAR_ALL_SELECTION ();
681 selection.op = SELECTION_CLEAR; /* XXX: too aggressive? */
683 else if (j >= row1 && j <= row2)
685 /* move selected region too */
686 selection.beg.row -= count;
687 selection.end.row -= count;
688 selection.mark.row -= count;
692 selection_check (0); /* _after_ TermWin.nscrolled update */
707 /* A1: Copy lines that will get clobbered by the rotation */
708 for (i = 0, j = row1; i < count; i++, j++)
710 buf_text[i] = screen.text[j];
711 buf_rend[i] = screen.rend[j];
713 /* A2: Rotate lines */
714 for (j = row1, i = j + count; i <= row2; i++, j++)
716 screen.tlen[j] = screen.tlen[i];
717 screen.text[j] = screen.text[i];
718 screen.rend[j] = screen.rend[i];
720 j = row2 - count + 1, i = count;
722 else /* if (j < 0) */
726 /* B1: Copy lines that will get clobbered by the rotation */
727 for (i = 0, j = row2; i < count; i++, j--)
729 buf_text[i] = screen.text[j];
730 buf_rend[i] = screen.rend[j];
732 /* B2: Rotate lines */
733 for (j = row2, i = j - count; i >= row1; i--, j--)
735 screen.tlen[j] = screen.tlen[i];
736 screen.text[j] = screen.text[i];
737 screen.rend[j] = screen.rend[i];
743 /* C: Resurrect lines */
747 screen.text[j] = buf_text[i];
748 screen.rend[j] = buf_rend[i];
750 if (!spec) /* line length may not equal TermWin.ncol */
751 scr_blank_screen_mem (screen.text, screen.rend,
752 (unsigned int)j, rstyle);
758 /* ------------------------------------------------------------------------- */
760 * Add text given in <str> of length <len> to screen struct
763 rxvt_term::scr_add_lines (const unicode_t *str, int nlines, int len)
765 unsigned char checksel, clearsel;
767 int i, row, last_col;
771 if (len <= 0) /* sanity */
775 last_col = TermWin.ncol;
777 D_SCREEN ((stderr, "rxvt_scr_add_lines (%d,%d)", nlines, len));
781 nlines += (screen.cur.row - screen.bscroll);
783 && (screen.tscroll == 0)
784 && (screen.bscroll == (TermWin.nrow - 1)))
786 /* _at least_ this many lines need to be scrolled */
787 scr_scroll_text (screen.tscroll, screen.bscroll, nlines, 0);
788 screen.cur.row -= nlines;
792 assert (screen.cur.col < last_col);
793 assert ((screen.cur.row < TermWin.nrow)
794 && (screen.cur.row >= - (int32_t)TermWin.nscrolled));
795 #else /* drive with your eyes closed */
796 MIN_IT (screen.cur.col, last_col - 1);
797 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
798 MAX_IT (screen.cur.row, - (int32_t)TermWin.nscrolled);
800 row = screen.cur.row + TermWin.saveLines;
802 checksel = selection.op && current_screen == selection.screen ? 1 : 0;
805 stp = screen.text[row];
806 srp = screen.rend[row];
820 if (screen.tlen[row] != -1) /* XXX: think about this */
821 MAX_IT (screen.tlen[row], screen.cur.col);
823 screen.flags &= ~Screen_WrapNext;
825 if (screen.cur.row == screen.bscroll)
826 scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
827 else if (screen.cur.row < (TermWin.nrow - 1))
828 row = (++screen.cur.row) + TermWin.saveLines;
830 stp = screen.text[row]; /* _must_ refresh */
831 srp = screen.rend[row]; /* _must_ refresh */
835 if (screen.tlen[row] != -1) /* XXX: think about this */
836 MAX_IT (screen.tlen[row], screen.cur.col);
838 screen.flags &= ~Screen_WrapNext;
843 if (checksel /* see if we're writing within selection */
844 && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
845 && ROWCOL_IS_BEFORE (screen.cur, selection.end))
851 if (screen.flags & Screen_WrapNext)
853 screen.tlen[row] = -1;
854 if (screen.cur.row == screen.bscroll)
855 scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
856 else if (screen.cur.row < (TermWin.nrow - 1))
857 row = (++screen.cur.row) + TermWin.saveLines;
859 stp = screen.text[row]; /* _must_ refresh */
860 srp = screen.rend[row]; /* _must_ refresh */
862 screen.flags &= ~Screen_WrapNext;
865 if (screen.flags & Screen_Insert)
866 scr_insdel_chars (1, INSERT);
868 // rely on wcwidth to tell us the character width, at least for non-latin1
869 // do wcwidth before further replacements, as wcwidth says that line-drawing
870 // characters have width -1 (DOH!) on GNU/Linux sometimes.
871 int width = c < 256 ? 1 : wcwidth (c);
873 if (charsets[screen.charset] == '0') // DEC SPECIAL
875 // vt100 special graphics and line drawing
876 static uint16_t vt100_0[32] = { // 5f .. 7e
877 0x0020, 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
878 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
879 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534,
880 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7,
883 if (c >= 0x5f && c <= 0x7e)
885 c = vt100_0[c - 0x5f];
892 #if !UNICODE_3 && ENABLE_COMBINING
893 // trim characters we can't store directly :(
895 c = rxvt_composite.compose (c); // map to lower 16 bits
897 rend_t rend = SET_FONT (rstyle, TermWin.fontset->find_font (c));
901 stp[screen.cur.col] = c;
902 srp[screen.cur.col] = rend;
904 if (screen.cur.col < last_col - 1)
908 screen.tlen[row] = last_col;
909 if (screen.flags & Screen_Autowrap)
910 screen.flags |= Screen_WrapNext;
918 // pad with spaces when overwriting wide character with smaller one
919 for (int c = screen.cur.col; c < last_col && stp[c] == NOCHAR; c++)
928 // handle combining characters
929 // we just tag the accent on the previous on-screen character.
930 // this is arguably not correct, but also arguably not wrong.
931 // we don't handle double-width characters nicely yet.
936 if (screen.cur.col > 0)
938 tp = stp + screen.cur.col - 1;
939 rp = srp + screen.cur.col - 1;
941 else if (screen.cur.row > 0
942 && screen.tlen [screen.cur.row - 1 + TermWin.saveLines] == -1)
944 tp = screen.text[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
945 rp = screen.rend[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
950 // handle double-width-chars by making them look extremely ugly
952 *tp = ' '; // hack //D //TODO //--tp, --rp;
954 // first try to find a precomposed character
955 unicode_t n = rxvt_compose (*tp, c);
957 n = rxvt_composite.compose (*tp, c);
960 *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
965 if (screen.tlen[row] != -1) /* XXX: think about this */
966 MAX_IT (screen.tlen[row], screen.cur.col);
969 * If we wrote anywhere in the selected area, kill the selection
970 * XXX: should we kill the mark too? Possibly, but maybe that
971 * should be a similar check.
977 assert (screen.cur.row >= 0);
978 #else /* drive with your eyes closed */
979 MAX_IT (screen.cur.row, 0);
983 /* ------------------------------------------------------------------------- */
985 * Process Backspace. Move back the cursor back a position, wrap if have to
989 rxvt_term::scr_backspace ()
992 if (screen.cur.col == 0)
994 if (screen.cur.row > 0)
996 #ifdef TERMCAP_HAS_BW
997 screen.cur.col = TermWin.ncol - 1;
1004 else if ((screen.flags & Screen_WrapNext) == 0)
1005 scr_gotorc (0, -1, RELATIVE);
1006 screen.flags &= ~Screen_WrapNext;
1009 /* ------------------------------------------------------------------------- */
1011 * Process Horizontal Tab
1012 * count: +ve = forward; -ve = backwards
1016 rxvt_term::scr_tab (int count)
1020 D_SCREEN ((stderr, "rxvt_scr_tab (%d)", count));
1022 i = x = screen.cur.col;
1028 for (; ++i < TermWin.ncol; )
1037 x = TermWin.ncol - 1;
1039 else /* if (count < 0) */
1053 if (x != screen.cur.col)
1054 scr_gotorc (0, x, R_RELATIVE);
1057 /* ------------------------------------------------------------------------- */
1059 * Process DEC Back Index
1061 * Move cursor left in row. If we're at the left boundary, shift everything
1062 * in that row right. Clear left column.
1066 rxvt_term::scr_backindex ()
1068 if (screen.cur.col > 0)
1069 scr_gotorc (0, -1, R_RELATIVE | C_RELATIVE);
1072 if (screen.tlen[screen.cur.row + TermWin.saveLines] == 0)
1073 return; /* um, yeah? */
1074 scr_insdel_chars (1, INSERT);
1078 /* ------------------------------------------------------------------------- */
1080 * Process DEC Forward Index
1082 * Move cursor right in row. If we're at the right boundary, shift everything
1083 * in that row left. Clear right column.
1087 rxvt_term::scr_forwardindex ()
1091 if (screen.cur.col < TermWin.ncol - 1)
1092 scr_gotorc (0, 1, R_RELATIVE | C_RELATIVE);
1095 row = screen.cur.row + TermWin.saveLines;
1096 if (screen.tlen[row] == 0)
1097 return; /* um, yeah? */
1098 else if (screen.tlen[row] == -1)
1099 screen.tlen[row] = TermWin.ncol;
1100 scr_gotorc (0, 0, R_RELATIVE);
1101 scr_insdel_chars (1, DELETE);
1102 scr_gotorc (0, TermWin.ncol - 1, R_RELATIVE);
1107 /* ------------------------------------------------------------------------- */
1112 rxvt_term::scr_gotorc (int row, int col, int relative)
1117 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));
1119 screen.cur.col = ((relative & C_RELATIVE) ? (screen.cur.col + col)
1121 MAX_IT (screen.cur.col, 0);
1122 MIN_IT (screen.cur.col, (int32_t)TermWin.ncol - 1);
1124 screen.flags &= ~Screen_WrapNext;
1125 if (relative & R_RELATIVE)
1129 if (screen.cur.row <= screen.bscroll
1130 && (screen.cur.row + row) > screen.bscroll)
1131 screen.cur.row = screen.bscroll;
1133 screen.cur.row += row;
1137 if (screen.cur.row >= screen.tscroll
1138 && (screen.cur.row + row) < screen.tscroll)
1139 screen.cur.row = screen.tscroll;
1141 screen.cur.row += row;
1146 if (screen.flags & Screen_Relative)
1147 { /* relative origin mode */
1148 screen.cur.row = row + screen.tscroll;
1149 MIN_IT (screen.cur.row, screen.bscroll);
1152 screen.cur.row = row;
1154 MAX_IT (screen.cur.row, 0);
1155 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1157 while (screen.cur.col > 0
1158 && screen.text[screen.cur.row + TermWin.saveLines][screen.cur.col] == NOCHAR)
1162 /* ------------------------------------------------------------------------- */
1164 * direction should be UP or DN
1167 rxvt_term::scr_index (enum page_dirn direction)
1172 dirn = ((direction == UP) ? 1 : -1);
1173 D_SCREEN ((stderr, "rxvt_scr_index (%d)", dirn));
1177 screen.flags &= ~Screen_WrapNext;
1178 if ((screen.cur.row == screen.bscroll && direction == UP)
1179 || (screen.cur.row == screen.tscroll && direction == DN))
1180 scr_scroll_text (screen.tscroll, screen.bscroll, dirn, 0);
1182 screen.cur.row += dirn;
1183 MAX_IT (screen.cur.row, 0);
1184 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1185 selection_check (0);
1188 /* ------------------------------------------------------------------------- */
1190 * Erase part or whole of a line
1191 * XTERM_SEQ: Clear line to right: ESC [ 0 K
1192 * XTERM_SEQ: Clear line to left : ESC [ 1 K
1193 * XTERM_SEQ: Clear whole line : ESC [ 2 K
1196 rxvt_term::scr_erase_line (int mode)
1198 unsigned int row, col, num;
1201 D_SCREEN ((stderr, "rxvt_scr_erase_line (%d) at screen row: %d", mode, screen.cur.row));
1204 selection_check (1);
1206 screen.flags &= ~Screen_WrapNext;
1208 row = TermWin.saveLines + screen.cur.row;
1211 case 0: /* erase to end of line */
1212 col = screen.cur.col;
1213 num = TermWin.ncol - col;
1214 MIN_IT (screen.tlen[row], (int16_t)col);
1215 if (ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur)
1216 || ROWCOL_IN_ROW_AT_OR_AFTER (selection.end, screen.cur))
1219 case 1: /* erase to beginning of line */
1221 num = screen.cur.col + 1;
1222 if (ROWCOL_IN_ROW_AT_OR_BEFORE (selection.beg, screen.cur)
1223 || ROWCOL_IN_ROW_AT_OR_BEFORE (selection.end, screen.cur))
1226 case 2: /* erase whole line */
1229 screen.tlen[row] = 0;
1230 if (selection.beg.row <= screen.cur.row
1231 && selection.end.row >= screen.cur.row)
1238 if (screen.text[row])
1239 scr_blank_line (& (screen.text[row][col]),
1240 & (screen.rend[row][col]), num, rstyle);
1242 scr_blank_screen_mem (screen.text, screen.rend, row, rstyle);
1245 /* ------------------------------------------------------------------------- */
1247 * Erase part of whole of the screen
1248 * XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
1249 * XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
1250 * XTERM_SEQ: Clear whole screen : ESC [ 2 J
1253 rxvt_term::scr_erase_screen (int mode)
1256 int32_t row, row_offset;
1261 D_SCREEN ((stderr, "rxvt_scr_erase_screen (%d) at screen row: %d", mode, screen.cur.row));
1263 row_offset = (int32_t)TermWin.saveLines;
1267 case 0: /* erase to end of screen */
1268 selection_check (1);
1270 row = screen.cur.row + 1; /* possible OOB */
1271 num = TermWin.nrow - row;
1273 case 1: /* erase to beginning of screen */
1274 selection_check (3);
1277 num = screen.cur.row;
1279 case 2: /* erase whole screen */
1280 selection_check (3);
1288 refresh_type |= REFRESH_BOUNDS;
1290 if (selection.op && current_screen == selection.screen
1291 && ((selection.beg.row >= row && selection.beg.row <= row + num)
1292 || (selection.end.row >= row
1293 && selection.end.row <= row + num)))
1296 if (row >= TermWin.nrow) /* Out Of Bounds */
1299 MIN_IT (num, (TermWin.nrow - row));
1301 if (rstyle & (RS_RVid | RS_Uline))
1302 ren = (rend_t) ~RS_None;
1303 else if (GET_BASEBG (rstyle) == Color_bg)
1305 ren = DEFAULT_RSTYLE;
1306 CLEAR_ROWS (row, num);
1310 ren = (rstyle & (RS_fgMask | RS_bgMask));
1311 gcvalue.foreground = PixColors[GET_BGCOLOR (rstyle)];
1312 XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1313 ERASE_ROWS (row, num);
1314 gcvalue.foreground = PixColors[Color_fg];
1315 XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1318 for (; num--; row++)
1320 scr_blank_screen_mem (screen.text, screen.rend,
1321 (unsigned int) (row + row_offset), rstyle);
1322 screen.tlen[row + row_offset] = 0;
1323 scr_blank_line (drawn_text[row], drawn_rend[row],
1324 (unsigned int)TermWin.ncol, ren);
1328 /* ------------------------------------------------------------------------- */
1330 * Fill the screen with `E's
1331 * XTERM_SEQ: Screen Alignment Test: ESC # 8
1342 selection_check (3);
1344 fs = SET_FONT (rstyle, TermWin.fontset->find_font ('E'));
1345 for (k = TermWin.saveLines, i = TermWin.nrow; i--; k++)
1347 screen.tlen[k] = TermWin.ncol; /* make the `E's selectable */
1348 fill_text (screen.text[k], 'E', TermWin.ncol);
1349 for (r1 = screen.rend[k], j = TermWin.ncol; j--; )
1354 /* ------------------------------------------------------------------------- */
1356 * Insert/Delete <count> lines
1359 rxvt_term::scr_insdel_lines (int count, int insdel)
1365 selection_check (1);
1367 if (screen.cur.row > screen.bscroll)
1370 end = screen.bscroll - screen.cur.row + 1;
1373 if (insdel == DELETE)
1375 else if (insdel == INSERT)
1378 screen.flags &= ~Screen_WrapNext;
1380 scr_scroll_text (screen.cur.row, screen.bscroll, insdel * count, 0);
1383 /* ------------------------------------------------------------------------- */
1385 * Insert/Delete <count> characters from the current position
1388 rxvt_term::scr_insdel_chars (int count, int insdel)
1402 selection_check (1);
1403 MIN_IT (count, (TermWin.ncol - screen.cur.col));
1405 row = screen.cur.row + TermWin.saveLines;
1406 screen.flags &= ~Screen_WrapNext;
1408 stp = screen.text[row];
1409 srp = screen.rend[row];
1410 slp = & (screen.tlen[row]);
1415 for (col = TermWin.ncol - 1; (col - count) >= screen.cur.col;
1418 stp[col] = stp[col - count];
1419 srp[col] = srp[col - count];
1425 MIN_IT (*slp, TermWin.ncol);
1428 if (selection.op && current_screen == selection.screen
1429 && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1431 if (selection.end.row != screen.cur.row
1432 || (selection.end.col + count >= TermWin.ncol))
1435 { /* shift selection */
1436 selection.beg.col += count;
1437 selection.mark.col += count; /* XXX: yes? */
1438 selection.end.col += count;
1442 scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1443 (unsigned int)count, rstyle);
1447 screen.cur.col += count; /* don't worry if > TermWin.ncol */
1448 selection_check (1);
1449 screen.cur.col -= count;
1450 scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1451 (unsigned int)count, rstyle);
1455 tr = srp[TermWin.ncol - 1] & (RS_fgMask | RS_bgMask | RS_baseattrMask);
1457 for (col = screen.cur.col; (col + count) < TermWin.ncol; col++)
1459 stp[col] = stp[col + count];
1460 srp[col] = srp[col + count];
1463 scr_blank_line (& (stp[TermWin.ncol - count]),
1464 & (srp[TermWin.ncol - count]),
1465 (unsigned int)count, tr);
1467 if (*slp == -1) /* break line continuation */
1468 *slp = TermWin.ncol;
1473 if (selection.op && current_screen == selection.screen
1474 && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1476 if (selection.end.row != screen.cur.row
1477 || (screen.cur.col >= selection.beg.col - count)
1478 || selection.end.col >= TermWin.ncol)
1482 /* shift selection */
1483 selection.beg.col -= count;
1484 selection.mark.col -= count; /* XXX: yes? */
1485 selection.end.col -= count;
1493 /* ------------------------------------------------------------------------- */
1495 * Set the scrolling region
1496 * XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
1499 rxvt_term::scr_scroll_region (int top, int bot)
1502 MIN_IT (bot, (int)TermWin.nrow - 1);
1507 screen.tscroll = top;
1508 screen.bscroll = bot;
1509 scr_gotorc (0, 0, 0);
1512 /* ------------------------------------------------------------------------- */
1514 * Make the cursor visible/invisible
1515 * XTERM_SEQ: Make cursor visible : ESC [ ? 25 h
1516 * XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
1519 rxvt_term::scr_cursor_visible (int mode)
1523 screen.flags |= Screen_VisibleCursor;
1525 screen.flags &= ~Screen_VisibleCursor;
1528 /* ------------------------------------------------------------------------- */
1530 * Set/unset automatic wrapping
1531 * XTERM_SEQ: Set Wraparound : ESC [ ? 7 h
1532 * XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
1535 rxvt_term::scr_autowrap (int mode)
1538 screen.flags |= Screen_Autowrap;
1540 screen.flags &= ~ (Screen_Autowrap | Screen_WrapNext);
1543 /* ------------------------------------------------------------------------- */
1545 * Set/unset margin origin mode
1546 * Absolute mode: line numbers are counted relative to top margin of screen
1547 * and the cursor can be moved outside the scrolling region.
1548 * Relative mode: line numbers are relative to top margin of scrolling region
1549 * and the cursor cannot be moved outside.
1550 * XTERM_SEQ: Set Absolute: ESC [ ? 6 h
1551 * XTERM_SEQ: Set Relative: ESC [ ? 6 l
1554 rxvt_term::scr_relative_origin (int mode)
1557 screen.flags |= Screen_Relative;
1559 screen.flags &= ~Screen_Relative;
1560 scr_gotorc (0, 0, 0);
1563 /* ------------------------------------------------------------------------- */
1565 * Set insert/replace mode
1566 * XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
1567 * XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
1570 rxvt_term::scr_insert_mode (int mode)
1573 screen.flags |= Screen_Insert;
1575 screen.flags &= ~Screen_Insert;
1578 /* ------------------------------------------------------------------------- */
1581 * XTERM_SEQ: Set tab at current column : ESC H
1582 * XTERM_SEQ: Clear tab at current column: ESC [ 0 g
1583 * XTERM_SEQ: Clear all tabs : ESC [ 3 g
1586 rxvt_term::scr_set_tab (int mode)
1589 MEMSET (tabs, 0, TermWin.ncol * sizeof (char));
1590 else if (screen.cur.col < TermWin.ncol)
1591 tabs[screen.cur.col] = (mode ? 1 : 0);
1594 /* ------------------------------------------------------------------------- */
1596 * Set reverse/normal video
1597 * XTERM_SEQ: Reverse video: ESC [ ? 5 h
1598 * XTERM_SEQ: Normal video : ESC [ ? 5 l
1601 rxvt_term::scr_rvideo_mode (int mode)
1608 SWAP_IT (PixColors[Color_fg], PixColors[Color_bg], rxvt_color);
1609 #if defined(XPM_BACKGROUND)
1610 if (bgPixmap.pixmap == None)
1612 #if defined(TRANSPARENT)
1613 if (! (Options & Opt_transparent) || am_transparent == 0)
1615 XSetWindowBackground (display->display, TermWin.vt,
1616 PixColors[Color_bg]);
1618 gcvalue.foreground = PixColors[Color_fg];
1619 gcvalue.background = PixColors[Color_bg];
1620 XChangeGC (display->display, TermWin.gc, GCBackground | GCForeground,
1627 /* ------------------------------------------------------------------------- */
1629 * Report current cursor position
1630 * XTERM_SEQ: Report position: ESC [ 6 n
1633 rxvt_term::scr_report_position ()
1635 tt_printf ("\033[%d;%dR", screen.cur.row + 1, screen.cur.col + 1);
1638 /* ------------------------------------------------------------------------- *
1640 * ------------------------------------------------------------------------- */
1646 rxvt_term::set_font_style ()
1648 switch (charsets[screen.charset])
1650 case '0': /* DEC Special Character & Line Drawing Set */
1652 case 'A': /* United Kingdom (UK) */
1654 case 'B': /* United States (USASCII) */
1656 case '<': /* Multinational character set */
1658 case '5': /* Finnish character set */
1660 case 'C': /* Finnish character set */
1662 case 'K': /* German character set */
1667 /* ------------------------------------------------------------------------- */
1670 * XTERM_SEQ: Invoke G0 character set: CTRL-O
1671 * XTERM_SEQ: Invoke G1 character set: CTRL-N
1672 * XTERM_SEQ: Invoke G2 character set: ESC N
1673 * XTERM_SEQ: Invoke G3 character set: ESC O
1676 rxvt_term::scr_charset_choose (int set)
1678 screen.charset = set;
1682 /* ------------------------------------------------------------------------- */
1685 * XTERM_SEQ: Set G0 character set: ESC ( <C>
1686 * XTERM_SEQ: Set G1 character set: ESC ) <C>
1687 * XTERM_SEQ: Set G2 character set: ESC * <C>
1688 * XTERM_SEQ: Set G3 character set: ESC + <C>
1689 * See set_font_style for possible values for <C>
1692 rxvt_term::scr_charset_set (int set, unsigned int ch)
1694 charsets[set] = (unsigned char)ch;
1699 /* ------------------------------------------------------------------------- *
1700 * MAJOR SCREEN MANIPULATION *
1701 * ------------------------------------------------------------------------- */
1704 * refresh matching text.
1707 rxvt_term::scr_refresh_rend (rend_t mask, rend_t value)
1711 for (int i = 0; i < TermWin.nrow; i++)
1714 rend_t *drp = drawn_rend [i];
1716 for (; col < TermWin.ncol; col++, drp++)
1717 if ((*drp & mask) == value)
1737 rxvt_term::scr_expose (int x, int y, int width, int height, bool refresh)
1740 row_col_t rc[RC_COUNT];
1742 if (drawn_text == NULL) /* sanity check */
1746 x = max (x, (int)TermWin.int_bwidth);
1747 x = min (x, (int)TermWin.width);
1748 y = max (y, (int)TermWin.int_bwidth);
1749 y = min (y, (int)TermWin.height);
1753 rc[PART_BEG].col = Pixel2Col (x);
1754 rc[PART_BEG].row = Pixel2Row (y);
1756 rc[PART_END].col = Pixel2Width (x + width + TermWin.fwidth - 1);
1757 rc[PART_END].row = Pixel2Row (y + height + TermWin.fheight - 1);
1760 for (i = PART_BEG; i < RC_COUNT; i++)
1762 MIN_IT (rc[i].col, TermWin.ncol - 1);
1763 MIN_IT (rc[i].row, TermWin.nrow - 1);
1766 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));
1768 for (i = rc[PART_BEG].row; i <= rc[PART_END].row; i++)
1769 fill_text (& (drawn_text[i][rc[PART_BEG].col]), 0,
1770 (rc[PART_END].col - rc[PART_BEG].col + 1));
1773 scr_refresh (SLOW_REFRESH | REFRESH_BOUNDS);
1776 /* ------------------------------------------------------------------------- */
1778 * Refresh the entire screen
1781 rxvt_term::scr_touch (bool refresh)
1783 scr_expose (0, 0, TermWin.width, TermWin.height, refresh);
1786 /* ------------------------------------------------------------------------- */
1788 * Move the display so that the line represented by scrollbar value Y is at
1789 * the top of the screen
1792 rxvt_term::scr_move_to (int y, int len)
1795 uint16_t oldviewstart;
1797 oldviewstart = TermWin.view_start;
1800 p = (TermWin.nrow + TermWin.nscrolled) * (len - y) / len;
1801 p -= (long) (TermWin.nrow - 1);
1804 TermWin.view_start = (uint16_t)min (p, TermWin.nscrolled);
1805 D_SCREEN ((stderr, "rxvt_scr_move_to (%d, %d) view_start:%d", y, len, TermWin.view_start));
1807 return scr_changeview (oldviewstart);
1810 /* ------------------------------------------------------------------------- */
1812 * Page the screen up/down nlines
1813 * direction should be UP or DN
1816 rxvt_term::scr_page (enum page_dirn direction, int nlines)
1819 uint16_t oldviewstart;
1821 D_SCREEN ((stderr, "rxvt_scr_page (%s, %d) view_start:%d", ((direction == UP) ? "UP" : "DN"), nlines, TermWin.view_start));
1823 assert ((nlines >= 0) && (nlines <= TermWin.nrow));
1825 oldviewstart = TermWin.view_start;
1826 if (direction == UP)
1828 n = TermWin.view_start + nlines;
1829 TermWin.view_start = min (n, TermWin.nscrolled);
1833 n = TermWin.view_start - nlines;
1834 TermWin.view_start = max (n, 0);
1836 return scr_changeview (oldviewstart);
1840 rxvt_term::scr_changeview (uint16_t oldviewstart)
1842 if (TermWin.view_start != oldviewstart)
1845 num_scr -= (TermWin.view_start - oldviewstart);
1847 return (int) (TermWin.view_start - oldviewstart);
1850 /* ------------------------------------------------------------------------- */
1852 rxvt_term::scr_bell ()
1855 # ifndef NO_MAPALERT
1856 # ifdef MAPALERT_OPTION
1857 if (Options & Opt_mapAlert)
1859 XMapWindow (display->display, TermWin.parent[0]);
1861 if (Options & Opt_visualBell)
1863 scr_rvideo_mode (!rvideo); /* refresh also done */
1864 scr_rvideo_mode (!rvideo); /* refresh also done */
1867 XBell (display->display, 0);
1871 /* ------------------------------------------------------------------------- */
1874 rxvt_term::scr_printscreen (int fullhist)
1877 int i, r1, nrows, row_offset;
1881 if ((fd = popen_printer ()) == NULL)
1883 nrows = TermWin.nrow;
1884 row_offset = TermWin.saveLines;
1886 row_offset -= TermWin.view_start;
1889 nrows += TermWin.nscrolled;
1890 row_offset -= TermWin.nscrolled;
1893 for (r1 = 0; r1 < nrows; r1++)
1895 t = screen.text[r1 + row_offset];
1896 for (i = TermWin.ncol - 1; i >= 0; i--)
1897 if (!isspace (t[i]))
1899 fprintf (fd, "%.*s\n", (i + 1), t);
1901 pclose_printer (fd);
1905 /* ------------------------------------------------------------------------- */
1907 * Refresh the screen
1908 * drawn_text/drawn_rend contain the screen information before the update.
1909 * screen.text/screen.rend contain what the screen will change to.
1912 #define FONT_WIDTH(X, Y) \
1913 (X)->per_char[ (Y) - (X)->min_char_or_byte2].width
1914 #define FONT_RBEAR(X, Y) \
1915 (X)->per_char[ (Y) - (X)->min_char_or_byte2].rbearing
1916 #define FONT_LBEAR(X, Y) \
1917 (X)->per_char[ (Y) - (X)->min_char_or_byte2].lbearing
1918 #define IS_FONT_CHAR(X, Y) \
1919 ((Y) >= (X)->min_char_or_byte2 && (Y) <= (X)->max_char_or_byte2)
1922 rxvt_term::scr_refresh (unsigned char refresh_type)
1924 unsigned char clearfirst, /* first character writes before cell */
1925 clearlast, /* last character writes beyond cell */
1926 must_clear, /* use draw_string not draw_image_string */
1927 rvid, /* reverse video this position */
1928 showcursor; /* show the cursor */
1929 int16_t col, row, /* column/row we're processing */
1930 ocrow; /* old cursor row */
1932 row_offset; /* basic offset in screen structure */
1933 #ifndef NO_CURSORCOLOR
1934 rend_t cc1; /* store colours at cursor position (s) */
1936 rend_t *drp, *srp; /* drawn-rend-pointer, screen-rend-pointer */
1937 text_t *dtp, *stp; /* drawn-text-pointer, screen-text-pointer */
1939 if (refresh_type == NO_REFRESH || !TermWin.mapped)
1945 clearfirst = clearlast = must_clear = 0;
1949 row_offset = TermWin.saveLines - TermWin.view_start;
1951 if ((refresh_type & REFRESH_BOUNDS))
1953 clearfirst = clearlast = 1;
1954 refresh_type &= ~REFRESH_BOUNDS;
1957 #ifdef XPM_BACKGROUND
1958 must_clear |= (bgPixmap.pixmap != None);
1961 must_clear |= ((Options & Opt_transparent) && am_transparent);
1963 ocrow = oldcursor.row; /* is there an old outline cursor on screen? */
1966 * B: reverse any characters which are selected
1968 scr_reverse_selection ();
1971 * C: set the cursor character (s)
1974 unsigned char setoldcursor;
1975 rend_t ccol1, /* Cursor colour */
1976 ccol2; /* Cursor colour2 */
1978 showcursor = (screen.flags & Screen_VisibleCursor);
1986 srp = &(screen.rend[screen.cur.row + TermWin.saveLines][screen.cur.col]);
1988 if (showcursor && TermWin.focus)
1991 #ifndef NO_CURSORCOLOR
1992 cc1 = *srp & (RS_fgMask | RS_bgMask);
1993 if (ISSET_PIXCOLOR (Color_cursor))
1994 ccol1 = Color_cursor;
1996 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
1997 ccol1 = GET_FGCOLOR (rstyle);
2001 if (ISSET_PIXCOLOR (Color_cursor2))
2002 ccol2 = Color_cursor2;
2004 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2005 ccol2 = GET_BGCOLOR (rstyle);
2009 *srp = SET_FGCOLOR (*srp, ccol1);
2010 *srp = SET_BGCOLOR (*srp, ccol2);
2015 /* make sure no outline cursor is left around */
2019 if (screen.cur.row + TermWin.view_start != ocrow
2020 || screen.cur.col != oldcursor.col)
2022 if (ocrow < TermWin.nrow
2023 && oldcursor.col < TermWin.ncol)
2024 drawn_rend[ocrow][oldcursor.col] ^= (RS_RVid | RS_Uline);
2026 if (TermWin.focus || !showcursor)
2032 else if (!TermWin.focus)
2037 if (screen.cur.row + TermWin.view_start >= TermWin.nrow)
2041 oldcursor.row = screen.cur.row + TermWin.view_start;
2042 oldcursor.col = screen.cur.col;
2047 #ifndef NO_SLOW_LINK_SUPPORT
2049 * D: CopyArea pass - very useful for slower links
2050 * This has been deliberately kept simple.
2053 if (refresh_type == FAST_REFRESH && num_scr_allow && i
2054 && abs (i) < TermWin.nrow && !must_clear)
2064 row = i > 0 ? 0 : j - 1;
2065 for (; j-- >= 0; row += (i > 0 ? 1 : -1))
2067 if (row + i >= 0 && row + i < TermWin.nrow && row + i != ocrow)
2069 stp = screen.text[row + row_offset];
2070 srp = screen.rend[row + row_offset];
2071 dtp = drawn_text[row];
2072 dtp2 = drawn_text[row + i];
2073 drp = drawn_rend[row];
2074 drp2 = drawn_rend[row + i];
2076 for (nits = 0, col = TermWin.ncol; col--; )
2077 if (stp[col] != dtp2[col] || srp[col] != drp2[col])
2079 else if (stp[col] != dtp[col] || srp[col] != drp[col])
2082 if (nits > 8) /* XXX: arbitrary choice */
2084 for (col = TermWin.ncol; col--; )
2100 /* also comes here at end if needed because of >= above */
2102 SWAP_IT (wlen, len, int);
2104 D_SCREEN ((stderr, "rxvt_scr_refresh (): XCopyArea: %d -> %d (height: %d)", len + i, len, wlen - len + 1));
2105 XCopyArea (display->display, TermWin.vt, TermWin.vt,
2106 TermWin.gc, 0, Row2Pixel (len + i),
2107 (unsigned int)TermWin_TotalWidth (),
2108 (unsigned int)Height2Pixel (wlen - len + 1),
2109 0, Row2Pixel (len));
2117 * E: main pass across every character
2119 for (row = 0; row < TermWin.nrow; row++)
2121 stp = screen.text[row + row_offset];
2122 srp = screen.rend[row + row_offset];
2123 dtp = drawn_text[row];
2124 drp = drawn_rend[row];
2127 * E2: OK, now the real pass
2129 int ypixel = (int)Row2Pixel (row);
2131 for (col = 0; col < TermWin.ncol; col++)
2133 /* compare new text with old - if exactly the same then continue */
2134 if (stp[col] == dtp[col] /* Must match characters to skip. */
2135 && (srp[col] == drp[col] /* Either rendition the same or */
2136 || (stp[col] == ' ' /* space w/ no background change */
2137 && GET_BGATTR (srp[col]) == GET_BGATTR (drp[col]))))
2140 // redraw one or more characters
2142 // seek to the beginning if wide characters
2143 while (stp[col] == NOCHAR && col > 0)
2146 rend_t rend = srp[col]; /* screen rendition (target rendtion) */
2147 text_t *text = stp + col;
2150 dtp[col] = stp[col];
2153 int xpixel = Col2Pixel (col);
2155 // this loop looks very messy, it can probably be optimized
2156 // and cleaned a bit by you?
2157 for (i = 0; ++col < TermWin.ncol; )
2159 if (stp[col] == NOCHAR)
2161 dtp[col] = stp[col];
2165 if (i) // only possible skip if char unchanged
2171 if (rend != srp[col])
2176 if (stp[col] != dtp[col]
2177 || srp[col] != drp[col])
2179 if (must_clear && (i++ > (count / 2)))
2182 dtp[col] = stp[col];
2186 else if (must_clear || (stp[col] != ' ' && ++i >= 32))
2190 col--; /* went one too far. move back */
2191 count -= i; /* dump any matching trailing chars */
2194 * Determine the attributes for the string
2196 int fid = GET_FONT (rend);
2197 int fore = GET_FGCOLOR (rend); // desired foreground
2198 int back = GET_BGCOLOR (rend); // desired background
2200 rend = GET_ATTR (rend);
2202 rvid = !!(rend & RS_RVid);
2204 #ifndef NO_BOLD_UNDERLINE_REVERSE
2205 if (rend & RS_Bold && fore == Color_fg)
2207 if (ISSET_PIXCOLOR (Color_BD))
2213 if (rend & RS_Uline)
2214 if (ISSET_PIXCOLOR (Color_UL))
2220 SWAP_IT (fore, back, int);
2222 #ifndef NO_BOLD_UNDERLINE_REVERSE
2223 if (ISSET_PIXCOLOR (Color_RV)
2224 # ifndef NO_CURSORCOLOR
2225 && !ISSET_PIXCOLOR (Color_cursor)
2233 if (rend & RS_Blink && back == Color_bg)
2235 if (!text_blink_ev.active)
2237 text_blink_ev.start (NOW + TEXT_BLINK_INTERVAL);
2240 else if (hidden_text)
2246 * Actually do the drawing of the string here
2248 rxvt_font *font = (*TermWin.fontset)[fid];
2251 font->clear_rect (*TermWin.drawable, xpixel, ypixel,
2252 TermWin.fwidth * count, TermWin.fheight,
2254 else if (back == Color_bg)
2258 CLEAR_CHARS (xpixel, ypixel, count);
2260 for (i = 0; i < count; i++) /* don't draw empty strings */
2263 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, -1);
2268 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, Color_bg);
2271 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, back);
2273 if ((rend & RS_Uline) && (font->descent > 1))
2274 XDrawLine (display->display, drawBuffer, TermWin.gc,
2275 xpixel, ypixel + font->ascent + 1,
2276 xpixel + Width2Pixel (count) - 1, ypixel + font->ascent + 1);
2277 } /* for (col....) */
2278 } /* for (row....) */
2281 * G: cleanup cursor and display outline cursor if necessary
2287 srp = & (screen.rend[screen.cur.row + TermWin.saveLines]
2290 #ifndef NO_CURSORCOLOR
2291 *srp = (*srp & ~ (RS_fgMask | RS_bgMask)) | cc1;
2294 else if (oldcursor.row >= 0)
2296 #ifndef NO_CURSORCOLOR
2297 if (ISSET_PIXCOLOR (Color_cursor))
2298 XSetForeground (display->display, TermWin.gc, PixColors[Color_cursor]);
2300 int cursorwidth = 1;
2301 while (oldcursor.col + cursorwidth < TermWin.ncol
2302 && drawn_text[oldcursor.row][oldcursor.col + cursorwidth] == NOCHAR)
2305 XDrawRectangle (display->display, drawBuffer, TermWin.gc,
2306 Col2Pixel (oldcursor.col),
2307 Row2Pixel (oldcursor.row),
2308 (unsigned int) (Width2Pixel (cursorwidth) - 1),
2309 (unsigned int) (Height2Pixel (1) - TermWin.lineSpace - 1));
2314 * H: cleanup selection
2316 scr_reverse_selection ();
2319 * I: other general cleanup
2321 if (clearfirst && TermWin.int_bwidth)
2323 * clear the whole screen height, note that width == 0 is treated
2324 * specially by XClearArea
2326 XClearArea (display->display, TermWin.vt, 0, 0,
2327 (unsigned int)TermWin.int_bwidth,
2328 (unsigned int)TermWin_TotalHeight (), False);
2330 if (clearlast && TermWin.int_bwidth)
2332 * clear the whole screen height, note that width == 0 is treated
2333 * specially by XClearArea
2335 XClearArea (display->display, TermWin.vt,
2336 TermWin.width + TermWin.int_bwidth, 0,
2337 (unsigned int)TermWin.int_bwidth,
2338 (unsigned int)TermWin_TotalHeight (), False);
2340 if (refresh_type & SMOOTH_REFRESH)
2341 XSync (display->display, False);
2345 want_refresh = 0; /* screen is current */
2349 rxvt_term::scr_remap_chars (text_t *tp, rend_t *rp)
2354 for (int i = TermWin.ncol; i; i--, rp++, tp++)
2355 *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
2359 rxvt_term::scr_remap_chars ()
2361 for (int i = TermWin.nrow + TermWin.saveLines; i--; )
2362 scr_remap_chars (screen.text[i], screen.rend[i]);
2364 for (int i = TermWin.nrow; i--; )
2366 scr_remap_chars (drawn_text[i], drawn_rend[i]);
2367 scr_remap_chars (swap.text[i], swap.rend[i]);
2371 /* ------------------------------------------------------------------------- */
2373 rxvt_term::scr_clear (bool really)
2375 if (!TermWin.mapped)
2382 if ((Options & Opt_transparent) && (am_pixmap_trans == 0))
2386 if (! (Options & Opt_transparent_all))
2389 i = (int) (sizeof (TermWin.parent) / sizeof (Window));
2392 if (TermWin.parent[i] != None)
2393 XClearWindow (display->display, TermWin.parent[i]);
2398 XClearWindow (display->display, TermWin.vt);
2401 /* ------------------------------------------------------------------------- */
2403 rxvt_term::scr_reverse_selection ()
2405 if (selection.op && current_screen == selection.screen)
2407 int end_row = TermWin.saveLines - TermWin.view_start;
2408 int i = selection.beg.row + TermWin.saveLines;
2409 int col, row = selection.end.row + TermWin.saveLines;
2413 col = selection.beg.col;
2420 end_row += TermWin.nrow;
2421 for (; i < row && i < end_row; i++, col = 0)
2422 for (srp = screen.rend[i]; col < TermWin.ncol; col++)
2423 srp[col] ^= RS_RVid;
2425 if (i == row && i < end_row)
2426 for (srp = screen.rend[i]; col < selection.end.col; col++)
2427 srp[col] ^= RS_RVid;
2431 /* ------------------------------------------------------------------------- */
2433 * Dump the whole scrollback and screen to the passed filedescriptor. The
2434 * invoking routine must close the fd.
2438 rxvt_term::scr_dump (int fd)
2441 unsigned int width, towrite;
2444 for (row = TermWin.saveLines - TermWin.nscrolled;
2445 row < TermWin.saveLines + TermWin.nrow - 1; row++)
2447 width = screen.tlen[row] >= 0 ? screen.tlen[row]
2449 for (towrite = width; towrite; towrite -= wrote)
2451 wrote = write (fd, & (screen.text[row][width - towrite]),
2454 return; /* XXX: death, no report */
2456 if (screen.tlen[row] >= 0)
2457 if (write (fd, r1, 1) <= 0)
2458 return; /* XXX: death, no report */
2463 /* ------------------------------------------------------------------------- *
2464 * CHARACTER SELECTION *
2465 * ------------------------------------------------------------------------- */
2468 * -TermWin.nscrolled <= (selection row) <= TermWin.nrow - 1
2471 rxvt_term::selection_check (int check_more)
2478 pos.row = pos.col = 0;
2479 if ((selection.beg.row < - (int32_t)TermWin.nscrolled)
2480 || (selection.beg.row >= TermWin.nrow)
2481 || (selection.mark.row < - (int32_t)TermWin.nscrolled)
2482 || (selection.mark.row >= TermWin.nrow)
2483 || (selection.end.row < - (int32_t)TermWin.nscrolled)
2484 || (selection.end.row >= TermWin.nrow)
2486 && current_screen == selection.screen
2487 && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
2488 && ROWCOL_IS_BEFORE (screen.cur, selection.end))
2490 && ROWCOL_IS_BEFORE (selection.beg, pos)
2491 && ROWCOL_IS_AFTER (selection.end, pos))
2493 && ROWCOL_IS_AFTER (selection.end, pos))
2494 || (check_more == 4 /* screen width change */
2495 && (selection.beg.row != selection.end.row
2496 || selection.end.col > TermWin.ncol)))
2500 /* ------------------------------------------------------------------------- */
2502 * Paste a selection direct to the command fd
2505 rxvt_term::paste (const unsigned char *data, unsigned int len)
2507 unsigned int i, j, n;
2508 unsigned char *ds = (unsigned char *)rxvt_malloc (PROP_SIZE);
2511 /* a paste should act like the user is typing, so check scrollTtyKeypress */
2512 ZERO_SCROLLBACK (r);
2515 /* convert normal newline chars into common keyboard Return key sequence */
2516 for (i = 0; i < len; i += PROP_SIZE)
2518 n = min (len - i, PROP_SIZE);
2519 MEMCPY (ds, data + i, n);
2521 for (j = 0; j < n; j++)
2525 tt_write (ds, (int)n);
2531 /* ------------------------------------------------------------------------- */
2533 * Respond to a notification that a primary selection has been sent
2534 * EXT: SelectionNotify
2537 rxvt_term::selection_paste (Window win, Atom prop, bool delete_prop)
2540 unsigned long bytes_after;
2543 D_SELECT ((stderr, "rxvt_selection_paste (%08lx, %lu, %d), wait=%2x", win, (unsigned long)prop, (int)delete_prop, selection_wait));
2545 if (prop == None) /* check for failed XConvertSelection */
2547 if ((selection_type & Sel_CompoundText))
2549 int selnum = selection_type & Sel_whereMask;
2552 if (selnum != Sel_direct)
2553 selection_request_other (XA_STRING, selnum);
2556 if ((selection_type & Sel_UTF8String))
2558 int selnum = selection_type & Sel_whereMask;
2560 selection_type = Sel_CompoundText;
2561 if (selnum != Sel_direct)
2562 selection_request_other (xa[XA_COMPOUND_TEXT], selnum);
2572 if (XGetWindowProperty (display->display, win, prop, (long) (nread / 4),
2573 (long) (PROP_SIZE / 4), delete_prop,
2574 AnyPropertyType, &ct.encoding, &ct.format,
2575 &ct.nitems, &bytes_after,
2576 &ct.value) != Success)
2579 if (ct.encoding == 0)
2581 D_SELECT ((stderr, "rxvt_selection_paste: property didn't exist!"));
2585 if (ct.value == NULL)
2587 D_SELECT ((stderr, "rxvt_selection_paste: property shooting blanks!"));
2593 D_SELECT ((stderr, "rxvt_selection_paste: property empty - also INCR end"));
2595 if (selection_wait == Sel_normal && nread == 0
2596 && (win != display->root || prop != XA_CUT_BUFFER0)) // avoid recursion
2599 * pass through again trying CUT_BUFFER0 if we've come from
2600 * XConvertSelection () but nothing was presented
2602 D_SELECT ((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2603 selection_paste (display->root, XA_CUT_BUFFER0, False);
2606 nread = -1; /* discount any previous stuff */
2614 if (XmbTextPropertyToTextList (display->display, &ct, &cl,
2617 for (int i = 0; i < cr; i++)
2618 paste ((unsigned char *)cl[i], STRLEN (cl[i]));
2620 XFreeStringList (cl);
2623 paste (ct.value, ct.nitems);
2625 if (bytes_after == 0)
2634 if (selection_wait == Sel_normal)
2635 selection_wait = Sel_none;
2637 D_SELECT ((stderr, "rxvt_selection_paste: bytes written: %ld", nread));
2642 rxvt_term::incr_cb (time_watcher &w)
2644 selection_wait = Sel_none;
2646 rxvt_warn ("data loss: timeout on INCR selection paste, ignoring.\n");
2650 * INCR support originally provided by Paul Sheer <psheer@obsidian.co.za>
2653 rxvt_term::selection_property (Window win, Atom prop)
2660 D_SELECT ((stderr, "rxvt_selection_property (%08lx, %lu)", win, (unsigned long)prop));
2661 if (selection_wait == Sel_normal)
2665 unsigned long bytes_after, nitems;
2666 unsigned char *s = NULL;
2668 a = XGetWindowProperty (display->display, win, prop, 0L, 1L, False,
2669 xa[XA_INCR], &atype, &afmt, &nitems,
2676 #ifndef __CYGWIN32__
2677 if (atype == xa[XA_INCR])
2678 { /* start an INCR transfer */
2679 D_SELECT ((stderr, "rxvt_selection_property: INCR: starting transfer"));
2680 XDeleteProperty (display->display, win, prop);
2681 XFlush (display->display);
2683 selection_wait = Sel_incr;
2688 else if (selection_wait == Sel_incr)
2692 if (selection_paste (win, prop, True) == -1)
2694 D_SELECT ((stderr, "rxvt_selection_property: INCR: clean end"));
2695 selection_wait = Sel_none;
2699 if (reget_time) /* received more data so reget time */
2700 incr_ev.start (NOW + 10);
2703 /* ------------------------------------------------------------------------- */
2705 * Request the current selection:
2706 * Order: > internal selection if available
2707 * > PRIMARY, SECONDARY, CLIPBOARD if ownership is claimed (+)
2709 * (+) if ownership is claimed but property is empty, rxvt_selection_paste ()
2710 * will auto fallback to CUT_BUFFER0
2711 * EXT: button 2 release
2714 rxvt_term::selection_request (Time tm, int x, int y)
2716 D_SELECT ((stderr, "rxvt_selection_request (%lu, %d, %d)", tm, x, y));
2718 if (x < 0 || x >= TermWin.width || y < 0 || y >= TermWin.height)
2719 return; /* outside window */
2722 { /* internal selection */
2723 D_SELECT ((stderr, "rxvt_selection_request: pasting internal"));
2724 char *str = rxvt_wcstombs (selection.text, selection.len);
2725 paste ((unsigned char *)str, strlen (str));
2733 selection_request_time = tm;
2734 selection_wait = Sel_normal;
2736 for (i = Sel_Primary; i <= Sel_Clipboard; i++)
2738 #if X_HAVE_UTF8_STRING
2739 selection_type = Sel_UTF8String;
2740 if (selection_request_other (xa[XA_UTF8_STRING], i))
2743 selection_type = Sel_CompoundText;
2744 if (selection_request_other (xa[XA_COMPOUND_TEXT], i))
2751 selection_wait = Sel_none; /* don't loop in rxvt_selection_paste () */
2752 D_SELECT ((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2753 selection_paste (display->root, XA_CUT_BUFFER0, False);
2757 rxvt_term::selection_request_other (Atom target, int selnum)
2761 char *debug_xa_names[] = { "PRIMARY", "SECONDARY", "CLIPBOARD" };
2764 selection_type |= selnum;
2766 if (selnum == Sel_Primary)
2768 else if (selnum == Sel_Secondary)
2771 sel = xa[XA_CLIPBOARD];
2773 if (XGetSelectionOwner (display->display, sel) != None)
2775 D_SELECT ((stderr, "rxvt_selection_request_other: pasting %s", debug_xa_names[selnum]));
2776 XConvertSelection (display->display, sel, target, xa[XA_VT_SELECTION],
2777 TermWin.vt, selection_request_time);
2784 /* ------------------------------------------------------------------------- */
2786 * Clear all selected text
2787 * EXT: SelectionClear
2790 rxvt_term::selection_clear ()
2792 D_SELECT ((stderr, "rxvt_selection_clear ()"));
2795 free (selection.text);
2796 selection.text = NULL;
2800 if (display->selection_owner == this)
2801 display->selection_owner = 0;
2804 /* ------------------------------------------------------------------------- */
2806 * Copy a selection into the cut buffer
2807 * EXT: button 1 or 3 release
2810 rxvt_term::selection_make (Time tm)
2812 int i, col, end_col, row, end_row;
2813 wchar_t *new_selection_text;
2816 D_SELECT ((stderr, "rxvt_selection_make (): selection.op=%d, selection.clicks=%d", selection.op, selection.clicks));
2817 switch (selection.op)
2819 case SELECTION_CONT:
2821 case SELECTION_INIT:
2824 case SELECTION_BEGIN:
2825 selection.op = SELECTION_DONE;
2831 selection.op = SELECTION_DONE;
2833 if (selection.clicks == 4)
2834 return; /* nothing selected, go away */
2836 i = (selection.end.row - selection.beg.row + 1) * (TermWin.ncol + 1);
2837 new_selection_text = (wchar_t *)rxvt_malloc ((i + 4) * sizeof (wchar_t));
2839 col = selection.beg.col;
2841 row = selection.beg.row + TermWin.saveLines;
2842 end_row = selection.end.row + TermWin.saveLines;
2846 for (; row <= end_row; row++, col = 0)
2848 t = &(screen.text[row][col]);
2850 end_col = screen.tlen[row];
2853 end_col = TermWin.ncol;
2856 MIN_IT (end_col, selection.end.col);
2858 for (; col < end_col; col++)
2862 #if ENABLE_COMBINING
2863 else if (IS_COMPOSE (*t))
2865 int len = rxvt_composite.expand (*t, 0);
2873 new_selection_text = (wchar_t *)rxvt_realloc (new_selection_text, (i + 4) * sizeof (wchar_t));
2876 ofs += rxvt_composite.expand (*t++, new_selection_text + ofs);
2880 new_selection_text[ofs++] = *t++;
2883 if (screen.tlen[row] != -1 && row != end_row)
2884 new_selection_text[ofs++] = L'\n';
2887 if (end_col != selection.end.col)
2888 new_selection_text[ofs++] = L'\n';
2890 new_selection_text[ofs] = 0;
2894 free (new_selection_text);
2898 free (selection.text);
2900 // we usually allocate much more than necessary, so realloc it smaller again
2901 selection.len = ofs;
2902 selection.text = (wchar_t *)rxvt_realloc (new_selection_text, (ofs + 1) * sizeof (wchar_t));
2904 XSetSelectionOwner (display->display, XA_PRIMARY, TermWin.vt, tm);
2905 if (XGetSelectionOwner (display->display, XA_PRIMARY) == TermWin.vt)
2906 display->set_selection_owner (this);
2908 rxvt_warn ("can't get primary selection, ignoring.\n");
2913 if (XwcTextListToTextProperty (display->display, &selection.text, 1, XStringStyle, &ct) >= 0)
2915 XChangeProperty (display->display, display->root, XA_CUT_BUFFER0, XA_STRING, 8,
2916 PropModeReplace, ct.value, ct.nitems);
2921 selection_time = tm;
2922 D_SELECT ((stderr, "rxvt_selection_make (): selection.len=%d", selection.len));
2925 /* ------------------------------------------------------------------------- */
2927 * Mark or select text based upon number of clicks: 1, 2, or 3
2928 * EXT: button 1 press
2931 rxvt_term::selection_click (int clicks, int x, int y)
2933 D_SELECT ((stderr, "rxvt_selection_click (%d, %d, %d)", clicks, x, y));
2935 clicks = ((clicks - 1) % 3) + 1;
2936 selection.clicks = clicks; /* save clicks so extend will work */
2938 selection_start_colrow (Pixel2Col (x), Pixel2Row (y));
2940 if (clicks == 2 || clicks == 3)
2941 selection_extend_colrow (selection.mark.col,
2942 selection.mark.row + TermWin.view_start,
2944 1, /* button press */
2945 0); /* click change */
2948 /* ------------------------------------------------------------------------- */
2950 * Mark a selection at the specified col/row
2953 rxvt_term::selection_start_colrow (int col, int row)
2956 selection.mark.col = col;
2957 selection.mark.row = row - TermWin.view_start;
2959 MAX_IT (selection.mark.row, - (int32_t)TermWin.nscrolled);
2960 MIN_IT (selection.mark.row, (int32_t)TermWin.nrow - 1);
2961 MAX_IT (selection.mark.col, 0);
2962 MIN_IT (selection.mark.col, (int32_t)TermWin.ncol - 1);
2964 while (selection.mark.col > 0
2965 && screen.text[selection.mark.row + TermWin.saveLines][selection.mark.col] == NOCHAR)
2966 --selection.mark.col;
2969 { /* clear the old selection */
2970 selection.beg.row = selection.end.row = selection.mark.row;
2971 selection.beg.col = selection.end.col = selection.mark.col;
2974 selection.op = SELECTION_INIT;
2975 selection.screen = current_screen;
2978 /* ------------------------------------------------------------------------- */
2980 * Word select: select text for 2 clicks
2981 * We now only find out the boundary in one direction
2984 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
2985 #define DELIMIT_TEXT(x) \
2986 (unicode::is_space (x) ? 2 : (x) <= 0xff && !!STRCHR (rs[Rs_cutchars], (x)))
2987 #define DELIMIT_REND(x) 1
2990 rxvt_term::selection_delimit_word (enum page_dirn dirn, const row_col_t *mark, row_col_t *ret)
2992 int col, row, dirnadd, tcol, trow, w1, w2;
2999 bound.row = TermWin.saveLines - TermWin.nscrolled - 1;
3005 bound.row = TermWin.saveLines + TermWin.nrow;
3006 bound.col = TermWin.ncol - 1;
3010 row = mark->row + TermWin.saveLines;
3013 /* find the edge of a word */
3014 stp = & (screen.text[row][col]);
3015 w1 = DELIMIT_TEXT (*stp);
3017 srp = (&screen.rend[row][col]);
3018 w2 = DELIMIT_REND (*srp);
3022 for (; col != bound.col; col += dirnadd)
3030 if (DELIMIT_TEXT (*stp) != w1)
3032 if (DELIMIT_REND (*srp) != w2)
3036 if ((col == bound.col) && (row != bound.row))
3038 if (screen.tlen[ (row - (dirn == UP ? 1 : 0))] == -1)
3040 trow = row + dirnadd;
3041 tcol = dirn == UP ? TermWin.ncol - 1 : 0;
3043 if (screen.text[trow] == NULL)
3046 stp = & (screen.text[trow][tcol]);
3047 srp = & (screen.rend[trow][tcol]);
3049 if (DELIMIT_TEXT (*stp) != w1 || DELIMIT_REND (*srp) != w2)
3060 Old_Word_Selection_You_Die:
3061 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));
3064 col++; /* put us on one past the end */
3066 /* Poke the values back in */
3067 ret->row = row - TermWin.saveLines;
3071 /* ------------------------------------------------------------------------- */
3073 * Extend the selection to the specified x/y pixel location
3074 * EXT: button 3 press; button 1 or 3 drag
3075 * flag == 0 ==> button 1
3076 * flag == 1 ==> button 3 press
3077 * flag == 2 ==> button 3 motion
3080 rxvt_term::selection_extend (int x, int y, int flag)
3084 col = Pixel2Col (x);
3085 row = Pixel2Row (y);
3087 MIN_IT (row, (int)TermWin.nrow - 1);
3089 MIN_IT (col, (int)TermWin.ncol);
3092 * If we're selecting characters (single click) then we must check first
3093 * if we are at the same place as the original mark. If we are then
3094 * select nothing. Otherwise, if we're to the right of the mark, you have to
3095 * be _past_ a character for it to be selected.
3097 if (((selection.clicks % 3) == 1) && !flag
3098 && (col == selection.mark.col
3099 && (row == selection.mark.row + TermWin.view_start)))
3101 /* select nothing */
3102 selection.beg.row = selection.end.row = 0;
3103 selection.beg.col = selection.end.col = 0;
3104 selection.clicks = 4;
3106 D_SELECT ((stderr, "rxvt_selection_extend () selection.clicks = 4"));
3110 if (selection.clicks == 4)
3111 selection.clicks = 1;
3113 selection_extend_colrow (col, row, !!flag, /* ? button 3 */
3114 flag == 1 ? 1 : 0, /* ? button press */
3115 0); /* no click change */
3118 /* ------------------------------------------------------------------------- */
3120 * Extend the selection to the specified col/row
3123 rxvt_term::selection_extend_colrow (int32_t col, int32_t row, int button3, int buttonpress, int clickchange)
3125 int16_t ncol = TermWin.ncol;
3132 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));
3133 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));
3136 switch (selection.op)
3138 case SELECTION_INIT:
3140 selection.op = SELECTION_BEGIN;
3142 case SELECTION_BEGIN:
3143 if (row != selection.mark.row || col != selection.mark.col
3144 || (!button3 && buttonpress))
3145 selection.op = SELECTION_CONT;
3147 case SELECTION_DONE:
3148 selection.op = SELECTION_CONT;
3150 case SELECTION_CONT:
3152 case SELECTION_CLEAR:
3153 selection_start_colrow (col, row);
3158 if (selection.beg.col == selection.end.col
3159 && selection.beg.col != selection.mark.col
3160 && selection.beg.row == selection.end.row
3161 && selection.beg.row != selection.mark.row)
3163 selection.beg.col = selection.end.col = selection.mark.col;
3164 selection.beg.row = selection.end.row = selection.mark.row;
3165 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));
3171 pos.row -= TermWin.view_start; /* adjust for scroll */
3174 * This is mainly xterm style selection with a couple of differences, mainly
3175 * in the way button3 drag extension works.
3176 * We're either doing: button1 drag; button3 press; or button3 drag
3177 * a) button1 drag : select around a midpoint/word/line - that point/word/line
3178 * is always at the left/right edge of the selection.
3179 * b) button3 press: extend/contract character/word/line at whichever edge of
3180 * the selection we are closest to.
3181 * c) button3 drag : extend/contract character/word/line - we select around
3182 * a point/word/line which is either the start or end of the selection
3183 * and it was decided by whichever point/word/line was `fixed' at the
3184 * time of the most recent button3 press
3186 if (button3 && buttonpress)
3187 { /* button3 press */
3189 * first determine which edge of the selection we are closest to
3191 if (ROWCOL_IS_BEFORE (pos, selection.beg)
3192 || (!ROWCOL_IS_AFTER (pos, selection.end)
3193 && (((pos.col - selection.beg.col)
3194 + ((pos.row - selection.beg.row) * ncol))
3195 < ((selection.end.col - pos.col)
3196 + ((selection.end.row - pos.row) * ncol)))))
3199 if (closeto == LEFT)
3201 selection.beg.row = pos.row;
3202 selection.beg.col = pos.col;
3203 selection.mark.row = selection.end.row;
3204 selection.mark.col = selection.end.col - (selection.clicks == 2);
3208 selection.end.row = pos.row;
3209 selection.end.col = pos.col;
3210 selection.mark.row = selection.beg.row;
3211 selection.mark.col = selection.beg.col;
3215 { /* button1 drag or button3 drag */
3216 if (ROWCOL_IS_AFTER (selection.mark, pos))
3218 if ((selection.mark.row == selection.end.row)
3219 && (selection.mark.col == selection.end.col)
3220 && clickchange && selection.clicks == 2)
3221 selection.mark.col--;
3223 selection.beg.row = pos.row;
3224 selection.beg.col = pos.col;
3225 selection.end.row = selection.mark.row;
3226 selection.end.col = selection.mark.col + (selection.clicks == 2);
3230 selection.beg.row = selection.mark.row;
3231 selection.beg.col = selection.mark.col;
3232 selection.end.row = pos.row;
3233 selection.end.col = pos.col;
3237 if (selection.clicks == 1)
3239 end_col = screen.tlen[selection.beg.row + TermWin.saveLines];
3241 if (end_col != -1 && selection.beg.col > end_col)
3244 selection.beg.col = ncol;
3246 if (selection.beg.row != selection.end.row)
3247 selection.beg.col = ncol;
3249 selection.beg.col = selection.mark.col;
3253 end_col = screen.tlen[selection.end.row + TermWin.saveLines];
3255 if (end_col != -1 && selection.end.col > end_col)
3256 selection.end.col = ncol;
3258 else if (selection.clicks == 2)
3260 if (ROWCOL_IS_AFTER (selection.end, selection.beg))
3261 selection.end.col--;
3263 selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3264 selection_delimit_word (DN, & (selection.end), & (selection.end));
3266 else if (selection.clicks == 3)
3269 if ((Options & Opt_tripleclickwords))
3273 selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3274 end_row = screen.tlen[selection.mark.row + TermWin.saveLines];
3276 for (end_row = selection.mark.row; end_row < TermWin.nrow; end_row++)
3278 end_col = screen.tlen[end_row + TermWin.saveLines];
3282 selection.end.row = end_row;
3283 selection.end.col = end_col;
3284 selection_remove_trailing_spaces ();
3292 if (ROWCOL_IS_AFTER (selection.mark, selection.beg))
3293 selection.mark.col++;
3294 selection.beg.col = 0;
3295 selection.end.col = ncol;
3299 if (button3 && buttonpress)
3300 { /* mark may need to be changed */
3301 if (closeto == LEFT)
3303 selection.mark.row = selection.end.row;
3304 selection.mark.col = selection.end.col
3305 - (selection.clicks == 2);
3309 selection.mark.row = selection.beg.row;
3310 selection.mark.col = selection.beg.col;
3313 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));
3318 rxvt_term::selection_remove_trailing_spaces ()
3320 int32_t end_col, end_row;
3323 end_col = selection.end.col;
3324 end_row = selection.end.row;
3325 for ( ; end_row >= selection.beg.row; )
3327 stp = screen.text[end_row + TermWin.saveLines];
3328 while (--end_col >= 0)
3330 if (stp[end_col] != ' ' && stp[end_col] != '\t')
3334 || screen.tlen[end_row - 1 + TermWin.saveLines] != -1)
3336 selection.end.col = end_col + 1;
3337 selection.end.row = end_row;
3341 end_col = TermWin.ncol;
3343 if (selection.mark.row > selection.end.row)
3345 selection.mark.row = selection.end.row;
3346 selection.mark.col = selection.end.col;
3348 else if (selection.mark.row == selection.end.row
3349 && selection.mark.col > selection.end.col)
3350 selection.mark.col = selection.end.col;
3354 /* ------------------------------------------------------------------------- */
3356 * Double click on button 3 when already selected
3357 * EXT: button 3 double click
3360 rxvt_term::selection_rotate (int x, int y)
3362 selection.clicks = selection.clicks % 3 + 1;
3363 selection_extend_colrow (Pixel2Col (x), Pixel2Row (y), 1, 0, 1);
3366 /* ------------------------------------------------------------------------- */
3368 * On some systems, the Atom typedef is 64 bits wide. We need to have a type
3369 * that is exactly 32 bits wide, because a format of 64 is not allowed by
3372 typedef CARD32 Atom32;
3374 /* ------------------------------------------------------------------------- */
3376 * Respond to a request for our current selection
3377 * EXT: SelectionRequest
3380 rxvt_term::selection_send (const XSelectionRequestEvent &rq)
3384 XICCEncodingStyle style;
3387 ev.type = SelectionNotify;
3389 ev.display = rq.display;
3390 ev.requestor = rq.requestor;
3391 ev.selection = rq.selection;
3392 ev.target = rq.target;
3395 if (rq.target == xa[XA_TARGETS])
3397 Atom32 target_list[6];
3398 Atom32 *target = target_list;
3400 *target++ = (Atom32) xa[XA_TARGETS];
3401 *target++ = (Atom32) xa[XA_TIMESTAMP];
3402 *target++ = (Atom32) XA_STRING;
3403 *target++ = (Atom32) xa[XA_TEXT];
3404 *target++ = (Atom32) xa[XA_COMPOUND_TEXT];
3405 #if X_HAVE_UTF8_STRING
3406 *target++ = (Atom32) xa[XA_UTF8_STRING];
3409 XChangeProperty (display->display, rq.requestor, rq.property, XA_ATOM,
3410 (8 * sizeof (target_list[0])), PropModeReplace,
3411 (unsigned char *)target_list,
3412 target - target_list);
3413 ev.property = rq.property;
3415 else if (rq.target == xa[XA_MULTIPLE])
3417 /* TODO: Handle MULTIPLE */
3419 else if (rq.target == xa[XA_TIMESTAMP] && selection.text)
3421 XChangeProperty (display->display, rq.requestor, rq.property, XA_INTEGER,
3422 (8 * sizeof (Time)), PropModeReplace,
3423 (unsigned char *)&selection_time, 1);
3424 ev.property = rq.property;
3426 else if (rq.target == XA_STRING
3427 || rq.target == xa[XA_TEXT]
3428 || rq.target == xa[XA_COMPOUND_TEXT]
3429 || rq.target == xa[XA_UTF8_STRING]
3438 if (target == XA_STRING)
3439 // we actually don't do XA_STRING, but who cares, as i18n clients
3440 // will ask for another format anyways.
3441 style = XStringStyle;
3442 else if (target == xa[XA_TEXT])
3443 style = XStdICCTextStyle;
3444 else if (target == xa[XA_COMPOUND_TEXT])
3445 style = XCompoundTextStyle;
3446 #if X_HAVE_UTF8_STRING
3447 else if (target == xa[XA_UTF8_STRING])
3448 style = XUTF8StringStyle;
3452 target = xa[XA_COMPOUND_TEXT];
3453 style = XCompoundTextStyle;
3458 cl = selection.text;
3459 selectlen = selection.len;
3467 // Xwc doesn't handle iso-10646 in wchar_t gracefully, so maybe recode it
3468 // manually for XUTF8StringStyle.
3469 if (XwcTextListToTextProperty (display->display, &cl, 1, style, &ct) >= 0)
3473 /* if we failed to convert then send it raw */
3474 ct.value = (unsigned char *)cl;
3475 ct.nitems = selectlen;
3478 XChangeProperty (display->display, rq.requestor, rq.property,
3479 target, 8, PropModeReplace,
3480 ct.value, (int)ct.nitems);
3481 ev.property = rq.property;
3487 XSendEvent (display->display, rq.requestor, False, 0L, (XEvent *)&ev);
3490 /* ------------------------------------------------------------------------- *
3492 * ------------------------------------------------------------------------- */
3495 * return col/row values corresponding to x/y pixel values
3498 rxvt_term::pixel_position (int *x, int *y)
3500 *x = Pixel2Col (*x);
3501 /* MAX_IT (*x, 0); MIN_IT (*x, (int)TermWin.ncol - 1); */
3502 *y = Pixel2Row (*y);
3503 /* MAX_IT (*y, 0); MIN_IT (*y, (int)TermWin.nrow - 1); */
3506 /* ------------------------------------------------------------------------- */
3509 rxvt_term::im_set_position (XPoint *pos)
3511 XWindowAttributes xwa;
3513 XGetWindowAttributes (display->display, TermWin.vt, &xwa);
3514 pos->x = Col2Pixel (screen.cur.col) + xwa.x;
3515 pos->y = Height2Pixel ((screen.cur.row + 1)) + xwa.y - TermWin.lineSpace;
3519 /* ------------------------------------------------------------------------- */
3521 /* ------------------------------------------------------------------------- *
3523 * ------------------------------------------------------------------------- */
3526 rxvt_debug_colors (void)
3529 const char *name[] =
3532 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
3535 fprintf (stderr, "Color ( ");
3536 if (rstyle & RS_RVid)
3537 fprintf (stderr, "rvid ");
3538 if (rstyle & RS_Bold)
3539 fprintf (stderr, "bold ");
3540 if (rstyle & RS_Blink)
3541 fprintf (stderr, "blink ");
3542 if (rstyle & RS_Uline)
3543 fprintf (stderr, "uline ");
3544 fprintf (stderr, "): ");
3546 color = GET_FGCOLOR (rstyle);
3547 #ifndef NO_BRIGHTCOLOR
3548 if (color >= minBrightCOLOR && color <= maxBrightCOLOR)
3550 color -= (minBrightCOLOR - minCOLOR);
3551 fprintf (stderr, "bright ");
3554 fprintf (stderr, "%s on ", name[color]);
3556 color = GET_BGCOLOR (rstyle);
3557 #ifndef NO_BRIGHTCOLOR
3558 if (color >= minBrightCOLOR && color <= maxBrightCOLOR)
3560 color -= (minBrightCOLOR - minCOLOR);
3561 fprintf (stderr, "bright ");
3564 fprintf (stderr, "%s\n", name[color]);