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.ascii_map [' ' - 0x20]);
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)
177 // we need at least two lines for wrapping to work correctly
178 if (nrow + TermWin.saveLines < 2)
187 prev_total_rows = prev_nrow + TermWin.saveLines;
188 total_rows = nrow + TermWin.saveLines;
191 screen.bscroll = nrow - 1;
195 talloc = new rxvt_salloc (ncol * sizeof (text_t));
196 ralloc = new rxvt_salloc (ncol * sizeof (rend_t));
202 * first time called so just malloc everything: don't rely on realloc
203 * Note: this is still needed so that all the scrollback lines are NULL
205 screen.text = (text_t **)rxvt_calloc (total_rows, sizeof (text_t *));
206 buf_text = (text_t **)rxvt_calloc (total_rows, sizeof (text_t *));
207 drawn_text = (text_t **)rxvt_calloc (nrow, sizeof (text_t *));
208 swap.text = (text_t **)rxvt_calloc (nrow, sizeof (text_t *));
210 screen.tlen = (int16_t *)rxvt_calloc (total_rows, sizeof (int16_t));
211 swap.tlen = (int16_t *)rxvt_calloc (nrow, sizeof (int16_t));
213 screen.rend = (rend_t **)rxvt_calloc (total_rows, sizeof (rend_t *));
214 buf_rend = (rend_t **)rxvt_calloc (total_rows, sizeof (rend_t *));
215 drawn_rend = (rend_t **)rxvt_calloc (nrow, sizeof (rend_t *));
216 swap.rend = (rend_t **)rxvt_calloc (nrow, sizeof (rend_t *));
218 for (p = 0; p < nrow; p++)
220 q = p + TermWin.saveLines;
221 scr_blank_screen_mem (screen.text, screen.rend, q, DEFAULT_RSTYLE);
222 scr_blank_screen_mem (swap.text, swap.rend, p, DEFAULT_RSTYLE);
223 screen.tlen[q] = swap.tlen[p] = 0;
224 scr_blank_screen_mem (drawn_text, drawn_rend, p, DEFAULT_RSTYLE);
227 MEMSET (charsets, 'B', sizeof (charsets));
228 TermWin.nscrolled = 0; /* no saved lines */
229 rstyle = DEFAULT_RSTYLE;
230 screen.flags = Screen_DefaultFlags;
231 screen.cur.row = screen.cur.col = 0;
233 current_screen = PRIMARY;
237 swap.flags = Screen_DefaultFlags;
238 swap.cur.row = swap.cur.col = 0;
240 current_screen = SECONDARY;
242 current_screen = PRIMARY;
245 selection.text = NULL;
247 selection.op = SELECTION_CLEAR;
248 selection.screen = PRIMARY;
249 selection.clicks = 0;
250 CLEAR_ALL_SELECTION ();
256 * add or delete rows as appropriate
258 setrstyle = DEFAULT_RSTYLE;
260 if (nrow < prev_nrow)
263 k = min (TermWin.nscrolled, prev_nrow - nrow);
264 scr_scroll_text (0, (int)prev_nrow - 1, k, 1);
266 for (p = nrow; p < prev_nrow; p++)
268 q = p + TermWin.saveLines;
273 assert (screen.rend[q]);
275 talloc->free (screen.text[q]);
276 ralloc->free (screen.rend[q]);
282 assert (swap.rend[p]);
284 talloc->free (swap.text[p]);
285 ralloc->free (swap.rend[p]);
289 assert (drawn_text[p] && drawn_rend[p]);
291 talloc->free (drawn_text[p]);
292 ralloc->free (drawn_rend[p]);
295 /* we have fewer rows so fix up cursor position */
296 MIN_IT (screen.cur.row, (int32_t)nrow - 1);
297 MIN_IT (swap.cur.row, (int32_t)nrow - 1);
299 scr_reset_realloc (); /* realloc _last_ */
301 else if (nrow > prev_nrow)
304 scr_reset_realloc (); /* realloc _first_ */
306 TermWin.ncol = prev_ncol; // save b/c scr_blank_screen_mem uses this
308 k = min (TermWin.nscrolled, nrow - prev_nrow);
310 for (p = prev_total_rows; p < total_rows; p++)
313 screen.text[p] = NULL;
314 screen.rend[p] = NULL;
317 for (p = prev_total_rows; p < total_rows - k; p++)
318 scr_blank_screen_mem (screen.text, screen.rend, p, setrstyle);
320 for (p = prev_nrow; p < nrow; p++)
325 drawn_text[p] = NULL;
326 drawn_rend[p] = NULL;
327 scr_blank_screen_mem (swap.text, swap.rend, p, setrstyle);
328 scr_blank_screen_mem (drawn_text, drawn_rend, p, setrstyle);
333 scr_scroll_text (0, (int)nrow - 1, -k, 1);
335 screen.s_cur.row += k;
336 TermWin.nscrolled -= k;
339 assert (screen.cur.row < TermWin.nrow);
340 assert (swap.cur.row < TermWin.nrow);
341 #else /* drive with your eyes closed */
343 MIN_IT (screen.cur.row, nrow - 1);
344 MIN_IT (swap.cur.row, nrow - 1);
346 TermWin.ncol = ncol; // save b/c scr_blank_screen_mem uses this
350 if (ncol != prev_ncol)
352 rxvt_salloc *ta = new rxvt_salloc (ncol * sizeof (text_t));
353 rxvt_salloc *ra = new rxvt_salloc (ncol * sizeof (rend_t));
355 for (p = 0; p < total_rows; p++)
359 screen.text[p] = (text_t *)ta->alloc (screen.text[p], prev_ncol * sizeof (text_t));
360 screen.rend[p] = (rend_t *)ra->alloc (screen.rend[p], prev_ncol * sizeof (rend_t));
362 MIN_IT (screen.tlen[p], (int16_t)ncol);
364 if (ncol > prev_ncol)
365 scr_blank_line (&screen.text[p][prev_ncol],
366 &screen.rend[p][prev_ncol],
367 ncol - prev_ncol, setrstyle);
371 for (p = 0; p < nrow; p++)
373 drawn_text[p] = (text_t *)ta->alloc (drawn_text[p], prev_ncol * sizeof (text_t));
374 drawn_rend[p] = (rend_t *)ra->alloc (drawn_rend[p], prev_ncol * sizeof (rend_t));
376 if (ncol > prev_ncol)
377 scr_blank_line (&drawn_text[p][prev_ncol],
378 &drawn_rend[p][prev_ncol],
379 ncol - prev_ncol, setrstyle);
383 swap.text[p] = (text_t *)ta->alloc (swap.text[p], prev_ncol * sizeof (text_t));
384 swap.rend[p] = (rend_t *)ra->alloc (swap.rend[p], prev_ncol * sizeof (rend_t));
386 MIN_IT (swap.tlen[p], (int16_t)ncol);
388 if (ncol > prev_ncol)
389 scr_blank_line (&swap.text[p][prev_ncol],
390 &swap.rend[p][prev_ncol],
391 ncol - prev_ncol, setrstyle);
396 MIN_IT (screen.cur.col, (int16_t)ncol - 1);
397 MIN_IT (swap.cur.col, (int16_t)ncol - 1);
399 delete talloc; talloc = ta;
400 delete ralloc; ralloc = ra;
410 tabs = (char *)rxvt_malloc (ncol * sizeof (char));
412 for (p = 0; p < ncol; p++)
413 tabs[p] = (p % TABSIZE == 0) ? 1 : 0;
419 rxvt_term::scr_reset_realloc ()
421 uint16_t total_rows, nrow;
424 total_rows = nrow + TermWin.saveLines;
426 screen.text = (text_t **)rxvt_realloc (screen.text, total_rows * sizeof (text_t *));
427 buf_text = (text_t **)rxvt_realloc (buf_text , total_rows * sizeof (text_t *));
428 drawn_text = (text_t **)rxvt_realloc (drawn_text , nrow * sizeof (text_t *));
429 swap.text = (text_t **)rxvt_realloc (swap.text , nrow * sizeof (text_t *));
431 screen.tlen = (int16_t *)rxvt_realloc (screen.tlen, total_rows * sizeof (int16_t));
432 swap.tlen = (int16_t *)rxvt_realloc (swap.tlen , total_rows * sizeof (int16_t));
434 screen.rend = (rend_t **)rxvt_realloc (screen.rend, total_rows * sizeof (rend_t *));
435 buf_rend = (rend_t **)rxvt_realloc (buf_rend , total_rows * sizeof (rend_t *));
436 drawn_rend = (rend_t **)rxvt_realloc (drawn_rend , nrow * sizeof (rend_t *));
437 swap.rend = (rend_t **)rxvt_realloc (swap.rend , nrow * sizeof (rend_t *));
441 /* ------------------------------------------------------------------------- */
443 * Free everything. That way malloc debugging can find leakage.
446 rxvt_term::scr_release ()
451 total_rows = TermWin.nrow + TermWin.saveLines;
453 delete talloc; talloc = 0;
454 delete ralloc; ralloc = 0;
468 /* NULL these so if anything tries to use them, we'll know about it */
469 screen.text = drawn_text = swap.text = NULL;
470 screen.rend = drawn_rend = swap.rend = NULL;
471 screen.tlen = swap.tlen = NULL;
477 /* ------------------------------------------------------------------------- */
482 rxvt_term::scr_poweron ()
484 D_SCREEN ((stderr, "rxvt_scr_poweron ()"));
487 prev_nrow = prev_ncol = 0;
491 scr_refresh (SLOW_REFRESH);
494 /* ------------------------------------------------------------------------- *
495 * PROCESS SCREEN COMMANDS *
496 * ------------------------------------------------------------------------- */
498 * Save and Restore cursor
499 * XTERM_SEQ: Save cursor : ESC 7
500 * XTERM_SEQ: Restore cursor: ESC 8
503 rxvt_term::scr_cursor (int mode)
507 D_SCREEN ((stderr, "rxvt_scr_cursor (%c)", mode));
509 #if NSCREENS && !defined(NO_SECONDARY_SCREEN_CURSOR)
510 if (current_screen == SECONDARY)
519 s->s_cur.row = s->cur.row;
520 s->s_cur.col = s->cur.col;
521 s->s_rstyle = rstyle;
522 s->s_charset = s->charset;
523 s->s_charset_char = charsets[s->charset];
528 s->cur.row = s->s_cur.row;
529 s->cur.col = s->s_cur.col;
530 s->flags &= ~Screen_WrapNext;
531 rstyle = s->s_rstyle;
532 s->charset = s->s_charset;
533 charsets[s->charset] = s->s_charset_char;
538 /* boundary check in case screen size changed between SAVE and RESTORE */
539 MIN_IT (s->cur.row, TermWin.nrow - 1);
540 MIN_IT (s->cur.col, TermWin.ncol - 1);
542 assert (s->cur.row >= 0);
543 assert (s->cur.col >= 0);
544 #else /* drive with your eyes closed */
545 MAX_IT (s->cur.row, 0);
546 MAX_IT (s->cur.col, 0);
550 /* ------------------------------------------------------------------------- */
552 * Swap between primary and secondary screens
553 * XTERM_SEQ: Primary screen : ESC [ ? 4 7 h
554 * XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
557 rxvt_term::scr_change_screen (int scrn)
566 D_SCREEN ((stderr, "rxvt_scr_change_screen (%d)", scrn));
568 TermWin.view_start = 0;
570 if (current_screen == scrn)
573 selection_check (2); /* check for boundary cross */
575 SWAP_IT (current_screen, scrn, int);
577 if (Options & Opt_secondaryScreen)
580 offset = TermWin.saveLines;
581 for (i = prev_nrow; i--;)
583 SWAP_IT (screen.text[i + offset], swap.text[i], text_t *);
584 SWAP_IT (screen.tlen[i + offset], swap.tlen[i], int16_t);
585 SWAP_IT (screen.rend[i + offset], swap.rend[i], rend_t *);
587 SWAP_IT (screen.cur.row, swap.cur.row, int16_t);
588 SWAP_IT (screen.cur.col, swap.cur.col, int16_t);
590 assert ((screen.cur.row >= 0) && (screen.cur.row < prev_nrow));
591 assert ((screen.cur.col >= 0) && (screen.cur.col < prev_ncol));
592 # else /* drive with your eyes closed */
593 MAX_IT (screen.cur.row, 0);
594 MIN_IT (screen.cur.row, (int32_t)prev_nrow - 1);
595 MAX_IT (screen.cur.col, 0);
596 MIN_IT (screen.cur.col, (int32_t)prev_ncol - 1);
598 SWAP_IT (screen.charset, swap.charset, int16_t);
599 SWAP_IT (screen.flags, swap.flags, int);
600 screen.flags |= Screen_VisibleCursor;
601 swap.flags |= Screen_VisibleCursor;
605 if (Options & Opt_secondaryScroll)
606 //if (current_screen == PRIMARY)
607 scr_scroll_text (0, (prev_nrow - 1), prev_nrow, 0);
611 /* ------------------------------------------------------------------------- */
613 * Change the colour for following text
616 rxvt_term::scr_color (unsigned int color, int fgbg)
619 if (fgbg == Color_fg)
620 rstyle = SET_FGCOLOR (rstyle, color);
622 rstyle = SET_BGCOLOR (rstyle, color);
625 /* ------------------------------------------------------------------------- */
627 * Change the rendition style for following text
630 rxvt_term::scr_rendition (int set, int style)
634 else if (style == ~RS_None)
635 rstyle = DEFAULT_RSTYLE;
640 /* ------------------------------------------------------------------------- */
642 * Scroll text between <row1> and <row2> inclusive, by <count> lines
643 * count positive ==> scroll up
644 * count negative ==> scroll down
645 * spec == 0 for normal routines
648 rxvt_term::scr_scroll_text (int row1, int row2, int count, int spec)
653 if (count == 0 || (row1 > row2))
657 D_SCREEN ((stderr, "rxvt_scroll_text (%d,%d,%d,%d): %s", row1, row2, count, spec, (current_screen == PRIMARY) ? "Primary" : "Secondary"));
659 if (row1 == 0 && count > 0
660 && (current_screen == PRIMARY || Options & Opt_secondaryScroll))
662 nscrolled = (long)TermWin.nscrolled + (long)count;
664 if (nscrolled > (long)TermWin.saveLines)
665 TermWin.nscrolled = TermWin.saveLines;
667 TermWin.nscrolled = (uint16_t)nscrolled;
669 if ((Options & Opt_scrollWithBuffer)
670 && TermWin.view_start != 0
671 && TermWin.view_start != TermWin.saveLines)
672 scr_page (UP, count);
675 row1 += TermWin.saveLines;
677 row2 += TermWin.saveLines;
679 if (selection.op && current_screen == selection.screen)
681 i = selection.beg.row + TermWin.saveLines;
682 j = selection.end.row + TermWin.saveLines;
683 if ((i < row1 && j > row1)
684 || (i < row2 && j > row2)
685 || (i - count < row1 && i >= row1)
686 || (i - count > row2 && i <= row2)
687 || (j - count < row1 && j >= row1)
688 || (j - count > row2 && j <= row2))
690 CLEAR_ALL_SELECTION ();
691 selection.op = SELECTION_CLEAR; /* XXX: too aggressive? */
693 else if (j >= row1 && j <= row2)
695 /* move selected region too */
696 selection.beg.row -= count;
697 selection.end.row -= count;
698 selection.mark.row -= count;
702 selection_check (0); /* _after_ TermWin.nscrolled update */
717 /* A1: Copy lines that will get clobbered by the rotation */
718 memcpy (buf_text, screen.text + row1, count * sizeof (text_t *));
719 memcpy (buf_rend, screen.rend + row1, count * sizeof (rend_t *));
721 /* A2: Rotate lines */
722 i = row2 - row1 - count + 1;
723 memmove (screen.tlen + row1, screen.tlen + row1 + count, i * sizeof (int16_t));
724 memmove (screen.text + row1, screen.text + row1 + count, i * sizeof (text_t *));
725 memmove (screen.rend + row1, screen.rend + row1 + count, i * sizeof (rend_t *));
727 j = row2 - count + 1, i = count;
729 else /* if (j < 0) */
733 /* B1: Copy lines that will get clobbered by the rotation */
734 for (i = 0, j = row2; i < count; i++, j--)
736 buf_text[i] = screen.text[j];
737 buf_rend[i] = screen.rend[j];
740 /* B2: Rotate lines */
741 for (j = row2, i = j - count; i >= row1; i--, j--)
743 screen.tlen[j] = screen.tlen[i];
744 screen.text[j] = screen.text[i];
745 screen.rend[j] = screen.rend[i];
752 /* C: Resurrect lines */
753 memset (screen.tlen + j, 0, i * sizeof (int16_t));
754 memcpy (screen.text + j, buf_text, i * sizeof (text_t *));
755 memcpy (screen.rend + j, buf_rend, i * sizeof (text_t *));
756 if (!spec) /* line length may not equal TermWin.ncol */
758 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;
798 assert (screen.cur.col < last_col);
799 assert ((screen.cur.row < TermWin.nrow)
800 && (screen.cur.row >= - (int32_t)TermWin.nscrolled));
801 #else /* drive with your eyes closed */
802 MIN_IT (screen.cur.col, last_col - 1);
803 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
804 MAX_IT (screen.cur.row, - (int32_t)TermWin.nscrolled);
806 row = screen.cur.row + TermWin.saveLines;
808 checksel = selection.op && current_screen == selection.screen ? 1 : 0;
811 stp = screen.text[row];
812 srp = screen.rend[row];
826 if (screen.tlen[row] != -1) /* XXX: think about this */
827 MAX_IT (screen.tlen[row], screen.cur.col);
829 screen.flags &= ~Screen_WrapNext;
831 if (screen.cur.row == screen.bscroll)
832 scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
833 else if (screen.cur.row < (TermWin.nrow - 1))
834 row = (++screen.cur.row) + TermWin.saveLines;
836 stp = screen.text[row]; /* _must_ refresh */
837 srp = screen.rend[row]; /* _must_ refresh */
841 if (screen.tlen[row] != -1) /* XXX: think about this */
842 MAX_IT (screen.tlen[row], screen.cur.col);
844 screen.flags &= ~Screen_WrapNext;
849 if (checksel /* see if we're writing within selection */
850 && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
851 && ROWCOL_IS_BEFORE (screen.cur, selection.end))
857 if (screen.flags & Screen_WrapNext)
859 screen.tlen[row] = -1;
860 if (screen.cur.row == screen.bscroll)
861 scr_scroll_text (screen.tscroll, screen.bscroll, 1, 0);
862 else if (screen.cur.row < (TermWin.nrow - 1))
863 row = (++screen.cur.row) + TermWin.saveLines;
865 stp = screen.text[row]; /* _must_ refresh */
866 srp = screen.rend[row]; /* _must_ refresh */
868 screen.flags &= ~Screen_WrapNext;
871 if (screen.flags & Screen_Insert)
872 scr_insdel_chars (1, INSERT);
874 // rely on wcwidth to tell us the character width, at least for non-latin1
875 // do wcwidth before further replacements, as wcwidth says that line-drawing
876 // characters have width -1 (DOH!) on GNU/Linux sometimes.
877 int width = c < 0x100 ? 1 : wcwidth (c);
879 if (charsets[screen.charset] == '0') // DEC SPECIAL
881 // vt100 special graphics and line drawing
882 static uint16_t vt100_0[32] = { // 5f .. 7e
883 0x0020, 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
884 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
885 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534,
886 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7,
889 if (c >= 0x5f && c <= 0x7e)
891 c = vt100_0[c - 0x5f];
898 #if !UNICODE_3 && ENABLE_COMBINING
899 // trim characters we can't store directly :(
901 c = rxvt_composite.compose (c); // map to lower 16 bits
903 bool bold = (Options & Opt_realBold) && ((rstyle & RS_Bold) != 0);
904 rend_t rend = SET_FONT (rstyle,
905 c > 0x7f || bold || c < 0x20
906 ? TermWin.fontset->find_font (c, bold)
907 : TermWin.ascii_map [c - 0x20]);
911 stp[screen.cur.col] = c;
912 srp[screen.cur.col] = rend;
914 if (screen.cur.col < last_col - 1)
918 screen.tlen[row] = last_col;
919 if (screen.flags & Screen_Autowrap)
920 screen.flags |= Screen_WrapNext;
928 // pad with spaces when overwriting wide character with smaller one
929 for (int c = screen.cur.col; c < last_col && stp[c] == NOCHAR; c++)
938 // handle combining characters
939 // we just tag the accent on the previous on-screen character.
940 // this is arguably not correct, but also arguably not wrong.
941 // we don't handle double-width characters nicely yet.
946 if (screen.cur.col > 0)
948 tp = stp + screen.cur.col - 1;
949 rp = srp + screen.cur.col - 1;
951 else if (screen.cur.row > 0
952 && screen.tlen [screen.cur.row - 1 + TermWin.saveLines] == -1)
954 tp = screen.text[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
955 rp = screen.rend[screen.cur.row - 1 + TermWin.saveLines] + last_col - 1;
960 // handle double-width-chars by making them look extremely ugly
962 *tp = ' '; // hack //D //TODO //--tp, --rp;
964 // first try to find a precomposed character
965 unicode_t n = rxvt_compose (*tp, c);
967 n = rxvt_composite.compose (*tp, c);
970 *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
975 if (screen.tlen[row] != -1) /* XXX: think about this */
976 MAX_IT (screen.tlen[row], screen.cur.col);
979 * If we wrote anywhere in the selected area, kill the selection
980 * XXX: should we kill the mark too? Possibly, but maybe that
981 * should be a similar check.
987 assert (screen.cur.row >= 0);
988 #else /* drive with your eyes closed */
989 MAX_IT (screen.cur.row, 0);
993 /* ------------------------------------------------------------------------- */
995 * Process Backspace. Move back the cursor back a position, wrap if have to
999 rxvt_term::scr_backspace ()
1003 if (screen.cur.col == 0)
1005 if (screen.cur.row > 0)
1007 #ifdef TERMCAP_HAS_BW
1008 screen.cur.col = TermWin.ncol - 1;
1015 else if ((screen.flags & Screen_WrapNext) == 0)
1016 scr_gotorc (0, -1, RELATIVE);
1018 screen.flags &= ~Screen_WrapNext;
1021 /* ------------------------------------------------------------------------- */
1023 * Process Horizontal Tab
1024 * count: +ve = forward; -ve = backwards
1028 rxvt_term::scr_tab (int count)
1032 D_SCREEN ((stderr, "rxvt_scr_tab (%d)", count));
1034 i = x = screen.cur.col;
1040 for (; ++i < TermWin.ncol; )
1049 x = TermWin.ncol - 1;
1051 else /* if (count < 0) */
1065 if (x != screen.cur.col)
1066 scr_gotorc (0, x, R_RELATIVE);
1069 /* ------------------------------------------------------------------------- */
1071 * Process DEC Back Index
1073 * Move cursor left in row. If we're at the left boundary, shift everything
1074 * in that row right. Clear left column.
1078 rxvt_term::scr_backindex ()
1080 if (screen.cur.col > 0)
1081 scr_gotorc (0, -1, R_RELATIVE | C_RELATIVE);
1084 if (screen.tlen[screen.cur.row + TermWin.saveLines] == 0)
1085 return; /* um, yeah? */
1086 scr_insdel_chars (1, INSERT);
1090 /* ------------------------------------------------------------------------- */
1092 * Process DEC Forward Index
1094 * Move cursor right in row. If we're at the right boundary, shift everything
1095 * in that row left. Clear right column.
1099 rxvt_term::scr_forwardindex ()
1103 if (screen.cur.col < TermWin.ncol - 1)
1104 scr_gotorc (0, 1, R_RELATIVE | C_RELATIVE);
1107 row = screen.cur.row + TermWin.saveLines;
1108 if (screen.tlen[row] == 0)
1109 return; /* um, yeah? */
1110 else if (screen.tlen[row] == -1)
1111 screen.tlen[row] = TermWin.ncol;
1112 scr_gotorc (0, 0, R_RELATIVE);
1113 scr_insdel_chars (1, DELETE);
1114 scr_gotorc (0, TermWin.ncol - 1, R_RELATIVE);
1119 /* ------------------------------------------------------------------------- */
1124 rxvt_term::scr_gotorc (int row, int col, int relative)
1129 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));
1131 screen.cur.col = relative & C_RELATIVE ? screen.cur.col + col : col;
1132 MAX_IT (screen.cur.col, 0);
1133 MIN_IT (screen.cur.col, (int32_t)TermWin.ncol - 1);
1135 screen.flags &= ~Screen_WrapNext;
1136 if (relative & R_RELATIVE)
1140 if (screen.cur.row <= screen.bscroll
1141 && (screen.cur.row + row) > screen.bscroll)
1142 screen.cur.row = screen.bscroll;
1144 screen.cur.row += row;
1148 if (screen.cur.row >= screen.tscroll
1149 && (screen.cur.row + row) < screen.tscroll)
1150 screen.cur.row = screen.tscroll;
1152 screen.cur.row += row;
1157 if (screen.flags & Screen_Relative)
1158 { /* relative origin mode */
1159 screen.cur.row = row + screen.tscroll;
1160 MIN_IT (screen.cur.row, screen.bscroll);
1163 screen.cur.row = row;
1165 MAX_IT (screen.cur.row, 0);
1166 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1168 while (screen.cur.col > 0
1169 && screen.text[screen.cur.row + TermWin.saveLines][screen.cur.col] == NOCHAR)
1173 /* ------------------------------------------------------------------------- */
1175 * direction should be UP or DN
1178 rxvt_term::scr_index (enum page_dirn direction)
1183 dirn = ((direction == UP) ? 1 : -1);
1184 D_SCREEN ((stderr, "rxvt_scr_index (%d)", dirn));
1188 screen.flags &= ~Screen_WrapNext;
1189 if ((screen.cur.row == screen.bscroll && direction == UP)
1190 || (screen.cur.row == screen.tscroll && direction == DN))
1191 scr_scroll_text (screen.tscroll, screen.bscroll, dirn, 0);
1193 screen.cur.row += dirn;
1194 MAX_IT (screen.cur.row, 0);
1195 MIN_IT (screen.cur.row, (int32_t)TermWin.nrow - 1);
1196 selection_check (0);
1199 /* ------------------------------------------------------------------------- */
1201 * Erase part or whole of a line
1202 * XTERM_SEQ: Clear line to right: ESC [ 0 K
1203 * XTERM_SEQ: Clear line to left : ESC [ 1 K
1204 * XTERM_SEQ: Clear whole line : ESC [ 2 K
1207 rxvt_term::scr_erase_line (int mode)
1209 unsigned int row, col, num;
1212 D_SCREEN ((stderr, "rxvt_scr_erase_line (%d) at screen row: %d", mode, screen.cur.row));
1215 selection_check (1);
1217 screen.flags &= ~Screen_WrapNext;
1219 row = TermWin.saveLines + screen.cur.row;
1222 case 0: /* erase to end of line */
1223 col = screen.cur.col;
1224 num = TermWin.ncol - col;
1225 MIN_IT (screen.tlen[row], (int16_t)col);
1226 if (ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur)
1227 || ROWCOL_IN_ROW_AT_OR_AFTER (selection.end, screen.cur))
1230 case 1: /* erase to beginning of line */
1232 num = screen.cur.col + 1;
1233 if (ROWCOL_IN_ROW_AT_OR_BEFORE (selection.beg, screen.cur)
1234 || ROWCOL_IN_ROW_AT_OR_BEFORE (selection.end, screen.cur))
1237 case 2: /* erase whole line */
1240 screen.tlen[row] = 0;
1241 if (selection.beg.row <= screen.cur.row
1242 && selection.end.row >= screen.cur.row)
1249 if (screen.text[row])
1250 scr_blank_line (&screen.text[row][col], &screen.rend[row][col], num, rstyle);
1252 scr_blank_screen_mem (screen.text, screen.rend, row, rstyle);
1255 /* ------------------------------------------------------------------------- */
1257 * Erase part of whole of the screen
1258 * XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
1259 * XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
1260 * XTERM_SEQ: Clear whole screen : ESC [ 2 J
1263 rxvt_term::scr_erase_screen (int mode)
1266 int32_t row, row_offset;
1271 D_SCREEN ((stderr, "rxvt_scr_erase_screen (%d) at screen row: %d", mode, screen.cur.row));
1273 row_offset = (int32_t)TermWin.saveLines;
1277 case 0: /* erase to end of screen */
1278 selection_check (1);
1280 row = screen.cur.row + 1; /* possible OOB */
1281 num = TermWin.nrow - row;
1283 case 1: /* erase to beginning of screen */
1284 selection_check (3);
1287 num = screen.cur.row;
1289 case 2: /* erase whole screen */
1290 selection_check (3);
1298 if (selection.op && current_screen == selection.screen
1299 && ((selection.beg.row >= row && selection.beg.row <= row + num)
1300 || (selection.end.row >= row
1301 && selection.end.row <= row + num)))
1304 if (row >= TermWin.nrow) /* Out Of Bounds */
1307 MIN_IT (num, (TermWin.nrow - row));
1309 if (rstyle & (RS_RVid | RS_Uline))
1310 ren = (rend_t) ~RS_None;
1311 else if (GET_BASEBG (rstyle) == Color_bg)
1313 ren = DEFAULT_RSTYLE;
1314 CLEAR_ROWS (row, num);
1318 ren = (rstyle & (RS_fgMask | RS_bgMask));
1319 gcvalue.foreground = PixColors[GET_BGCOLOR (rstyle)];
1320 XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1321 ERASE_ROWS (row, num);
1322 gcvalue.foreground = PixColors[Color_fg];
1323 XChangeGC (display->display, TermWin.gc, GCForeground, &gcvalue);
1326 for (; num--; row++)
1328 scr_blank_screen_mem (screen.text, screen.rend,
1329 (unsigned int) (row + row_offset), rstyle);
1330 screen.tlen[row + row_offset] = 0;
1331 scr_blank_line (drawn_text[row], drawn_rend[row],
1332 (unsigned int)TermWin.ncol, ren);
1336 /* ------------------------------------------------------------------------- */
1338 * Fill the screen with `E's
1339 * XTERM_SEQ: Screen Alignment Test: ESC # 8
1350 selection_check (3);
1352 fs = SET_FONT (rstyle, TermWin.fontset->find_font ('E'));
1353 for (k = TermWin.saveLines, i = TermWin.nrow; i--; k++)
1355 screen.tlen[k] = TermWin.ncol; /* make the `E's selectable */
1356 fill_text (screen.text[k], 'E', TermWin.ncol);
1357 for (r1 = screen.rend[k], j = TermWin.ncol; j--; )
1362 /* ------------------------------------------------------------------------- */
1364 * Insert/Delete <count> lines
1367 rxvt_term::scr_insdel_lines (int count, int insdel)
1373 selection_check (1);
1375 if (screen.cur.row > screen.bscroll)
1378 end = screen.bscroll - screen.cur.row + 1;
1381 if (insdel == DELETE)
1383 else if (insdel == INSERT)
1386 screen.flags &= ~Screen_WrapNext;
1388 scr_scroll_text (screen.cur.row, screen.bscroll, insdel * count, 0);
1391 /* ------------------------------------------------------------------------- */
1393 * Insert/Delete <count> characters from the current position
1396 rxvt_term::scr_insdel_chars (int count, int insdel)
1410 selection_check (1);
1411 MIN_IT (count, (TermWin.ncol - screen.cur.col));
1413 row = screen.cur.row + TermWin.saveLines;
1414 screen.flags &= ~Screen_WrapNext;
1416 stp = screen.text[row];
1417 srp = screen.rend[row];
1418 slp = & (screen.tlen[row]);
1423 for (col = TermWin.ncol - 1; (col - count) >= screen.cur.col;
1426 stp[col] = stp[col - count];
1427 srp[col] = srp[col - count];
1433 MIN_IT (*slp, TermWin.ncol);
1436 if (selection.op && current_screen == selection.screen
1437 && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1439 if (selection.end.row != screen.cur.row
1440 || (selection.end.col + count >= TermWin.ncol))
1443 { /* shift selection */
1444 selection.beg.col += count;
1445 selection.mark.col += count; /* XXX: yes? */
1446 selection.end.col += count;
1450 scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1451 (unsigned int)count, rstyle);
1455 screen.cur.col += count; /* don't worry if > TermWin.ncol */
1456 selection_check (1);
1457 screen.cur.col -= count;
1458 scr_blank_line (& (stp[screen.cur.col]), & (srp[screen.cur.col]),
1459 (unsigned int)count, rstyle);
1463 tr = srp[TermWin.ncol - 1] & (RS_fgMask | RS_bgMask | RS_baseattrMask);
1465 for (col = screen.cur.col; (col + count) < TermWin.ncol; col++)
1467 stp[col] = stp[col + count];
1468 srp[col] = srp[col + count];
1471 scr_blank_line (& (stp[TermWin.ncol - count]),
1472 & (srp[TermWin.ncol - count]),
1473 (unsigned int)count, tr);
1475 if (*slp == -1) /* break line continuation */
1476 *slp = TermWin.ncol;
1481 if (selection.op && current_screen == selection.screen
1482 && ROWCOL_IN_ROW_AT_OR_AFTER (selection.beg, screen.cur))
1484 if (selection.end.row != screen.cur.row
1485 || (screen.cur.col >= selection.beg.col - count)
1486 || selection.end.col >= TermWin.ncol)
1490 /* shift selection */
1491 selection.beg.col -= count;
1492 selection.mark.col -= count; /* XXX: yes? */
1493 selection.end.col -= count;
1501 /* ------------------------------------------------------------------------- */
1503 * Set the scrolling region
1504 * XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
1507 rxvt_term::scr_scroll_region (int top, int bot)
1510 MIN_IT (bot, (int)TermWin.nrow - 1);
1515 screen.tscroll = top;
1516 screen.bscroll = bot;
1517 scr_gotorc (0, 0, 0);
1520 /* ------------------------------------------------------------------------- */
1522 * Make the cursor visible/invisible
1523 * XTERM_SEQ: Make cursor visible : ESC [ ? 25 h
1524 * XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
1527 rxvt_term::scr_cursor_visible (int mode)
1532 screen.flags |= Screen_VisibleCursor;
1534 screen.flags &= ~Screen_VisibleCursor;
1537 /* ------------------------------------------------------------------------- */
1539 * Set/unset automatic wrapping
1540 * XTERM_SEQ: Set Wraparound : ESC [ ? 7 h
1541 * XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
1544 rxvt_term::scr_autowrap (int mode)
1547 screen.flags |= Screen_Autowrap;
1549 screen.flags &= ~ (Screen_Autowrap | Screen_WrapNext);
1552 /* ------------------------------------------------------------------------- */
1554 * Set/unset margin origin mode
1555 * Absolute mode: line numbers are counted relative to top margin of screen
1556 * and the cursor can be moved outside the scrolling region.
1557 * Relative mode: line numbers are relative to top margin of scrolling region
1558 * and the cursor cannot be moved outside.
1559 * XTERM_SEQ: Set Absolute: ESC [ ? 6 h
1560 * XTERM_SEQ: Set Relative: ESC [ ? 6 l
1563 rxvt_term::scr_relative_origin (int mode)
1566 screen.flags |= Screen_Relative;
1568 screen.flags &= ~Screen_Relative;
1569 scr_gotorc (0, 0, 0);
1572 /* ------------------------------------------------------------------------- */
1574 * Set insert/replace mode
1575 * XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
1576 * XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
1579 rxvt_term::scr_insert_mode (int mode)
1582 screen.flags |= Screen_Insert;
1584 screen.flags &= ~Screen_Insert;
1587 /* ------------------------------------------------------------------------- */
1590 * XTERM_SEQ: Set tab at current column : ESC H
1591 * XTERM_SEQ: Clear tab at current column: ESC [ 0 g
1592 * XTERM_SEQ: Clear all tabs : ESC [ 3 g
1595 rxvt_term::scr_set_tab (int mode)
1598 MEMSET (tabs, 0, TermWin.ncol * sizeof (char));
1599 else if (screen.cur.col < TermWin.ncol)
1600 tabs[screen.cur.col] = (mode ? 1 : 0);
1603 /* ------------------------------------------------------------------------- */
1605 * Set reverse/normal video
1606 * XTERM_SEQ: Reverse video: ESC [ ? 5 h
1607 * XTERM_SEQ: Normal video : ESC [ ? 5 l
1610 rxvt_term::scr_rvideo_mode (int mode)
1617 SWAP_IT (PixColors[Color_fg], PixColors[Color_bg], rxvt_color);
1618 #if defined(XPM_BACKGROUND)
1619 if (bgPixmap.pixmap == None)
1621 #if defined(TRANSPARENT)
1622 if (! (Options & Opt_transparent) || am_transparent == 0)
1624 XSetWindowBackground (display->display, TermWin.vt,
1625 PixColors[Color_bg]);
1627 gcvalue.foreground = PixColors[Color_fg];
1628 gcvalue.background = PixColors[Color_bg];
1629 XChangeGC (display->display, TermWin.gc, GCBackground | GCForeground,
1636 /* ------------------------------------------------------------------------- */
1638 * Report current cursor position
1639 * XTERM_SEQ: Report position: ESC [ 6 n
1642 rxvt_term::scr_report_position ()
1644 tt_printf ("\033[%d;%dR", screen.cur.row + 1, screen.cur.col + 1);
1647 /* ------------------------------------------------------------------------- *
1649 * ------------------------------------------------------------------------- */
1655 rxvt_term::set_font_style ()
1657 switch (charsets[screen.charset])
1659 case '0': /* DEC Special Character & Line Drawing Set */
1661 case 'A': /* United Kingdom (UK) */
1663 case 'B': /* United States (USASCII) */
1665 case '<': /* Multinational character set */
1667 case '5': /* Finnish character set */
1669 case 'C': /* Finnish character set */
1671 case 'K': /* German character set */
1676 /* ------------------------------------------------------------------------- */
1679 * XTERM_SEQ: Invoke G0 character set: CTRL-O
1680 * XTERM_SEQ: Invoke G1 character set: CTRL-N
1681 * XTERM_SEQ: Invoke G2 character set: ESC N
1682 * XTERM_SEQ: Invoke G3 character set: ESC O
1685 rxvt_term::scr_charset_choose (int set)
1687 screen.charset = set;
1691 /* ------------------------------------------------------------------------- */
1694 * XTERM_SEQ: Set G0 character set: ESC ( <C>
1695 * XTERM_SEQ: Set G1 character set: ESC ) <C>
1696 * XTERM_SEQ: Set G2 character set: ESC * <C>
1697 * XTERM_SEQ: Set G3 character set: ESC + <C>
1698 * See set_font_style for possible values for <C>
1701 rxvt_term::scr_charset_set (int set, unsigned int ch)
1703 charsets[set] = (unsigned char)ch;
1708 /* ------------------------------------------------------------------------- *
1709 * MAJOR SCREEN MANIPULATION *
1710 * ------------------------------------------------------------------------- */
1713 * refresh matching text.
1716 rxvt_term::scr_refresh_rend (rend_t mask, rend_t value)
1720 for (int i = 0; i < TermWin.nrow; i++)
1723 rend_t *drp = drawn_rend [i];
1725 for (; col < TermWin.ncol; col++, drp++)
1726 if ((*drp & mask) == value)
1746 rxvt_term::scr_expose (int x, int y, int width, int height, bool refresh)
1749 row_col_t rc[RC_COUNT];
1751 if (drawn_text == NULL) /* sanity check */
1754 #ifndef NO_SLOW_LINK_SUPPORT
1755 if (refresh_type == FAST_REFRESH && !display->is_local)
1758 height = TermWin.height;
1764 x = min (x, (int)TermWin.width);
1766 y = min (y, (int)TermWin.height);
1770 rc[PART_BEG].col = Pixel2Col (x);
1771 rc[PART_BEG].row = Pixel2Row (y);
1773 rc[PART_END].col = Pixel2Width (x + width + TermWin.fwidth - 1);
1774 rc[PART_END].row = Pixel2Row (y + height + TermWin.fheight - 1);
1777 for (i = PART_BEG; i < RC_COUNT; i++)
1779 MIN_IT (rc[i].col, TermWin.ncol - 1);
1780 MIN_IT (rc[i].row, TermWin.nrow - 1);
1783 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));
1785 for (i = rc[PART_BEG].row; i <= rc[PART_END].row; i++)
1786 fill_text (&drawn_text[i][rc[PART_BEG].col], 0, rc[PART_END].col - rc[PART_BEG].col + 1);
1789 scr_refresh (SLOW_REFRESH);
1792 /* ------------------------------------------------------------------------- */
1794 * Refresh the entire screen
1797 rxvt_term::scr_touch (bool refresh)
1799 scr_expose (0, 0, TermWin.width, TermWin.height, refresh);
1802 /* ------------------------------------------------------------------------- */
1804 * Move the display so that the line represented by scrollbar value Y is at
1805 * the top of the screen
1808 rxvt_term::scr_move_to (int y, int len)
1811 uint16_t oldviewstart;
1813 oldviewstart = TermWin.view_start;
1816 p = (TermWin.nrow + TermWin.nscrolled) * (len - y) / len;
1817 p -= (long) (TermWin.nrow - 1);
1820 TermWin.view_start = (uint16_t)min (p, TermWin.nscrolled);
1821 D_SCREEN ((stderr, "rxvt_scr_move_to (%d, %d) view_start:%d", y, len, TermWin.view_start));
1823 return scr_changeview (oldviewstart);
1826 /* ------------------------------------------------------------------------- */
1828 * Page the screen up/down nlines
1829 * direction should be UP or DN
1832 rxvt_term::scr_page (enum page_dirn direction, int nlines)
1835 uint16_t oldviewstart;
1837 D_SCREEN ((stderr, "rxvt_scr_page (%s, %d) view_start:%d", ((direction == UP) ? "UP" : "DN"), nlines, TermWin.view_start));
1839 assert ((nlines >= 0) && (nlines <= TermWin.nrow));
1841 oldviewstart = TermWin.view_start;
1842 if (direction == UP)
1844 n = TermWin.view_start + nlines;
1845 TermWin.view_start = min (n, TermWin.nscrolled);
1849 n = TermWin.view_start - nlines;
1850 TermWin.view_start = max (n, 0);
1852 return scr_changeview (oldviewstart);
1856 rxvt_term::scr_changeview (uint16_t oldviewstart)
1858 if (TermWin.view_start != oldviewstart)
1861 num_scr -= (TermWin.view_start - oldviewstart);
1864 return (int) (TermWin.view_start - oldviewstart);
1867 /* ------------------------------------------------------------------------- */
1869 rxvt_term::scr_bell ()
1872 # ifndef NO_MAPALERT
1873 # ifdef MAPALERT_OPTION
1874 if (Options & Opt_mapAlert)
1876 XMapWindow (display->display, TermWin.parent[0]);
1878 if (Options & Opt_visualBell)
1880 scr_rvideo_mode (!rvideo); /* refresh also done */
1881 scr_rvideo_mode (!rvideo); /* refresh also done */
1884 XBell (display->display, 0);
1888 /* ------------------------------------------------------------------------- */
1891 rxvt_term::scr_printscreen (int fullhist)
1894 int i, r1, nrows, row_offset;
1898 if ((fd = popen_printer ()) == NULL)
1900 nrows = TermWin.nrow;
1901 row_offset = TermWin.saveLines;
1903 row_offset -= TermWin.view_start;
1906 nrows += TermWin.nscrolled;
1907 row_offset -= TermWin.nscrolled;
1910 for (r1 = 0; r1 < nrows; r1++)
1912 t = screen.text[r1 + row_offset];
1913 for (i = TermWin.ncol - 1; i >= 0; i--)
1914 if (!isspace (t[i]))
1916 fprintf (fd, "%.*s\n", (i + 1), t);
1918 pclose_printer (fd);
1922 /* ------------------------------------------------------------------------- */
1924 * Refresh the screen
1925 * drawn_text/drawn_rend contain the screen information before the update.
1926 * screen.text/screen.rend contain what the screen will change to.
1929 #define FONT_WIDTH(X, Y) \
1930 (X)->per_char[ (Y) - (X)->min_char_or_byte2].width
1931 #define FONT_RBEAR(X, Y) \
1932 (X)->per_char[ (Y) - (X)->min_char_or_byte2].rbearing
1933 #define FONT_LBEAR(X, Y) \
1934 (X)->per_char[ (Y) - (X)->min_char_or_byte2].lbearing
1935 #define IS_FONT_CHAR(X, Y) \
1936 ((Y) >= (X)->min_char_or_byte2 && (Y) <= (X)->max_char_or_byte2)
1939 rxvt_term::scr_refresh (unsigned char refresh_type)
1941 unsigned char must_clear, /* use draw_string not draw_image_string */
1942 rvid, /* reverse video this position */
1943 showcursor; /* show the cursor */
1944 int16_t col, row, /* column/row we're processing */
1945 ocrow; /* old cursor row */
1947 row_offset; /* basic offset in screen structure */
1948 #ifndef NO_CURSORCOLOR
1949 rend_t cc1; /* store colours at cursor position (s) */
1951 rend_t *drp, *srp; /* drawn-rend-pointer, screen-rend-pointer */
1952 text_t *dtp, *stp; /* drawn-text-pointer, screen-text-pointer */
1954 if (refresh_type == NO_REFRESH || !TermWin.mapped)
1964 row_offset = TermWin.saveLines - TermWin.view_start;
1966 #ifdef XPM_BACKGROUND
1967 must_clear |= (bgPixmap.pixmap != None);
1970 must_clear |= ((Options & Opt_transparent) && am_transparent);
1972 ocrow = oldcursor.row; /* is there an old outline cursor on screen? */
1975 * B: reverse any characters which are selected
1977 scr_reverse_selection ();
1980 * C: set the cursor character (s)
1983 unsigned char setoldcursor;
1984 rend_t ccol1, /* Cursor colour */
1985 ccol2; /* Cursor colour2 */
1987 showcursor = (screen.flags & Screen_VisibleCursor);
1995 srp = &(screen.rend[screen.cur.row + TermWin.saveLines][screen.cur.col]);
1997 if (showcursor && TermWin.focus)
2000 #ifndef NO_CURSORCOLOR
2001 cc1 = *srp & (RS_fgMask | RS_bgMask);
2002 if (ISSET_PIXCOLOR (Color_cursor))
2003 ccol1 = Color_cursor;
2005 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2006 ccol1 = GET_FGCOLOR (rstyle);
2010 if (ISSET_PIXCOLOR (Color_cursor2))
2011 ccol2 = Color_cursor2;
2013 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
2014 ccol2 = GET_BGCOLOR (rstyle);
2018 *srp = SET_FGCOLOR (*srp, ccol1);
2019 *srp = SET_BGCOLOR (*srp, ccol2);
2024 /* make sure no outline cursor is left around */
2028 if (screen.cur.row + TermWin.view_start != ocrow
2029 || screen.cur.col != oldcursor.col)
2031 if (ocrow < TermWin.nrow
2032 && oldcursor.col < TermWin.ncol)
2033 drawn_rend[ocrow][oldcursor.col] ^= (RS_RVid | RS_Uline);
2035 if (TermWin.focus || !showcursor)
2041 else if (!TermWin.focus)
2046 if (screen.cur.row + TermWin.view_start >= TermWin.nrow)
2050 oldcursor.row = screen.cur.row + TermWin.view_start;
2051 oldcursor.col = screen.cur.col;
2056 #ifndef NO_SLOW_LINK_SUPPORT
2058 * D: CopyArea pass - very useful for slower links
2059 * This has been deliberately kept simple.
2062 if (!display->is_local
2063 && refresh_type == FAST_REFRESH && num_scr_allow && i
2064 && abs (i) < TermWin.nrow && !must_clear)
2074 row = i > 0 ? 0 : j - 1;
2075 for (; j-- >= 0; row += (i > 0 ? 1 : -1))
2077 if (row + i >= 0 && row + i < TermWin.nrow && row + i != ocrow)
2079 stp = screen.text[row + row_offset];
2080 srp = screen.rend[row + row_offset];
2081 dtp = drawn_text[row];
2082 dtp2 = drawn_text[row + i];
2083 drp = drawn_rend[row];
2084 drp2 = drawn_rend[row + i];
2086 for (nits = 0, col = TermWin.ncol; col--; )
2087 if (stp[col] != dtp2[col] || srp[col] != drp2[col])
2089 else if (stp[col] != dtp[col] || srp[col] != drp[col])
2092 if (nits > 8) /* XXX: arbitrary choice */
2094 for (col = TermWin.ncol; col--; )
2110 /* also comes here at end if needed because of >= above */
2112 SWAP_IT (wlen, len, int);
2114 D_SCREEN ((stderr, "rxvt_scr_refresh (): XCopyArea: %d -> %d (height: %d)", len + i, len, wlen - len + 1));
2115 XCopyArea (display->display, TermWin.vt, TermWin.vt,
2116 TermWin.gc, 0, Row2Pixel (len + i),
2117 (unsigned int)TermWin_TotalWidth (),
2118 (unsigned int)Height2Pixel (wlen - len + 1),
2119 0, Row2Pixel (len));
2127 * E: main pass across every character
2129 for (row = 0; row < TermWin.nrow; row++)
2131 stp = screen.text[row + row_offset];
2132 srp = screen.rend[row + row_offset];
2133 dtp = drawn_text[row];
2134 drp = drawn_rend[row];
2137 * E2: OK, now the real pass
2139 int ypixel = (int)Row2Pixel (row);
2141 for (col = 0; col < TermWin.ncol; col++)
2143 /* compare new text with old - if exactly the same then continue */
2144 if (stp[col] == dtp[col] /* Must match characters to skip. */
2145 && (srp[col] == drp[col] /* Either rendition the same or */
2146 || (stp[col] == ' ' /* space w/ no background change */
2147 && GET_BGATTR (srp[col]) == GET_BGATTR (drp[col]))))
2150 // redraw one or more characters
2152 // seek to the beginning if wide characters
2153 while (stp[col] == NOCHAR && col > 0)
2156 rend_t rend = srp[col]; /* screen rendition (target rendtion) */
2157 text_t *text = stp + col;
2160 dtp[col] = stp[col];
2163 int xpixel = Col2Pixel (col);
2165 // this loop looks very messy, it can probably be optimized
2166 // and cleaned a bit by you?
2167 for (i = 0; ++col < TermWin.ncol; )
2169 if (stp[col] == NOCHAR)
2171 dtp[col] = stp[col];
2175 if (i) // only possible skip if char unchanged
2181 if (rend != srp[col])
2186 if (stp[col] != dtp[col]
2187 || srp[col] != drp[col])
2189 if (must_clear && (i++ > (count / 2)))
2192 dtp[col] = stp[col];
2196 else if (must_clear || (stp[col] != ' ' && ++i >= 32))
2200 col--; /* went one too far. move back */
2201 count -= i; /* dump any matching trailing chars */
2204 * Determine the attributes for the string
2206 int fid = GET_FONT (rend);
2207 int fore = GET_FGCOLOR (rend); // desired foreground
2208 int back = GET_BGCOLOR (rend); // desired background
2210 rend = GET_ATTR (rend);
2212 rvid = !!(rend & RS_RVid);
2214 #ifndef NO_BOLD_UNDERLINE_REVERSE
2215 if (rend & RS_Bold && fore == Color_fg && !(Options & Opt_realBold))
2217 if (ISSET_PIXCOLOR (Color_BD))
2223 if (rend & RS_Uline)
2224 if (ISSET_PIXCOLOR (Color_UL))
2230 SWAP_IT (fore, back, int);
2232 #ifndef NO_BOLD_UNDERLINE_REVERSE
2233 if (ISSET_PIXCOLOR (Color_RV)
2234 # ifndef NO_CURSORCOLOR
2235 && !ISSET_PIXCOLOR (Color_cursor)
2243 if (rend & RS_Blink && (back == Color_bg || fore == Color_bg))
2245 if (!text_blink_ev.active)
2247 text_blink_ev.start (NOW + TEXT_BLINK_INTERVAL);
2250 else if (hidden_text)
2256 * Actually do the drawing of the string here
2258 rxvt_font *font = (*TermWin.fontset)[fid];
2261 font->clear_rect (*TermWin.drawable, xpixel, ypixel,
2262 TermWin.fwidth * count, TermWin.fheight,
2264 else if (back == Color_bg)
2268 CLEAR_CHARS (xpixel, ypixel, count);
2270 for (i = 0; i < count; i++) /* don't draw empty strings */
2273 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, -1);
2278 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, Color_bg);
2281 font->draw (*TermWin.drawable, xpixel, ypixel, text, count, fore, back);
2283 if (rend & RS_Uline && font->descent > 1 && fore != back)
2284 XDrawLine (display->display, drawBuffer, TermWin.gc,
2285 xpixel, ypixel + font->ascent + 1,
2286 xpixel + Width2Pixel (count) - 1, ypixel + font->ascent + 1);
2287 } /* for (col....) */
2288 } /* for (row....) */
2291 * G: cleanup cursor and display outline cursor if necessary
2297 srp = & (screen.rend[screen.cur.row + TermWin.saveLines]
2300 #ifndef NO_CURSORCOLOR
2301 *srp = (*srp & ~ (RS_fgMask | RS_bgMask)) | cc1;
2304 else if (oldcursor.row >= 0)
2306 #ifndef NO_CURSORCOLOR
2307 if (ISSET_PIXCOLOR (Color_cursor))
2308 XSetForeground (display->display, TermWin.gc, PixColors[Color_cursor]);
2310 int cursorwidth = 1;
2311 while (oldcursor.col + cursorwidth < TermWin.ncol
2312 && drawn_text[oldcursor.row][oldcursor.col + cursorwidth] == NOCHAR)
2315 XDrawRectangle (display->display, drawBuffer, TermWin.gc,
2316 Col2Pixel (oldcursor.col),
2317 Row2Pixel (oldcursor.row),
2318 (unsigned int) (Width2Pixel (cursorwidth) - 1),
2319 (unsigned int) (Height2Pixel (1) - TermWin.lineSpace - 1));
2324 * H: cleanup selection
2326 scr_reverse_selection ();
2328 if (refresh_type & SMOOTH_REFRESH)
2329 XFlush (display->display);
2333 want_refresh = 0; /* screen is current */
2337 rxvt_term::scr_remap_chars (text_t *tp, rend_t *rp)
2342 for (int i = TermWin.ncol; i; i--, rp++, tp++)
2343 *rp = SET_FONT (*rp, TermWin.fontset->find_font (*tp));
2347 rxvt_term::scr_remap_chars ()
2349 for (int i = TermWin.nrow + TermWin.saveLines; i--; )
2350 scr_remap_chars (screen.text[i], screen.rend[i]);
2352 for (int i = TermWin.nrow; i--; )
2354 scr_remap_chars (drawn_text[i], drawn_rend[i]);
2355 scr_remap_chars (swap.text[i], swap.rend[i]);
2359 /* ------------------------------------------------------------------------- */
2361 rxvt_term::scr_clear (bool really)
2363 if (!TermWin.mapped)
2370 if ((Options & Opt_transparent) && (am_pixmap_trans == 0))
2374 if (! (Options & Opt_transparent_all))
2377 i = (int) (sizeof (TermWin.parent) / sizeof (Window));
2380 if (TermWin.parent[i] != None)
2381 XClearWindow (display->display, TermWin.parent[i]);
2386 XClearWindow (display->display, TermWin.vt);
2389 /* ------------------------------------------------------------------------- */
2391 rxvt_term::scr_reverse_selection ()
2393 if (selection.op && current_screen == selection.screen)
2395 int end_row = TermWin.saveLines - TermWin.view_start;
2396 int i = selection.beg.row + TermWin.saveLines;
2397 int col, row = selection.end.row + TermWin.saveLines;
2401 col = selection.beg.col;
2408 end_row += TermWin.nrow;
2409 for (; i < row && i < end_row; i++, col = 0)
2410 for (srp = screen.rend[i]; col < TermWin.ncol; col++)
2411 srp[col] ^= RS_RVid;
2413 if (i == row && i < end_row)
2414 for (srp = screen.rend[i]; col < selection.end.col; col++)
2415 srp[col] ^= RS_RVid;
2419 /* ------------------------------------------------------------------------- */
2421 * Dump the whole scrollback and screen to the passed filedescriptor. The
2422 * invoking routine must close the fd.
2426 rxvt_term::scr_dump (int fd)
2429 unsigned int width, towrite;
2432 for (row = TermWin.saveLines - TermWin.nscrolled;
2433 row < TermWin.saveLines + TermWin.nrow - 1; row++)
2435 width = screen.tlen[row] >= 0 ? screen.tlen[row]
2437 for (towrite = width; towrite; towrite -= wrote)
2439 wrote = write (fd, & (screen.text[row][width - towrite]),
2442 return; /* XXX: death, no report */
2444 if (screen.tlen[row] >= 0)
2445 if (write (fd, r1, 1) <= 0)
2446 return; /* XXX: death, no report */
2451 /* ------------------------------------------------------------------------- *
2452 * CHARACTER SELECTION *
2453 * ------------------------------------------------------------------------- */
2456 * -TermWin.nscrolled <= (selection row) <= TermWin.nrow - 1
2459 rxvt_term::selection_check (int check_more)
2466 pos.row = pos.col = 0;
2467 if ((selection.beg.row < - (int32_t)TermWin.nscrolled)
2468 || (selection.beg.row >= TermWin.nrow)
2469 || (selection.mark.row < - (int32_t)TermWin.nscrolled)
2470 || (selection.mark.row >= TermWin.nrow)
2471 || (selection.end.row < - (int32_t)TermWin.nscrolled)
2472 || (selection.end.row >= TermWin.nrow)
2474 && current_screen == selection.screen
2475 && !ROWCOL_IS_BEFORE (screen.cur, selection.beg)
2476 && ROWCOL_IS_BEFORE (screen.cur, selection.end))
2478 && ROWCOL_IS_BEFORE (selection.beg, pos)
2479 && ROWCOL_IS_AFTER (selection.end, pos))
2481 && ROWCOL_IS_AFTER (selection.end, pos))
2482 || (check_more == 4 /* screen width change */
2483 && (selection.beg.row != selection.end.row
2484 || selection.end.col > TermWin.ncol)))
2488 /* ------------------------------------------------------------------------- */
2490 * Paste a selection direct to the command fd
2493 rxvt_term::paste (const unsigned char *data, unsigned int len)
2495 unsigned int i, j, n;
2496 unsigned char *ds = (unsigned char *)rxvt_malloc (PROP_SIZE);
2499 /* a paste should act like the user is typing, so check scrollTtyKeypress */
2500 ZERO_SCROLLBACK (r);
2503 /* convert normal newline chars into common keyboard Return key sequence */
2504 for (i = 0; i < len; i += PROP_SIZE)
2506 n = min (len - i, PROP_SIZE);
2507 MEMCPY (ds, data + i, n);
2509 for (j = 0; j < n; j++)
2513 tt_write (ds, (int)n);
2519 /* ------------------------------------------------------------------------- */
2521 * Respond to a notification that a primary selection has been sent
2522 * EXT: SelectionNotify
2525 rxvt_term::selection_paste (Window win, Atom prop, bool delete_prop)
2528 unsigned long bytes_after;
2531 D_SELECT ((stderr, "rxvt_selection_paste (%08lx, %lu, %d), wait=%2x", win, (unsigned long)prop, (int)delete_prop, selection_wait));
2533 if (prop == None) /* check for failed XConvertSelection */
2535 if ((selection_type & Sel_CompoundText))
2537 int selnum = selection_type & Sel_whereMask;
2540 if (selnum != Sel_direct)
2541 selection_request_other (XA_STRING, selnum);
2544 if ((selection_type & Sel_UTF8String))
2546 int selnum = selection_type & Sel_whereMask;
2548 selection_type = Sel_CompoundText;
2549 if (selnum != Sel_direct)
2550 selection_request_other (xa[XA_COMPOUND_TEXT], selnum);
2560 if (XGetWindowProperty (display->display, win, prop, (long) (nread / 4),
2561 (long) (PROP_SIZE / 4), delete_prop,
2562 AnyPropertyType, &ct.encoding, &ct.format,
2563 &ct.nitems, &bytes_after,
2564 &ct.value) != Success)
2567 if (ct.encoding == 0)
2569 D_SELECT ((stderr, "rxvt_selection_paste: property didn't exist!"));
2572 else if (ct.encoding == xa[XA_INCR])
2574 // INCR selection, start handshake
2575 XDeleteProperty (display->display, win, prop);
2576 selection_wait = Sel_incr;
2577 incr_ev.start (NOW + 10);
2581 if (ct.value == NULL)
2583 D_SELECT ((stderr, "rxvt_selection_paste: property shooting blanks!"));
2589 D_SELECT ((stderr, "rxvt_selection_paste: property empty - also INCR end"));
2591 if (selection_wait == Sel_normal && nread == 0
2592 && (win != display->root || prop != XA_CUT_BUFFER0)) // avoid recursion
2595 * pass through again trying CUT_BUFFER0 if we've come from
2596 * XConvertSelection () but nothing was presented
2598 D_SELECT ((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2599 selection_paste (display->root, XA_CUT_BUFFER0, False);
2602 nread = -1; /* discount any previous stuff */
2610 if (XmbTextPropertyToTextList (display->display, &ct, &cl, &cr) >= 0 && cl)
2612 for (int i = 0; i < cr; i++)
2613 paste ((unsigned char *)cl[i], STRLEN (cl[i]));
2615 XFreeStringList (cl);
2618 paste (ct.value, ct.nitems);
2620 if (bytes_after == 0)
2629 if (selection_wait == Sel_normal)
2630 selection_wait = Sel_none;
2632 D_SELECT ((stderr, "rxvt_selection_paste: bytes written: %ld", nread));
2637 rxvt_term::incr_cb (time_watcher &w)
2639 selection_wait = Sel_none;
2641 rxvt_warn ("data loss: timeout on INCR selection paste, ignoring.\n");
2645 * INCR support originally provided by Paul Sheer <psheer@obsidian.co.za>
2648 rxvt_term::selection_property (Window win, Atom prop)
2650 if (prop == None || selection_wait != Sel_incr)
2653 if (selection_paste (win, prop, 1) > 0)
2654 incr_ev.start (NOW + 10);
2657 selection_wait = Sel_none;
2662 /* ------------------------------------------------------------------------- */
2664 * Request the current selection:
2665 * Order: > internal selection if available
2666 * > PRIMARY, SECONDARY, CLIPBOARD if ownership is claimed (+)
2668 * (+) if ownership is claimed but property is empty, rxvt_selection_paste ()
2669 * will auto fallback to CUT_BUFFER0
2670 * EXT: button 2 release
2673 rxvt_term::selection_request (Time tm, int x, int y)
2675 D_SELECT ((stderr, "rxvt_selection_request (%lu, %d, %d)", tm, x, y));
2677 if (x < 0 || x >= TermWin.width || y < 0 || y >= TermWin.height)
2678 return; /* outside window */
2681 { /* internal selection */
2682 D_SELECT ((stderr, "rxvt_selection_request: pasting internal"));
2683 char *str = rxvt_wcstombs (selection.text, selection.len);
2684 paste ((unsigned char *)str, strlen (str));
2692 selection_request_time = tm;
2693 selection_wait = Sel_normal;
2695 for (i = Sel_Primary; i <= Sel_Clipboard; i++)
2697 #if X_HAVE_UTF8_STRING
2698 selection_type = Sel_UTF8String;
2699 if (selection_request_other (xa[XA_UTF8_STRING], i))
2702 selection_type = Sel_CompoundText;
2703 if (selection_request_other (xa[XA_COMPOUND_TEXT], i))
2710 selection_wait = Sel_none; /* don't loop in rxvt_selection_paste () */
2711 D_SELECT ((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2712 selection_paste (display->root, XA_CUT_BUFFER0, False);
2716 rxvt_term::selection_request_other (Atom target, int selnum)
2720 char *debug_xa_names[] = { "PRIMARY", "SECONDARY", "CLIPBOARD" };
2723 selection_type |= selnum;
2725 if (selnum == Sel_Primary)
2727 else if (selnum == Sel_Secondary)
2730 sel = xa[XA_CLIPBOARD];
2732 if (XGetSelectionOwner (display->display, sel) != None)
2734 D_SELECT ((stderr, "rxvt_selection_request_other: pasting %s", debug_xa_names[selnum]));
2735 XConvertSelection (display->display, sel, target, xa[XA_VT_SELECTION],
2736 TermWin.vt, selection_request_time);
2743 /* ------------------------------------------------------------------------- */
2745 * Clear all selected text
2746 * EXT: SelectionClear
2749 rxvt_term::selection_clear ()
2751 D_SELECT ((stderr, "rxvt_selection_clear ()"));
2754 free (selection.text);
2755 selection.text = NULL;
2759 if (display->selection_owner == this)
2760 display->selection_owner = 0;
2763 /* ------------------------------------------------------------------------- */
2765 * Copy a selection into the cut buffer
2766 * EXT: button 1 or 3 release
2769 rxvt_term::selection_make (Time tm)
2771 int i, col, end_col, row, end_row;
2772 wchar_t *new_selection_text;
2775 D_SELECT ((stderr, "rxvt_selection_make (): selection.op=%d, selection.clicks=%d", selection.op, selection.clicks));
2776 switch (selection.op)
2778 case SELECTION_CONT:
2780 case SELECTION_INIT:
2783 case SELECTION_BEGIN:
2784 selection.op = SELECTION_DONE;
2790 selection.op = SELECTION_DONE;
2792 if (selection.clicks == 4)
2793 return; /* nothing selected, go away */
2795 i = (selection.end.row - selection.beg.row + 1) * (TermWin.ncol + 1);
2796 new_selection_text = (wchar_t *)rxvt_malloc ((i + 4) * sizeof (wchar_t));
2798 col = selection.beg.col;
2800 row = selection.beg.row + TermWin.saveLines;
2801 end_row = selection.end.row + TermWin.saveLines;
2805 for (; row <= end_row; row++, col = 0)
2807 t = &(screen.text[row][col]);
2809 end_col = screen.tlen[row];
2812 end_col = TermWin.ncol;
2815 MIN_IT (end_col, selection.end.col);
2817 for (; col < end_col; col++)
2821 #if ENABLE_COMBINING
2822 else if (IS_COMPOSE (*t))
2824 int len = rxvt_composite.expand (*t, 0);
2832 new_selection_text = (wchar_t *)rxvt_realloc (new_selection_text, (i + 4) * sizeof (wchar_t));
2835 ofs += rxvt_composite.expand (*t++, new_selection_text + ofs);
2839 new_selection_text[ofs++] = *t++;
2842 if (screen.tlen[row] != -1 && row != end_row)
2843 new_selection_text[ofs++] = C0_LF;
2846 if (end_col != selection.end.col)
2847 new_selection_text[ofs++] = C0_LF;
2849 new_selection_text[ofs] = 0;
2853 free (new_selection_text);
2857 free (selection.text);
2859 // we usually allocate much more than necessary, so realloc it smaller again
2860 selection.len = ofs;
2861 selection.text = (wchar_t *)rxvt_realloc (new_selection_text, (ofs + 1) * sizeof (wchar_t));
2863 XSetSelectionOwner (display->display, XA_PRIMARY, TermWin.vt, tm);
2864 if (XGetSelectionOwner (display->display, XA_PRIMARY) == TermWin.vt)
2865 display->set_selection_owner (this);
2867 rxvt_warn ("can't get primary selection, ignoring.\n");
2872 if (XwcTextListToTextProperty (display->display, &selection.text, 1, XStringStyle, &ct) >= 0)
2874 XChangeProperty (display->display, display->root, XA_CUT_BUFFER0, XA_STRING, 8,
2875 PropModeReplace, ct.value, ct.nitems);
2880 selection_time = tm;
2881 D_SELECT ((stderr, "rxvt_selection_make (): selection.len=%d", selection.len));
2884 /* ------------------------------------------------------------------------- */
2886 * Mark or select text based upon number of clicks: 1, 2, or 3
2887 * EXT: button 1 press
2890 rxvt_term::selection_click (int clicks, int x, int y)
2892 D_SELECT ((stderr, "rxvt_selection_click (%d, %d, %d)", clicks, x, y));
2894 clicks = ((clicks - 1) % 3) + 1;
2895 selection.clicks = clicks; /* save clicks so extend will work */
2897 selection_start_colrow (Pixel2Col (x), Pixel2Row (y));
2899 if (clicks == 2 || clicks == 3)
2900 selection_extend_colrow (selection.mark.col,
2901 selection.mark.row + TermWin.view_start,
2903 1, /* button press */
2904 0); /* click change */
2907 /* ------------------------------------------------------------------------- */
2909 * Mark a selection at the specified col/row
2912 rxvt_term::selection_start_colrow (int col, int row)
2915 selection.mark.col = col;
2916 selection.mark.row = row - TermWin.view_start;
2918 MAX_IT (selection.mark.row, - (int32_t)TermWin.nscrolled);
2919 MIN_IT (selection.mark.row, (int32_t)TermWin.nrow - 1);
2920 MAX_IT (selection.mark.col, 0);
2921 MIN_IT (selection.mark.col, (int32_t)TermWin.ncol - 1);
2923 while (selection.mark.col > 0
2924 && screen.text[selection.mark.row + TermWin.saveLines][selection.mark.col] == NOCHAR)
2925 --selection.mark.col;
2928 { /* clear the old selection */
2929 selection.beg.row = selection.end.row = selection.mark.row;
2930 selection.beg.col = selection.end.col = selection.mark.col;
2933 selection.op = SELECTION_INIT;
2934 selection.screen = current_screen;
2937 /* ------------------------------------------------------------------------- */
2939 * Word select: select text for 2 clicks
2940 * We now only find out the boundary in one direction
2943 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
2944 #define DELIMIT_TEXT(x) \
2945 (unicode::is_space (x) ? 2 : (x) <= 0xff && !!STRCHR (rs[Rs_cutchars], (x)))
2946 #define DELIMIT_REND(x) 1
2949 rxvt_term::selection_delimit_word (enum page_dirn dirn, const row_col_t *mark, row_col_t *ret)
2951 int col, row, dirnadd, tcol, trow, w1, w2;
2958 bound.row = TermWin.saveLines - TermWin.nscrolled - 1;
2964 bound.row = TermWin.saveLines + TermWin.nrow;
2965 bound.col = TermWin.ncol - 1;
2969 row = mark->row + TermWin.saveLines;
2972 /* find the edge of a word */
2973 stp = & (screen.text[row][col]);
2974 w1 = DELIMIT_TEXT (*stp);
2976 srp = (&screen.rend[row][col]);
2977 w2 = DELIMIT_REND (*srp);
2981 for (; col != bound.col; col += dirnadd)
2989 if (DELIMIT_TEXT (*stp) != w1)
2991 if (DELIMIT_REND (*srp) != w2)
2995 if ((col == bound.col) && (row != bound.row))
2997 if (screen.tlen[ (row - (dirn == UP ? 1 : 0))] == -1)
2999 trow = row + dirnadd;
3000 tcol = dirn == UP ? TermWin.ncol - 1 : 0;
3002 if (screen.text[trow] == NULL)
3005 stp = & (screen.text[trow][tcol]);
3006 srp = & (screen.rend[trow][tcol]);
3008 if (DELIMIT_TEXT (*stp) != w1 || DELIMIT_REND (*srp) != w2)
3019 Old_Word_Selection_You_Die:
3020 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));
3023 col++; /* put us on one past the end */
3025 /* Poke the values back in */
3026 ret->row = row - TermWin.saveLines;
3030 /* ------------------------------------------------------------------------- */
3032 * Extend the selection to the specified x/y pixel location
3033 * EXT: button 3 press; button 1 or 3 drag
3034 * flag == 0 ==> button 1
3035 * flag == 1 ==> button 3 press
3036 * flag == 2 ==> button 3 motion
3039 rxvt_term::selection_extend (int x, int y, int flag)
3043 col = Pixel2Col (x);
3044 row = Pixel2Row (y);
3046 MIN_IT (row, (int)TermWin.nrow - 1);
3048 MIN_IT (col, (int)TermWin.ncol);
3051 * If we're selecting characters (single click) then we must check first
3052 * if we are at the same place as the original mark. If we are then
3053 * select nothing. Otherwise, if we're to the right of the mark, you have to
3054 * be _past_ a character for it to be selected.
3056 if (((selection.clicks % 3) == 1) && !flag
3057 && (col == selection.mark.col
3058 && (row == selection.mark.row + TermWin.view_start)))
3060 /* select nothing */
3061 selection.beg.row = selection.end.row = 0;
3062 selection.beg.col = selection.end.col = 0;
3063 selection.clicks = 4;
3065 D_SELECT ((stderr, "rxvt_selection_extend () selection.clicks = 4"));
3069 if (selection.clicks == 4)
3070 selection.clicks = 1;
3072 selection_extend_colrow (col, row, !!flag, /* ? button 3 */
3073 flag == 1 ? 1 : 0, /* ? button press */
3074 0); /* no click change */
3077 /* ------------------------------------------------------------------------- */
3079 * Extend the selection to the specified col/row
3082 rxvt_term::selection_extend_colrow (int32_t col, int32_t row, int button3, int buttonpress, int clickchange)
3084 int16_t ncol = TermWin.ncol;
3091 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));
3092 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));
3095 switch (selection.op)
3097 case SELECTION_INIT:
3099 selection.op = SELECTION_BEGIN;
3101 case SELECTION_BEGIN:
3102 if (row != selection.mark.row || col != selection.mark.col
3103 || (!button3 && buttonpress))
3104 selection.op = SELECTION_CONT;
3106 case SELECTION_DONE:
3107 selection.op = SELECTION_CONT;
3109 case SELECTION_CONT:
3111 case SELECTION_CLEAR:
3112 selection_start_colrow (col, row);
3117 if (selection.beg.col == selection.end.col
3118 && selection.beg.col != selection.mark.col
3119 && selection.beg.row == selection.end.row
3120 && selection.beg.row != selection.mark.row)
3122 selection.beg.col = selection.end.col = selection.mark.col;
3123 selection.beg.row = selection.end.row = selection.mark.row;
3124 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));
3130 pos.row -= TermWin.view_start; /* adjust for scroll */
3133 * This is mainly xterm style selection with a couple of differences, mainly
3134 * in the way button3 drag extension works.
3135 * We're either doing: button1 drag; button3 press; or button3 drag
3136 * a) button1 drag : select around a midpoint/word/line - that point/word/line
3137 * is always at the left/right edge of the selection.
3138 * b) button3 press: extend/contract character/word/line at whichever edge of
3139 * the selection we are closest to.
3140 * c) button3 drag : extend/contract character/word/line - we select around
3141 * a point/word/line which is either the start or end of the selection
3142 * and it was decided by whichever point/word/line was `fixed' at the
3143 * time of the most recent button3 press
3145 if (button3 && buttonpress)
3146 { /* button3 press */
3148 * first determine which edge of the selection we are closest to
3150 if (ROWCOL_IS_BEFORE (pos, selection.beg)
3151 || (!ROWCOL_IS_AFTER (pos, selection.end)
3152 && (((pos.col - selection.beg.col)
3153 + ((pos.row - selection.beg.row) * ncol))
3154 < ((selection.end.col - pos.col)
3155 + ((selection.end.row - pos.row) * ncol)))))
3158 if (closeto == LEFT)
3160 selection.beg.row = pos.row;
3161 selection.beg.col = pos.col;
3162 selection.mark.row = selection.end.row;
3163 selection.mark.col = selection.end.col - (selection.clicks == 2);
3167 selection.end.row = pos.row;
3168 selection.end.col = pos.col;
3169 selection.mark.row = selection.beg.row;
3170 selection.mark.col = selection.beg.col;
3174 { /* button1 drag or button3 drag */
3175 if (ROWCOL_IS_AFTER (selection.mark, pos))
3177 if ((selection.mark.row == selection.end.row)
3178 && (selection.mark.col == selection.end.col)
3179 && clickchange && selection.clicks == 2)
3180 selection.mark.col--;
3182 selection.beg.row = pos.row;
3183 selection.beg.col = pos.col;
3184 selection.end.row = selection.mark.row;
3185 selection.end.col = selection.mark.col + (selection.clicks == 2);
3189 selection.beg.row = selection.mark.row;
3190 selection.beg.col = selection.mark.col;
3191 selection.end.row = pos.row;
3192 selection.end.col = pos.col;
3196 if (selection.clicks == 1)
3198 end_col = screen.tlen[selection.beg.row + TermWin.saveLines];
3200 if (end_col != -1 && selection.beg.col > end_col)
3203 selection.beg.col = ncol;
3205 if (selection.beg.row != selection.end.row)
3206 selection.beg.col = ncol;
3208 selection.beg.col = selection.mark.col;
3212 end_col = screen.tlen[selection.end.row + TermWin.saveLines];
3214 if (end_col != -1 && selection.end.col > end_col)
3215 selection.end.col = ncol;
3217 else if (selection.clicks == 2)
3219 if (ROWCOL_IS_AFTER (selection.end, selection.beg))
3220 selection.end.col--;
3222 selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3223 selection_delimit_word (DN, & (selection.end), & (selection.end));
3225 else if (selection.clicks == 3)
3228 if ((Options & Opt_tripleclickwords))
3232 selection_delimit_word (UP, & (selection.beg), & (selection.beg));
3233 end_row = screen.tlen[selection.mark.row + TermWin.saveLines];
3235 for (end_row = selection.mark.row; end_row < TermWin.nrow; end_row++)
3237 end_col = screen.tlen[end_row + TermWin.saveLines];
3241 selection.end.row = end_row;
3242 selection.end.col = end_col;
3243 selection_remove_trailing_spaces ();
3251 if (ROWCOL_IS_AFTER (selection.mark, selection.beg))
3252 selection.mark.col++;
3253 selection.beg.col = 0;
3254 selection.end.col = ncol;
3258 if (button3 && buttonpress)
3259 { /* mark may need to be changed */
3260 if (closeto == LEFT)
3262 selection.mark.row = selection.end.row;
3263 selection.mark.col = selection.end.col
3264 - (selection.clicks == 2);
3268 selection.mark.row = selection.beg.row;
3269 selection.mark.col = selection.beg.col;
3272 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));
3277 rxvt_term::selection_remove_trailing_spaces ()
3279 int32_t end_col, end_row;
3282 end_col = selection.end.col;
3283 end_row = selection.end.row;
3284 for ( ; end_row >= selection.beg.row; )
3286 stp = screen.text[end_row + TermWin.saveLines];
3287 while (--end_col >= 0)
3289 if (stp[end_col] != ' ' && stp[end_col] != '\t')
3293 || screen.tlen[end_row - 1 + TermWin.saveLines] != -1)
3295 selection.end.col = end_col + 1;
3296 selection.end.row = end_row;
3300 end_col = TermWin.ncol;
3302 if (selection.mark.row > selection.end.row)
3304 selection.mark.row = selection.end.row;
3305 selection.mark.col = selection.end.col;
3307 else if (selection.mark.row == selection.end.row
3308 && selection.mark.col > selection.end.col)
3309 selection.mark.col = selection.end.col;
3313 /* ------------------------------------------------------------------------- */
3315 * Double click on button 3 when already selected
3316 * EXT: button 3 double click
3319 rxvt_term::selection_rotate (int x, int y)
3321 selection.clicks = selection.clicks % 3 + 1;
3322 selection_extend_colrow (Pixel2Col (x), Pixel2Row (y), 1, 0, 1);
3325 /* ------------------------------------------------------------------------- */
3327 * On some systems, the Atom typedef is 64 bits wide. We need to have a type
3328 * that is exactly 32 bits wide, because a format of 64 is not allowed by
3331 typedef CARD32 Atom32;
3333 /* ------------------------------------------------------------------------- */
3335 * Respond to a request for our current selection
3336 * EXT: SelectionRequest
3339 rxvt_term::selection_send (const XSelectionRequestEvent &rq)
3343 XICCEncodingStyle style;
3346 ev.type = SelectionNotify;
3348 ev.display = rq.display;
3349 ev.requestor = rq.requestor;
3350 ev.selection = rq.selection;
3351 ev.target = rq.target;
3354 if (rq.target == xa[XA_TARGETS])
3356 Atom32 target_list[6];
3357 Atom32 *target = target_list;
3359 *target++ = (Atom32) xa[XA_TARGETS];
3360 *target++ = (Atom32) xa[XA_TIMESTAMP];
3361 *target++ = (Atom32) XA_STRING;
3362 *target++ = (Atom32) xa[XA_TEXT];
3363 *target++ = (Atom32) xa[XA_COMPOUND_TEXT];
3364 #if X_HAVE_UTF8_STRING
3365 *target++ = (Atom32) xa[XA_UTF8_STRING];
3368 XChangeProperty (display->display, rq.requestor, rq.property, XA_ATOM,
3369 (8 * sizeof (target_list[0])), PropModeReplace,
3370 (unsigned char *)target_list,
3371 target - target_list);
3372 ev.property = rq.property;
3375 else if (rq.target == xa[XA_MULTIPLE])
3377 /* TODO: Handle MULTIPLE */
3380 else if (rq.target == xa[XA_TIMESTAMP] && selection.text)
3382 XChangeProperty (display->display, rq.requestor, rq.property, XA_INTEGER,
3383 (8 * sizeof (Time)), PropModeReplace,
3384 (unsigned char *)&selection_time, 1);
3385 ev.property = rq.property;
3387 else if (rq.target == XA_STRING
3388 || rq.target == xa[XA_TEXT]
3389 || rq.target == xa[XA_COMPOUND_TEXT]
3390 || rq.target == xa[XA_UTF8_STRING]
3399 if (target == XA_STRING)
3400 // we actually don't do XA_STRING, but who cares, as i18n clients
3401 // will ask for another format anyways.
3402 style = XStringStyle;
3403 else if (target == xa[XA_TEXT])
3404 style = XStdICCTextStyle;
3405 else if (target == xa[XA_COMPOUND_TEXT])
3406 style = XCompoundTextStyle;
3407 #if X_HAVE_UTF8_STRING
3408 else if (target == xa[XA_UTF8_STRING])
3409 style = XUTF8StringStyle;
3413 target = xa[XA_COMPOUND_TEXT];
3414 style = XCompoundTextStyle;
3419 cl = selection.text;
3420 selectlen = selection.len;
3428 // Xwc doesn't handle iso-10646 in wchar_t gracefully, so maybe recode it
3429 // manually for XUTF8StringStyle.
3430 if (XwcTextListToTextProperty (display->display, &cl, 1, style, &ct) >= 0)
3434 /* if we failed to convert then send it raw */
3435 ct.value = (unsigned char *)cl;
3436 ct.nitems = selectlen;
3439 XChangeProperty (display->display, rq.requestor, rq.property,
3440 target, 8, PropModeReplace,
3441 ct.value, (int)ct.nitems);
3442 ev.property = rq.property;
3448 XSendEvent (display->display, rq.requestor, False, 0L, (XEvent *)&ev);
3451 /* ------------------------------------------------------------------------- *
3453 * ------------------------------------------------------------------------- */
3456 * return col/row values corresponding to x/y pixel values
3459 rxvt_term::pixel_position (int *x, int *y)
3461 *x = Pixel2Col (*x);
3462 /* MAX_IT (*x, 0); MIN_IT (*x, (int)TermWin.ncol - 1); */
3463 *y = Pixel2Row (*y);
3464 /* MAX_IT (*y, 0); MIN_IT (*y, (int)TermWin.nrow - 1); */
3467 /* ------------------------------------------------------------------------- */
3470 rxvt_term::im_set_position (XPoint *pos)
3472 XWindowAttributes xwa;
3474 XGetWindowAttributes (display->display, TermWin.vt, &xwa);
3475 pos->x = Col2Pixel (screen.cur.col) + xwa.x;
3476 pos->y = Height2Pixel ((screen.cur.row + 1)) + xwa.y - TermWin.lineSpace;
3480 /* ------------------------------------------------------------------------- */