*** empty log message ***
[dana/urxvt.git] / src / screen.C
1 /*--------------------------------*-C-*--------------------------------------*
2  * File:        screen.c
3  *---------------------------------------------------------------------------*
4  * $Id: screen.C,v 1.4 2003-11-25 15:25:17 pcg Exp $
5  *
6  * Copyright (c) 1997-2001 Geoff Wing <gcw@pobox.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *--------------------------------------------------------------------------*/
22 /*
23  * We handle _all_ screen updates and selections
24  */
25
26 #include "../config.h"          /* NECESSARY */
27 #define INTERN_SCREEN
28 #include "rxvt.h"               /* NECESSARY */
29 #include "screen.intpro"        /* PROTOS for internal routines */
30
31 #include <X11/Xmd.h>            /* get the typedef for CARD32 */
32
33 #include <stdint.h>
34 #include <wchar.h>
35
36 inline void fill_text (text_t *start, text_t value, int len)
37 {
38   while (len--)
39     *start++ = value;
40 }
41
42 #define RESET_CHSTAT(H)                         \
43     if ((H)->chstat == WBYTE)                   \
44         (H)->chstat = SBYTE, (H)->lost_multi = 1
45
46 /* ------------------------------------------------------------------------- */
47 #define PROP_SIZE               16384
48 #define TABSIZE                 8       /* default tab size */
49
50 /* ------------------------------------------------------------------------- *
51  *             GENERAL SCREEN AND SELECTION UPDATE ROUTINES                  *
52  * ------------------------------------------------------------------------- */
53 #define ZERO_SCROLLBACK(R)                                              \
54     if (((R)->Options & Opt_scrollTtyOutput) == Opt_scrollTtyOutput)    \
55         (R)->TermWin.view_start = 0
56 #define CLEAR_SELECTION(R)                                              \
57     (R)->selection.beg.row = (R)->selection.beg.col                     \
58         = (R)->selection.end.row = (R)->selection.end.col = 0
59 #define CLEAR_ALL_SELECTION(R)                                          \
60     (R)->selection.beg.row = (R)->selection.beg.col                     \
61         = (R)->selection.mark.row = (R)->selection.mark.col             \
62         = (R)->selection.end.row = (R)->selection.end.col = 0
63
64 #define ROW_AND_COL_IS_AFTER(A, B, C, D)                                \
65     (((A) > (C)) || (((A) == (C)) && ((B) > (D))))
66 #define ROW_AND_COL_IS_BEFORE(A, B, C, D)                               \
67     (((A) < (C)) || (((A) == (C)) && ((B) < (D))))
68 #define ROW_AND_COL_IN_ROW_AFTER(A, B, C, D)                            \
69     (((A) == (C)) && ((B) > (D)))
70 #define ROW_AND_COL_IN_ROW_AT_OR_AFTER(A, B, C, D)                      \
71     (((A) == (C)) && ((B) >= (D)))
72 #define ROW_AND_COL_IN_ROW_BEFORE(A, B, C, D)                           \
73     (((A) == (C)) && ((B) < (D)))
74 #define ROW_AND_COL_IN_ROW_AT_OR_BEFORE(A, B, C, D)                     \
75     (((A) == (C)) && ((B) <= (D)))
76
77 /* these must be row_col_t */
78 #define ROWCOL_IS_AFTER(X, Y)                                           \
79     ROW_AND_COL_IS_AFTER((X).row, (X).col, (Y).row, (Y).col)
80 #define ROWCOL_IS_BEFORE(X, Y)                                          \
81     ROW_AND_COL_IS_BEFORE((X).row, (X).col, (Y).row, (Y).col)
82 #define ROWCOL_IN_ROW_AFTER(X, Y)                                       \
83     ROW_AND_COL_IN_ROW_AFTER((X).row, (X).col, (Y).row, (Y).col)
84 #define ROWCOL_IN_ROW_BEFORE(X, Y)                                      \
85     ROW_AND_COL_IN_ROW_BEFORE((X).row, (X).col, (Y).row, (Y).col)
86 #define ROWCOL_IN_ROW_AT_OR_AFTER(X, Y)                                 \
87     ROW_AND_COL_IN_ROW_AT_OR_AFTER((X).row, (X).col, (Y).row, (Y).col)
88 #define ROWCOL_IN_ROW_AT_OR_BEFORE(X, Y)                                \
89     ROW_AND_COL_IN_ROW_AT_OR_BEFORE((X).row, (X).col, (Y).row, (Y).col)
90
91 /*
92  * CLEAR_ROWS : clear <num> rows starting from row <row>
93  * CLEAR_CHARS: clear <num> chars starting from pixel position <x,y>
94  * ERASE_ROWS : set <num> rows starting from row <row> to the foreground colour
95  */
96 #define drawBuffer      (R->TermWin.vt)
97
98 #define CLEAR_ROWS(row, num)                                            \
99     if (R->TermWin.mapped)                                              \
100         XClearArea(R->Xdisplay, drawBuffer, R->TermWin.int_bwidth,      \
101                    Row2Pixel(row), (unsigned int)R->TermWin.width,      \
102                    (unsigned int)Height2Pixel(num), False)
103
104 #define CLEAR_CHARS(x, y, num)                                          \
105     if (R->TermWin.mapped)                                              \
106         XClearArea(R->Xdisplay, drawBuffer, x, y,                       \
107                    (unsigned int)Width2Pixel(num),                      \
108                    (unsigned int)Height2Pixel(1), False)
109
110 #define ERASE_ROWS(row, num)                                            \
111     XFillRectangle(R->Xdisplay, drawBuffer, R->TermWin.gc,              \
112                    R->TermWin.int_bwidth, Row2Pixel(row),               \
113                    (unsigned int)R->TermWin.width,                      \
114                    (unsigned int)Height2Pixel(num))
115
116 /* ------------------------------------------------------------------------- *
117  *                        SCREEN `COMMON' ROUTINES                           *
118  * ------------------------------------------------------------------------- */
119 /* Fill part/all of a line with blanks. */
120 /* INTPROTO */
121 void
122 rxvt_blank_line(pR_ text_t *et, rend_t *er, unsigned int width, rend_t efs)
123 {
124     efs &= ~RS_baseattrMask;
125     efs = SET_FONT (efs, R->TermWin.fontset->find_font (' '));
126
127     while (width--)
128       {
129         *et++ = ' ';
130         *er++ = efs;
131       }
132 }
133
134 /* ------------------------------------------------------------------------- */
135 /* Fill a full line with blanks - make sure it is allocated first */
136 /* INTPROTO */
137 void
138 rxvt_blank_screen_mem(pR_ text_t **tp, rend_t **rp, unsigned int row, rend_t efs)
139 {
140     int             width = R->TermWin.ncol;
141     rend_t         *er;
142
143 #ifdef DEBUG_STRICT
144     assert((tp[row] && rp[row]) || (tp[row] == NULL && rp[row] == NULL));
145 #endif
146     if (tp[row] == NULL) {
147         tp[row] = (text_t *)rxvt_malloc(sizeof(text_t) * width);
148         rp[row] = (rend_t *)rxvt_malloc(sizeof(rend_t) * width);
149     }
150     rxvt_blank_line (aR_ tp[row], rp[row], width, efs);
151 }
152
153 /* ------------------------------------------------------------------------- *
154  *                          SCREEN INITIALISATION                            *
155  * ------------------------------------------------------------------------- */
156 /* EXTPROTO */
157 void
158 rxvt_scr_reset(pR)
159 {
160     unsigned int    ncol, nrow, prev_ncol, prev_nrow,
161                     total_rows, prev_total_rows;
162     unsigned int    p, q;
163     int             k;
164     rend_t          setrstyle;
165
166     D_SCREEN((stderr, "rxvt_scr_reset()"));
167
168     R->TermWin.view_start = 0;
169     RESET_CHSTAT(R);
170     R->num_scr = 0;
171
172     prev_ncol = R->prev_ncol;
173     prev_nrow = R->prev_nrow;
174     if (R->TermWin.ncol == 0)
175         R->TermWin.ncol = 80;
176     if (R->TermWin.nrow == 0)
177         R->TermWin.nrow = 24;
178     ncol = R->TermWin.ncol;
179     nrow = R->TermWin.nrow;
180     if (ncol == prev_ncol && nrow == prev_nrow)
181         return;
182
183     R->want_refresh = 1;
184
185     total_rows = nrow + R->TermWin.saveLines;
186     prev_total_rows = prev_nrow + R->TermWin.saveLines;
187
188     R->screen.tscroll = 0;
189     R->screen.bscroll = nrow - 1;
190
191     if (prev_nrow == 0) {
192 /*
193  * A: first time called so just malloc everything : don't rely on realloc
194  *    Note: this is still needed so that all the scrollback lines are NULL
195  */
196         R->screen.text = (text_t **)rxvt_calloc(total_rows, sizeof(text_t *));
197         R->buf_text = (text_t **)rxvt_calloc(total_rows, sizeof(text_t *));
198         R->drawn_text = (text_t **)rxvt_calloc(nrow, sizeof(text_t *));
199         R->swap.text = (text_t **)rxvt_calloc(nrow, sizeof(text_t *));
200
201         R->screen.tlen = (int16_t *)rxvt_calloc(total_rows, sizeof(int16_t));
202         R->swap.tlen = (int16_t *)rxvt_calloc(nrow, sizeof(int16_t));
203
204         R->screen.rend = (rend_t **)rxvt_calloc(total_rows, sizeof(rend_t *));
205         R->buf_rend = (rend_t **)rxvt_calloc(total_rows, sizeof(rend_t *));
206         R->drawn_rend = (rend_t **)rxvt_calloc(nrow, sizeof(rend_t *));
207         R->swap.rend = (rend_t **)rxvt_calloc(nrow, sizeof(rend_t *));
208
209         for (p = 0; p < nrow; p++) {
210             q = p + R->TermWin.saveLines;
211             rxvt_blank_screen_mem(aR_ R->screen.text, R->screen.rend,
212                                   q, DEFAULT_RSTYLE);
213             rxvt_blank_screen_mem(aR_ R->swap.text, R->swap.rend,
214                                   p, DEFAULT_RSTYLE);
215             R->screen.tlen[q] = R->swap.tlen[p] = 0;
216             rxvt_blank_screen_mem(aR_ R->drawn_text, R->drawn_rend,
217                                   p, DEFAULT_RSTYLE);
218         }
219         MEMSET(R->charsets, 'B', sizeof(R->charsets));
220         R->TermWin.nscrolled = 0;       /* no saved lines */
221         R->rstyle = DEFAULT_RSTYLE;
222         R->screen.flags = Screen_DefaultFlags;
223         R->screen.cur.row = R->screen.cur.col = 0;
224         R->screen.charset = 0;
225         R->current_screen = PRIMARY;
226         rxvt_scr_cursor(aR_ SAVE);
227 #if NSCREENS
228         R->swap.flags = Screen_DefaultFlags;
229         R->swap.cur.row = R->swap.cur.col = 0;
230         R->swap.charset = 0;
231         R->current_screen = SECONDARY;
232         rxvt_scr_cursor(aR_ SAVE);
233         R->current_screen = PRIMARY;
234 #endif
235         R->selection.text = NULL;
236         R->selection.len = 0;
237         R->selection.op = SELECTION_CLEAR;
238         R->selection.screen = PRIMARY;
239         R->selection.clicks = 0;
240         CLEAR_ALL_SELECTION(R);
241         R->rvideo = 0;
242
243     } else {
244 /*
245  * B1: add or delete rows as appropriate
246  */
247         setrstyle = DEFAULT_RSTYLE;
248
249         if (nrow < prev_nrow) {
250             /* delete rows */
251             k = min(R->TermWin.nscrolled, prev_nrow - nrow);
252             rxvt_scroll_text(aR_ 0, (int)prev_nrow - 1, k, 1);
253             for (p = nrow; p < prev_nrow; p++) {
254                 q = p + R->TermWin.saveLines;
255                 if (R->screen.text[q]) {
256 #ifdef DEBUG_STRICT
257                     assert(R->screen.rend[q]);
258 #endif
259                     free(R->screen.text[q]);
260                     free(R->screen.rend[q]);
261                 }
262                 if (R->swap.text[p]) {
263 #ifdef DEBUG_STRICT
264                     assert(R->swap.rend[p]);
265 #endif
266                     free(R->swap.text[p]);
267                     free(R->swap.rend[p]);
268                 }
269 #ifdef DEBUG_STRICT
270                 assert(R->drawn_text[p] && R->drawn_rend[p]);
271 #endif
272                 free(R->drawn_text[p]);
273                 free(R->drawn_rend[p]);
274             }
275             /* we have fewer rows so fix up cursor position */
276             MIN_IT(R->screen.cur.row, (int32_t)nrow - 1);
277             MIN_IT(R->swap.cur.row, (int32_t)nrow - 1);
278
279             rxvt_scr_reset_realloc(aR); /* realloc _last_ */
280
281         } else if (nrow > prev_nrow) {
282             /* add rows */
283             rxvt_scr_reset_realloc(aR); /* realloc _first_ */
284
285             k = min(R->TermWin.nscrolled, nrow - prev_nrow);
286             for (p = prev_total_rows; p < total_rows; p++) {
287                 R->screen.tlen[p] = 0;
288                 R->screen.text[p] = NULL;
289                 R->screen.rend[p] = NULL;
290             }
291             for (p = prev_total_rows; p < total_rows - k; p++)
292                 rxvt_blank_screen_mem(aR_ R->screen.text, R->screen.rend,
293                                       p, setrstyle);
294             for (p = prev_nrow; p < nrow; p++) {
295                 R->swap.tlen[p] = 0;
296                 R->swap.text[p] = NULL;
297                 R->swap.rend[p] = NULL;
298                 R->drawn_text[p] = NULL;
299                 R->drawn_rend[p] = NULL;
300                 rxvt_blank_screen_mem(aR_ R->swap.text, R->swap.rend,
301                                       p, setrstyle);
302                 rxvt_blank_screen_mem(aR_ R->drawn_text, R->drawn_rend,
303                                       p, setrstyle);
304             }
305             if (k > 0) {
306                 rxvt_scroll_text(aR_ 0, (int)nrow - 1, -k, 1);
307                 R->screen.cur.row += k;
308                 R->screen.s_cur.row += k;
309                 R->TermWin.nscrolled -= k;
310             }
311 #ifdef DEBUG_STRICT
312             assert(R->screen.cur.row < R->TermWin.nrow);
313             assert(R->swap.cur.row < R->TermWin.nrow);
314 #else                           /* drive with your eyes closed */
315             MIN_IT(R->screen.cur.row, nrow - 1);
316             MIN_IT(R->swap.cur.row, nrow - 1);
317 #endif
318         }
319 /* B2: resize columns */
320         if (ncol != prev_ncol) {
321             for (p = 0; p < total_rows; p++) {
322                 if (R->screen.text[p]) {
323                     R->screen.text[p] = (text_t *)rxvt_realloc(R->screen.text[p], ncol * sizeof(text_t));
324                     R->screen.rend[p] = (rend_t *)rxvt_realloc(R->screen.rend[p], ncol * sizeof(rend_t));
325                     MIN_IT(R->screen.tlen[p], (int16_t)ncol);
326                     if (ncol > prev_ncol)
327                         rxvt_blank_line(aR_
328                                         &(R->screen.text[p][prev_ncol]),
329                                         &(R->screen.rend[p][prev_ncol]),
330                                         ncol - prev_ncol,
331                                         setrstyle);
332                 }
333             }
334             for (p = 0; p < nrow; p++) {
335                 R->drawn_text[p] = (text_t *)rxvt_realloc(R->drawn_text[p], ncol * sizeof(text_t));
336                 R->drawn_rend[p] = (rend_t *)rxvt_realloc(R->drawn_rend[p], ncol * sizeof(rend_t));
337                 if (R->swap.text[p]) {
338                     R->swap.text[p] = (text_t *)rxvt_realloc(R->swap.text[p], ncol * sizeof(text_t));
339                     R->swap.rend[p] = (rend_t *)rxvt_realloc(R->swap.rend[p], ncol * sizeof(rend_t));
340                     MIN_IT(R->swap.tlen[p], (int16_t)ncol);
341                     if (ncol > prev_ncol)
342                         rxvt_blank_line(aR_
343                                         &(R->swap.text[p][prev_ncol]),
344                                         &(R->swap.rend[p][prev_ncol]),
345                                         ncol - prev_ncol, setrstyle);
346                 }
347                 if (ncol > prev_ncol)
348                     rxvt_blank_line(aR_
349                                     &(R->drawn_text[p][prev_ncol]),
350                                     &(R->drawn_rend[p][prev_ncol]),
351                                     ncol - prev_ncol, setrstyle);
352             }
353             MIN_IT(R->screen.cur.col, (int16_t)ncol - 1);
354             MIN_IT(R->swap.cur.col, (int16_t)ncol - 1);
355         }
356         if (R->tabs)
357             free(R->tabs);
358     }
359
360     R->tabs = (char *)rxvt_malloc(ncol * sizeof(char));
361
362     for (p = 0; p < ncol; p++)
363         R->tabs[p] = (p % TABSIZE == 0) ? 1 : 0;
364
365     R->prev_nrow = nrow;
366     R->prev_ncol = ncol;
367
368     rxvt_tt_winsize(R->cmd_fd, R->TermWin.ncol, R->TermWin.nrow, R->cmd_pid);
369 }
370
371 /* INTPROTO */
372 void
373 rxvt_scr_reset_realloc(pR)
374 {
375     uint16_t       total_rows, nrow;
376
377     nrow = R->TermWin.nrow;
378     total_rows = nrow + R->TermWin.saveLines;
379 /* *INDENT-OFF* */
380     R->screen.text = (text_t **)rxvt_realloc(R->screen.text, total_rows * sizeof(text_t *));
381     R->buf_text    = (text_t **)rxvt_realloc(R->buf_text   , total_rows * sizeof(text_t *));
382     R->drawn_text  = (text_t **)rxvt_realloc(R->drawn_text , nrow       * sizeof(text_t *));
383     R->swap.text   = (text_t **)rxvt_realloc(R->swap.text  , nrow       * sizeof(text_t *));
384
385     R->screen.tlen = (int16_t *)rxvt_realloc(R->screen.tlen, total_rows * sizeof(int16_t));
386     R->swap.tlen   = (int16_t *)rxvt_realloc(R->swap.tlen  , total_rows * sizeof(int16_t));
387
388     R->screen.rend = (rend_t **)rxvt_realloc(R->screen.rend, total_rows * sizeof(rend_t *));
389     R->buf_rend    = (rend_t **)rxvt_realloc(R->buf_rend   , total_rows * sizeof(rend_t *));
390     R->drawn_rend  = (rend_t **)rxvt_realloc(R->drawn_rend , nrow       * sizeof(rend_t *));
391     R->swap.rend   = (rend_t **)rxvt_realloc(R->swap.rend  , nrow       * sizeof(rend_t *));
392 /* *INDENT-ON* */
393 }
394
395 /* ------------------------------------------------------------------------- */
396 /*
397  * Free everything.  That way malloc debugging can find leakage.
398  */
399 /* EXTPROTO */
400 void
401 rxvt_scr_release(pR)
402 {
403     uint16_t       total_rows;
404     int             i;
405
406     total_rows = R->TermWin.nrow + R->TermWin.saveLines;
407     for (i = 0; i < total_rows; i++) {
408         if (R->screen.text[i]) {        /* then so is R->screen.rend[i] */
409             free(R->screen.text[i]);
410 #ifdef DEBUG_STRICT
411             assert(R->screen.rend[i]);
412 #endif
413             free(R->screen.rend[i]);
414         }
415     }
416     for (i = 0; i < R->TermWin.nrow; i++) {
417         free(R->drawn_text[i]);
418         free(R->drawn_rend[i]);
419         free(R->swap.text[i]);
420         free(R->swap.rend[i]);
421     }
422     free(R->screen.text);
423     free(R->screen.tlen);
424     free(R->screen.rend);
425     free(R->drawn_text);
426     free(R->drawn_rend);
427     free(R->swap.text);
428     free(R->swap.tlen);
429     free(R->swap.rend);
430     free(R->buf_text);
431     free(R->buf_rend);
432     free(R->tabs);
433
434 /* NULL these so if anything tries to use them, we'll know about it */
435     R->screen.text = R->drawn_text = R->swap.text = NULL;
436     R->screen.rend = R->drawn_rend = R->swap.rend = NULL;
437     R->screen.tlen = R->swap.tlen = NULL;
438     R->buf_text = NULL;
439     R->buf_rend = NULL;
440     R->tabs = NULL;
441 }
442
443 /* ------------------------------------------------------------------------- */
444 /*
445  * Hard reset
446  */
447 /* EXTPROTO */
448 void
449 rxvt_scr_poweron(pR)
450 {
451     D_SCREEN((stderr, "rxvt_scr_poweron()"));
452
453     rxvt_scr_release(aR);
454     R->prev_nrow = R->prev_ncol = 0;
455     rxvt_scr_reset(aR);
456
457     rxvt_scr_clear(aR);
458     rxvt_scr_refresh(aR_ SLOW_REFRESH);
459 #ifdef RXVT_GRAPHICS
460     rxvt_Gr_reset(aR);
461 #endif
462 }
463
464 /* ------------------------------------------------------------------------- *
465  *                         PROCESS SCREEN COMMANDS                           *
466  * ------------------------------------------------------------------------- */
467 /*
468  * Save and Restore cursor
469  * XTERM_SEQ: Save cursor   : ESC 7
470  * XTERM_SEQ: Restore cursor: ESC 8
471  */
472 /* EXTPROTO */
473 void
474 rxvt_scr_cursor(pR_ int mode)
475 {
476     screen_t       *s;
477
478     D_SCREEN((stderr, "rxvt_scr_cursor(%c)", mode));
479
480 #if NSCREENS && !defined(NO_SECONDARY_SCREEN_CURSOR)
481     if (R->current_screen == SECONDARY)
482         s = &(R->swap);
483     else
484 #endif
485         s = &(R->screen);
486     switch (mode) {
487     case SAVE:
488         s->s_cur.row = s->cur.row;
489         s->s_cur.col = s->cur.col;
490         s->s_rstyle = R->rstyle;
491         s->s_charset = s->charset;
492         s->s_charset_char = R->charsets[s->charset];
493         break;
494     case RESTORE:
495         R->want_refresh = 1;
496         s->cur.row = s->s_cur.row;
497         s->cur.col = s->s_cur.col;
498         s->flags &= ~Screen_WrapNext;
499         R->rstyle = s->s_rstyle;
500         s->charset = s->s_charset;
501         R->charsets[s->charset] = s->s_charset_char;
502         rxvt_set_font_style(aR);
503         break;
504     }
505 /* boundary check in case screen size changed between SAVE and RESTORE */
506     MIN_IT(s->cur.row, R->TermWin.nrow - 1);
507     MIN_IT(s->cur.col, R->TermWin.ncol - 1);
508 #ifdef DEBUG_STRICT
509     assert(s->cur.row >= 0);
510     assert(s->cur.col >= 0);
511 #else                           /* drive with your eyes closed */
512     MAX_IT(s->cur.row, 0);
513     MAX_IT(s->cur.col, 0);
514 #endif
515 }
516
517 /* ------------------------------------------------------------------------- */
518 /*
519  * Swap between primary and secondary screens
520  * XTERM_SEQ: Primary screen  : ESC [ ? 4 7 h
521  * XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
522  */
523 /* EXTPROTO */
524 int
525 rxvt_scr_change_screen(pR_ int scrn)
526 {
527     int             i;
528 #if NSCREENS
529     int             offset;
530 #endif
531
532     R->want_refresh = 1;
533
534     D_SCREEN((stderr, "rxvt_scr_change_screen(%d)", scrn));
535
536     R->TermWin.view_start = 0;
537     RESET_CHSTAT(R);
538
539     if (R->current_screen == scrn)
540         return R->current_screen;
541
542     rxvt_selection_check(aR_ 2);        /* check for boundary cross */
543
544     SWAP_IT(R->current_screen, scrn, int);
545 #if NSCREENS
546     R->num_scr = 0;
547     offset = R->TermWin.saveLines;
548     for (i = R->prev_nrow; i--;) {
549         SWAP_IT(R->screen.text[i + offset], R->swap.text[i], text_t *);
550         SWAP_IT(R->screen.tlen[i + offset], R->swap.tlen[i], int16_t);
551         SWAP_IT(R->screen.rend[i + offset], R->swap.rend[i], rend_t *);
552     }
553     SWAP_IT(R->screen.cur.row, R->swap.cur.row, int16_t);
554     SWAP_IT(R->screen.cur.col, R->swap.cur.col, int16_t);
555 # ifdef DEBUG_STRICT
556     assert((R->screen.cur.row >= 0) && (R->screen.cur.row < R->prev_nrow));
557     assert((R->screen.cur.col >= 0) && (R->screen.cur.col < R->prev_ncol));
558 # else                          /* drive with your eyes closed */
559     MAX_IT(R->screen.cur.row, 0);
560     MIN_IT(R->screen.cur.row, (int32_t)R->prev_nrow - 1);
561     MAX_IT(R->screen.cur.col, 0);
562     MIN_IT(R->screen.cur.col, (int32_t)R->prev_ncol - 1);
563 # endif
564     SWAP_IT(R->screen.charset, R->swap.charset, int16_t);
565     SWAP_IT(R->screen.flags, R->swap.flags, int);
566     R->screen.flags |= Screen_VisibleCursor;
567     R->swap.flags |= Screen_VisibleCursor;
568
569 # ifdef RXVT_GRAPHICS
570
571     if (rxvt_Gr_Displayed(aR)) {
572         rxvt_Gr_scroll(aR_ 0);
573         rxvt_Gr_ChangeScreen(aR);
574     }
575 # endif
576 #else
577 # ifdef SCROLL_ON_NO_SECONDARY
578 #  ifdef RXVT_GRAPHICS
579     if (rxvt_Gr_Displayed(aR))
580         rxvt_Gr_ClearScreen(aR);
581 #  endif
582     if (R->current_screen == PRIMARY
583 #  ifdef RXVT_GRAPHICS
584         && !rxvt_Gr_Displayed(aR)
585 #  endif
586         )
587         rxvt_scroll_text(aR_ 0, (R->prev_nrow - 1), R->prev_nrow, 0);
588 # endif
589 #endif
590     return scrn;
591 }
592
593 /* ------------------------------------------------------------------------- */
594 /*
595  * Change the colour for following text
596  */
597 /* EXTPROTO */
598 void
599 rxvt_scr_color(pR_ unsigned int color, int fgbg)
600 {
601     color &= RS_fgMask;
602     if (fgbg == Color_fg)
603         R->rstyle = SET_FGCOLOR(R->rstyle, color);
604     else 
605         R->rstyle = SET_BGCOLOR(R->rstyle, color);
606 }
607
608 /* ------------------------------------------------------------------------- */
609 /*
610  * Change the rendition style for following text
611  */
612 /* EXTPROTO */
613 void
614 rxvt_scr_rendition(pR_ int set, int style)
615 {
616     if (set)
617         R->rstyle |= style;
618     else if (style == ~RS_None)
619         R->rstyle = DEFAULT_RSTYLE;
620     else
621         R->rstyle &= ~style;
622 }
623
624 /* ------------------------------------------------------------------------- */
625 /*
626  * Scroll text between <row1> and <row2> inclusive, by <count> lines
627  * count positive ==> scroll up
628  * count negative ==> scroll down
629  * spec == 0 for normal routines
630  */
631 /* EXTPROTO */
632 int
633 rxvt_scroll_text(pR_ int row1, int row2, int count, int spec)
634 {
635     int             i, j;
636     long            nscrolled;
637
638     if (count == 0 || (row1 > row2))
639         return 0;
640
641     R->want_refresh = 1;
642     D_SCREEN((stderr, "rxvt_scroll_text(%d,%d,%d,%d): %s", row1, row2, count, spec, (R->current_screen == PRIMARY) ? "Primary" : "Secondary"));
643
644     if ((count > 0) && (row1 == 0) && (R->current_screen == PRIMARY)) {
645         nscrolled = (long)R->TermWin.nscrolled + (long)count;;
646         if (nscrolled > (long)R->TermWin.saveLines)
647             R->TermWin.nscrolled = R->TermWin.saveLines;
648         else
649             R->TermWin.nscrolled = (uint16_t)nscrolled;
650         if ((R->Options & Opt_scrollWithBuffer)
651             && R->TermWin.view_start != 0
652             && R->TermWin.view_start != R->TermWin.saveLines)
653             rxvt_scr_page(aR_ UP, count);
654     } else if (!spec)
655         row1 += R->TermWin.saveLines;
656     row2 += R->TermWin.saveLines;
657
658     if (R->selection.op && R->current_screen == R->selection.screen) {
659         i = R->selection.beg.row + R->TermWin.saveLines;
660         j = R->selection.end.row + R->TermWin.saveLines;
661         if ((i < row1 && j > row1)
662             || (i < row2 && j > row2)
663             || (i - count < row1 && i >= row1)
664             || (i - count > row2 && i <= row2)
665             || (j - count < row1 && j >= row1)
666             || (j - count > row2 && j <= row2)) {
667             CLEAR_ALL_SELECTION(R);
668             R->selection.op = SELECTION_CLEAR;  /* XXX: too aggressive? */
669         } else if (j >= row1 && j <= row2) {
670             /* move selected region too */
671             R->selection.beg.row -= count;
672             R->selection.end.row -= count;
673             R->selection.mark.row -= count;
674         }
675     }
676     rxvt_selection_check(aR_ 0);        /* _after_ R->TermWin.nscrolled update */
677
678     R->num_scr += count;
679     j = count;
680     if (count < 0)
681         count = -count;
682     i = row2 - row1 + 1;
683     MIN_IT(count, i);
684
685     if (j > 0) {
686 /* A: scroll up */
687
688 /* A1: Copy lines that will get clobbered by the rotation */
689         for (i = 0, j = row1; i < count; i++, j++) {
690             R->buf_text[i] = R->screen.text[j];
691             R->buf_rend[i] = R->screen.rend[j];
692         }
693 /* A2: Rotate lines */
694         for (j = row1, i = j + count; i <= row2; i++, j++) {
695             R->screen.tlen[j] = R->screen.tlen[i];
696             R->screen.text[j] = R->screen.text[i];
697             R->screen.rend[j] = R->screen.rend[i];
698         }
699         j = row2 - count + 1, i = count;
700     } else /* if (j < 0) */ {
701 /* B: scroll down */
702
703 /* B1: Copy lines that will get clobbered by the rotation */
704         for (i = 0, j = row2; i < count; i++, j--) {
705             R->buf_text[i] = R->screen.text[j];
706             R->buf_rend[i] = R->screen.rend[j];
707         }
708 /* B2: Rotate lines */
709         for (j = row2, i = j - count; i >= row1; i--, j--) {
710             R->screen.tlen[j] = R->screen.tlen[i];
711             R->screen.text[j] = R->screen.text[i];
712             R->screen.rend[j] = R->screen.rend[i];
713         }
714         j = row1, i = count;
715         count = -count;
716     }
717
718 /* C: Resurrect lines */
719     for (; i--; j++) {
720         R->screen.tlen[j] = 0;
721         R->screen.text[j] = R->buf_text[i];
722         R->screen.rend[j] = R->buf_rend[i];
723         if (!spec)              /* line length may not equal TermWin.ncol */
724             rxvt_blank_screen_mem(aR_ R->screen.text, R->screen.rend,
725                                   (unsigned int)j, R->rstyle);
726     }
727
728 #ifdef RXVT_GRAPHICS
729     if (rxvt_Gr_Displayed(aR))
730         rxvt_Gr_scroll(aR_ count);
731 #endif
732     return count;
733 }
734
735 /* ------------------------------------------------------------------------- */
736 /*
737  * Add text given in <str> of length <len> to screen struct
738  */
739 /* EXTPROTO */
740 void
741 rxvt_scr_add_lines(pR_ const uint32_t *str, int nlines, int len)
742 {
743     unsigned char   checksel, clearsel;
744     uint32_t        c;
745     int             i, row, last_col;
746     text_t         *stp;
747     rend_t         *srp;
748
749     if (len <= 0)               /* sanity */
750         return;
751
752     R->want_refresh = 1;
753     last_col = R->TermWin.ncol;
754
755     D_SCREEN((stderr, "rxvt_scr_add_lines(%d,%d)", nlines, len));
756     ZERO_SCROLLBACK(R);
757     if (nlines > 0) {
758         nlines += (R->screen.cur.row - R->screen.bscroll);
759         if ((nlines > 0)
760             && (R->screen.tscroll == 0)
761             && (R->screen.bscroll == (R->TermWin.nrow - 1))) {
762             /* _at least_ this many lines need to be scrolled */
763             rxvt_scroll_text(aR_ R->screen.tscroll, R->screen.bscroll, nlines,
764                              0);
765             R->screen.cur.row -= nlines;
766         }
767     }
768 #ifdef DEBUG_STRICT
769     assert(R->screen.cur.col < last_col);
770     assert((R->screen.cur.row < R->TermWin.nrow)
771            && (R->screen.cur.row >= -(int32_t)R->TermWin.nscrolled));
772 #else                           /* drive with your eyes closed */
773     MIN_IT(R->screen.cur.col, last_col - 1);
774     MIN_IT(R->screen.cur.row, (int32_t)R->TermWin.nrow - 1);
775     MAX_IT(R->screen.cur.row, -(int32_t)R->TermWin.nscrolled);
776 #endif
777     row = R->screen.cur.row + R->TermWin.saveLines;
778
779     checksel = (R->selection.op
780                 && R->current_screen == R->selection.screen) ? 1 : 0;
781     clearsel = 0;
782
783     stp = R->screen.text[row];
784     srp = R->screen.rend[row];
785
786     for (i = 0; i < len;) {
787         c = str[i++];
788         switch (c) {
789         case '\t':
790             rxvt_scr_tab(aR_ 1);
791             continue;
792         case '\n':
793             if (R->screen.tlen[row] != -1)      /* XXX: think about this */
794                 MAX_IT(R->screen.tlen[row], R->screen.cur.col);
795             R->screen.flags &= ~Screen_WrapNext;
796             if (R->screen.cur.row == R->screen.bscroll)
797                 rxvt_scroll_text(aR_ R->screen.tscroll, R->screen.bscroll, 1, 0);
798             else if (R->screen.cur.row < (R->TermWin.nrow - 1))
799                 row = (++R->screen.cur.row) + R->TermWin.saveLines;
800             stp = R->screen.text[row];  /* _must_ refresh */
801             srp = R->screen.rend[row];  /* _must_ refresh */
802             RESET_CHSTAT(R);
803             continue;
804         case '\r':
805             if (R->screen.tlen[row] != -1)      /* XXX: think about this */
806                 MAX_IT(R->screen.tlen[row], R->screen.cur.col);
807             R->screen.flags &= ~Screen_WrapNext;
808             R->screen.cur.col = 0;
809             RESET_CHSTAT(R);
810             continue;
811         default:
812             if (c == 127)
813                 continue;       /* yummmm..... */
814             break;
815         }
816
817         if (checksel            /* see if we're writing within selection */
818             && !ROWCOL_IS_BEFORE(R->screen.cur, R->selection.beg)
819             && ROWCOL_IS_BEFORE(R->screen.cur, R->selection.end)) {
820             checksel = 0;
821             clearsel = 1;
822         }
823         if (R->screen.flags & Screen_WrapNext) {
824             R->screen.tlen[row] = -1;
825             if (R->screen.cur.row == R->screen.bscroll)
826                 rxvt_scroll_text(aR_ R->screen.tscroll, R->screen.bscroll, 1, 0);
827             else if (R->screen.cur.row < (R->TermWin.nrow - 1))
828                 row = (++R->screen.cur.row) + R->TermWin.saveLines;
829             stp = R->screen.text[row];  /* _must_ refresh */
830             srp = R->screen.rend[row];  /* _must_ refresh */
831             R->screen.cur.col = 0;
832             R->screen.flags &= ~Screen_WrapNext;
833         }
834         if (R->screen.flags & Screen_Insert)
835             rxvt_scr_insdel_chars(aR_ 1, INSERT);
836
837         if (R->charsets[R->screen.charset] == '0') // DEC SPECIAL
838           switch (c)
839             {
840               case '+': c = 0x2192; break; case ',': c = 0x2190; break; case '-': c = 0x2191; break;
841               case '.': c = 0x2193; break; case '0': c = 0x25ae; break; case '`': c = 0x25c6; break;
842               case 'a': c = 0x2592; break; case 'f': c = 0x00b0; break; case 'g': c = 0x00b1; break;
843               case 'h': c = 0x2592; break; case 'i': c = 0x2603; break; case 'j': c = 0x2518; break;
844               case 'k': c = 0x2510; break; case 'l': c = 0x250c; break; case 'm': c = 0x2514; break;
845               case 'n': c = 0x253c; break; case 'o': c = 0x23ba; break; case 'p': c = 0x23bb; break;
846               case 'q': c = 0x2500; break; case 'r': c = 0x23bc; break; case 's': c = 0x23bd; break;
847               case 't': c = 0x251c; break; case 'u': c = 0x2524; break; case 'v': c = 0x2534; break;
848               case 'w': c = 0x252c; break; case 'x': c = 0x2502; break; case 'y': c = 0x2264; break;
849               case 'z': c = 0x2265; break; case '{': c = 0x03c0; break; case '|': c = 0x2260; break;
850               case '}': c = 0x00a3; break; case '~': c = 0x00b7; break;
851             }
852
853         rend_t rend = SET_FONT (R->rstyle, R->TermWin.fontset->find_font (c));
854
855         stp[R->screen.cur.col] = c;
856         srp[R->screen.cur.col] = rend;
857
858         if (c > 255)
859           {
860             // rely on wcwidth to tell us the character width, at least for non iso-8859-1
861             int width = wcwidth (c);
862             
863             if (width > 1)
864               {
865                 while (--width > 0 && R->screen.cur.col < last_col - 1)
866                   {
867
868                     srp[R->screen.cur.col] |= RS_wide;
869
870                     R->screen.cur.col++;
871                     stp[R->screen.cur.col] = NOCHAR;
872                     srp[R->screen.cur.col] = rend;
873                   }
874               }
875           }
876
877         if (R->screen.cur.col < last_col - 1)
878             R->screen.cur.col++;
879         else {
880             R->screen.tlen[row] = last_col;
881             if (R->screen.flags & Screen_Autowrap)
882                 R->screen.flags |= Screen_WrapNext;
883         }
884     }
885     if (R->screen.tlen[row] != -1)      /* XXX: think about this */
886         MAX_IT(R->screen.tlen[row], R->screen.cur.col);
887
888 /*
889  * If we wrote anywhere in the selected area, kill the selection
890  * XXX: should we kill the mark too?  Possibly, but maybe that
891  *      should be a similar check.
892  */
893     if (clearsel)
894         CLEAR_SELECTION(R);
895
896 #ifdef DEBUG_STRICT
897     assert(R->screen.cur.row >= 0);
898 #else                           /* drive with your eyes closed */
899     MAX_IT(R->screen.cur.row, 0);
900 #endif
901 }
902
903 /* ------------------------------------------------------------------------- */
904 /*
905  * Process Backspace.  Move back the cursor back a position, wrap if have to
906  * XTERM_SEQ: CTRL-H
907  */
908 /* EXTPROTO */
909 void
910 rxvt_scr_backspace(pR)
911 {
912     RESET_CHSTAT(R);
913     R->want_refresh = 1;
914     if (R->screen.cur.col == 0) {
915         if (R->screen.cur.row > 0) {
916 #ifdef TERMCAP_HAS_BW
917             R->screen.cur.col = R->TermWin.ncol - 1;
918             R->screen.cur.row--;
919             return;
920 #endif
921         }
922     } else if ((R->screen.flags & Screen_WrapNext) == 0)
923         rxvt_scr_gotorc(aR_ 0, -1, RELATIVE);
924     R->screen.flags &= ~Screen_WrapNext;
925 }
926
927 /* ------------------------------------------------------------------------- */
928 /*
929  * Process Horizontal Tab
930  * count: +ve = forward; -ve = backwards
931  * XTERM_SEQ: CTRL-I
932  */
933 /* EXTPROTO */
934 void
935 rxvt_scr_tab(pR_ int count)
936 {
937     int             i, x;
938
939     D_SCREEN((stderr, "rxvt_scr_tab(%d)", count));
940     R->want_refresh = 1;
941     RESET_CHSTAT(R);
942     i = x = R->screen.cur.col;
943     if (count == 0)
944         return;
945     else if (count > 0) {
946         for (; ++i < R->TermWin.ncol; )
947             if (R->tabs[i]) {
948                 x = i;
949                 if (!--count)
950                     break;
951             }
952         if (count)
953             x = R->TermWin.ncol - 1;
954     } else /* if (count < 0) */ {
955         for (; --i >= 0; )
956             if (R->tabs[i]) {
957                 x = i;
958                 if (!++count)
959                     break;
960             }
961         if (count)
962             x = 0;
963     }
964     if (x != R->screen.cur.col)
965         rxvt_scr_gotorc(aR_ 0, x, R_RELATIVE);
966 }
967
968 /* ------------------------------------------------------------------------- */
969 /*
970  * Process DEC Back Index
971  * XTERM_SEQ: ESC 6
972  * Move cursor left in row.  If we're at the left boundary, shift everything
973  * in that row right.  Clear left column.
974  */
975 #ifndef NO_FRILLS
976 /* EXTPROTO */
977 void
978 rxvt_scr_backindex(pR)
979 {
980     if (R->screen.cur.col > 0)
981         rxvt_scr_gotorc(aR_ 0, -1, R_RELATIVE | C_RELATIVE);
982     else {
983         if (R->screen.tlen[R->screen.cur.row + R->TermWin.saveLines] == 0)
984             return;             /* um, yeah? */
985         rxvt_scr_insdel_chars(aR_ 1, INSERT);
986     }
987 }
988 #endif
989 /* ------------------------------------------------------------------------- */
990 /*
991  * Process DEC Forward Index
992  * XTERM_SEQ: ESC 9
993  * Move cursor right in row.  If we're at the right boundary, shift everything
994  * in that row left.  Clear right column.
995  */
996 #ifndef NO_FRILLS
997 /* EXTPROTO */
998 void
999 rxvt_scr_forwardindex(pR)
1000 {
1001     int             row;
1002
1003     if (R->screen.cur.col < R->TermWin.ncol - 1)
1004         rxvt_scr_gotorc(aR_ 0, 1, R_RELATIVE | C_RELATIVE);
1005     else {
1006         row = R->screen.cur.row + R->TermWin.saveLines;
1007         if (R->screen.tlen[row] == 0)
1008             return;             /* um, yeah? */
1009         else if (R->screen.tlen[row] == -1)
1010             R->screen.tlen[row] = R->TermWin.ncol;
1011         rxvt_scr_gotorc(aR_ 0, 0, R_RELATIVE);
1012         rxvt_scr_insdel_chars(aR_ 1, DELETE);
1013         rxvt_scr_gotorc(aR_ 0, R->TermWin.ncol - 1, R_RELATIVE);
1014     }
1015 }
1016 #endif
1017
1018 /* ------------------------------------------------------------------------- */
1019 /*
1020  * Goto Row/Column
1021  */
1022 /* EXTPROTO */
1023 void
1024 rxvt_scr_gotorc(pR_ int row, int col, int relative)
1025 {
1026     R->want_refresh = 1;
1027     ZERO_SCROLLBACK(R);
1028     RESET_CHSTAT(R);
1029 #ifdef RXVT_GRAPHICS
1030     if (rxvt_Gr_Displayed(aR))
1031         rxvt_Gr_scroll(aR_ 0);
1032 #endif
1033
1034     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, R->screen.cur.row, R->screen.cur.col));
1035
1036     R->screen.cur.col = ((relative & C_RELATIVE) ? (R->screen.cur.col + col)
1037                                                  : col);
1038     MAX_IT(R->screen.cur.col, 0);
1039     MIN_IT(R->screen.cur.col, (int32_t)R->TermWin.ncol - 1);
1040
1041     R->screen.flags &= ~Screen_WrapNext;
1042     if (relative & R_RELATIVE) {
1043         if (row > 0) {
1044             if (R->screen.cur.row <= R->screen.bscroll
1045                 && (R->screen.cur.row + row) > R->screen.bscroll)
1046                 R->screen.cur.row = R->screen.bscroll;
1047             else
1048                 R->screen.cur.row += row;
1049         } else if (row < 0) {
1050             if (R->screen.cur.row >= R->screen.tscroll
1051                 && (R->screen.cur.row + row) < R->screen.tscroll)
1052                 R->screen.cur.row = R->screen.tscroll;
1053             else
1054                 R->screen.cur.row += row;
1055         }
1056     } else {
1057         if (R->screen.flags & Screen_Relative) {        /* relative origin mode */
1058             R->screen.cur.row = row + R->screen.tscroll;
1059             MIN_IT(R->screen.cur.row, R->screen.bscroll);
1060         } else
1061             R->screen.cur.row = row;
1062     }
1063     MAX_IT(R->screen.cur.row, 0);
1064     MIN_IT(R->screen.cur.row, (int32_t)R->TermWin.nrow - 1);
1065 }
1066
1067 /* ------------------------------------------------------------------------- */
1068 /*
1069  * direction  should be UP or DN
1070  */
1071 /* EXTPROTO */
1072 void
1073 rxvt_scr_index(pR_ enum page_dirn direction)
1074 {
1075     int             dirn;
1076
1077     R->want_refresh = 1;
1078     dirn = ((direction == UP) ? 1 : -1);
1079     D_SCREEN((stderr, "rxvt_scr_index(%d)", dirn));
1080
1081     ZERO_SCROLLBACK(R);
1082     RESET_CHSTAT(R);
1083
1084 #ifdef RXVT_GRAPHICS
1085     if (rxvt_Gr_Displayed(aR))
1086         rxvt_Gr_scroll(aR_ 0);
1087 #endif
1088
1089     R->screen.flags &= ~Screen_WrapNext;
1090     if ((R->screen.cur.row == R->screen.bscroll && direction == UP)
1091         || (R->screen.cur.row == R->screen.tscroll && direction == DN))
1092         rxvt_scroll_text(aR_ R->screen.tscroll, R->screen.bscroll, dirn, 0);
1093     else
1094         R->screen.cur.row += dirn;
1095     MAX_IT(R->screen.cur.row, 0);
1096     MIN_IT(R->screen.cur.row, (int32_t)R->TermWin.nrow - 1);
1097     rxvt_selection_check(aR_ 0);
1098 }
1099
1100 /* ------------------------------------------------------------------------- */
1101 /*
1102  * Erase part or whole of a line
1103  * XTERM_SEQ: Clear line to right: ESC [ 0 K
1104  * XTERM_SEQ: Clear line to left : ESC [ 1 K
1105  * XTERM_SEQ: Clear whole line   : ESC [ 2 K
1106  */
1107 /* EXTPROTO */
1108 void
1109 rxvt_scr_erase_line(pR_ int mode)
1110 {
1111     unsigned int    row, col, num;
1112
1113     R->want_refresh = 1;
1114     D_SCREEN((stderr, "rxvt_scr_erase_line(%d) at screen row: %d", mode, R->screen.cur.row));
1115     ZERO_SCROLLBACK(R);
1116     RESET_CHSTAT(R);
1117
1118 #ifdef RXVT_GRAPHICS
1119     if (rxvt_Gr_Displayed(aR))
1120         rxvt_Gr_scroll(aR_ 0);
1121 #endif
1122
1123     rxvt_selection_check(aR_ 1);
1124
1125     R->screen.flags &= ~Screen_WrapNext;
1126
1127     row = R->TermWin.saveLines + R->screen.cur.row;
1128     switch (mode) {
1129     case 0:                     /* erase to end of line */
1130         col = R->screen.cur.col;
1131         num = R->TermWin.ncol - col;
1132         MIN_IT(R->screen.tlen[row], (int16_t)col);
1133         if (ROWCOL_IN_ROW_AT_OR_AFTER(R->selection.beg, R->screen.cur)
1134             || ROWCOL_IN_ROW_AT_OR_AFTER(R->selection.end, R->screen.cur))
1135             CLEAR_SELECTION(R);
1136         break;
1137     case 1:                     /* erase to beginning of line */
1138         col = 0;
1139         num = R->screen.cur.col + 1;
1140         if (ROWCOL_IN_ROW_AT_OR_BEFORE(R->selection.beg, R->screen.cur)
1141             || ROWCOL_IN_ROW_AT_OR_BEFORE(R->selection.end, R->screen.cur))
1142             CLEAR_SELECTION(R);
1143         break;
1144     case 2:                     /* erase whole line */
1145         col = 0;
1146         num = R->TermWin.ncol;
1147         R->screen.tlen[row] = 0;
1148         if (R->selection.beg.row <= R->screen.cur.row
1149             && R->selection.end.row >= R->screen.cur.row)
1150             CLEAR_SELECTION(R);
1151         break;
1152     default:
1153         return;
1154     }
1155     if (R->screen.text[row])
1156         rxvt_blank_line(aR_
1157                         &(R->screen.text[row][col]),
1158                         &(R->screen.rend[row][col]), num, R->rstyle);
1159     else
1160         rxvt_blank_screen_mem(aR_ R->screen.text, R->screen.rend, row,
1161                               R->rstyle);
1162 }
1163
1164 /* ------------------------------------------------------------------------- */
1165 /*
1166  * Erase part of whole of the screen
1167  * XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
1168  * XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
1169  * XTERM_SEQ: Clear whole screen        : ESC [ 2 J
1170  */
1171 /* EXTPROTO */
1172 void
1173 rxvt_scr_erase_screen(pR_ int mode)
1174 {
1175     int             num;
1176     int32_t         row, row_offset;
1177     rend_t          ren;
1178     XGCValues       gcvalue;
1179
1180     R->want_refresh = 1;
1181     D_SCREEN((stderr, "rxvt_scr_erase_screen(%d) at screen row: %d", mode, R->screen.cur.row));
1182     ZERO_SCROLLBACK(R);
1183     RESET_CHSTAT(R);
1184     row_offset = (int32_t)R->TermWin.saveLines;
1185
1186     switch (mode) {
1187     case 0:                     /* erase to end of screen */
1188         rxvt_selection_check(aR_ 1);
1189         rxvt_scr_erase_line(aR_ 0);
1190         row = R->screen.cur.row + 1;    /* possible OOB */
1191         num = R->TermWin.nrow - row;
1192         break;
1193     case 1:                     /* erase to beginning of screen */
1194         rxvt_selection_check(aR_ 3);
1195         rxvt_scr_erase_line(aR_ 1);
1196         row = 0;
1197         num = R->screen.cur.row;
1198         break;
1199     case 2:                     /* erase whole screen */
1200         rxvt_selection_check(aR_ 3);
1201 #ifdef RXVT_GRAPHICS
1202         rxvt_Gr_ClearScreen(aR);
1203 #endif
1204         row = 0;
1205         num = R->TermWin.nrow;
1206         break;
1207     default:
1208         return;
1209     }
1210     R->refresh_type |= REFRESH_BOUNDS;
1211     if (R->selection.op && R->current_screen == R->selection.screen
1212         && ((R->selection.beg.row >= row && R->selection.beg.row <= row + num)
1213             || (R->selection.end.row >= row
1214                 && R->selection.end.row <= row + num)))
1215         CLEAR_SELECTION(R);
1216     if (row >= R->TermWin.nrow) /* Out Of Bounds */
1217         return;
1218     MIN_IT(num, (R->TermWin.nrow - row));
1219     if (R->rstyle & (RS_RVid | RS_Uline))
1220         ren = (rend_t) ~RS_None;
1221     else if (GET_BASEBG(R->rstyle) == Color_bg) {
1222         ren = DEFAULT_RSTYLE;
1223         CLEAR_ROWS(row, num);
1224     } else {
1225         ren = (R->rstyle & (RS_fgMask | RS_bgMask));
1226         gcvalue.foreground = R->PixColors[GET_BGCOLOR(R->rstyle)];
1227         XChangeGC(R->Xdisplay, R->TermWin.gc, GCForeground, &gcvalue);
1228         ERASE_ROWS(row, num);
1229         gcvalue.foreground = R->PixColors[Color_fg];
1230         XChangeGC(R->Xdisplay, R->TermWin.gc, GCForeground, &gcvalue);
1231     }
1232     for (; num--; row++) {
1233         rxvt_blank_screen_mem(aR_ R->screen.text, R->screen.rend,
1234                               (unsigned int)(row + row_offset), R->rstyle);
1235         R->screen.tlen[row + row_offset] = 0;
1236         rxvt_blank_line(aR_
1237                         R->drawn_text[row], R->drawn_rend[row],
1238                         (unsigned int)R->TermWin.ncol, ren);
1239     }
1240 }
1241
1242 /* ------------------------------------------------------------------------- */
1243 /*
1244  * Fill the screen with `E's
1245  * XTERM_SEQ: Screen Alignment Test: ESC # 8
1246  */
1247 /* EXTPROTO */
1248 void
1249 rxvt_scr_E(pR)
1250 {
1251     int             i, j, k;
1252     rend_t         *r1, fs;
1253
1254     R->want_refresh = 1;
1255     R->num_scr_allow = 0;
1256     ZERO_SCROLLBACK(R);
1257     RESET_CHSTAT(R);
1258     rxvt_selection_check(aR_ 3);
1259
1260     fs = SET_FONT (R->rstyle, R->TermWin.fontset->find_font ('E'));
1261     for (k = R->TermWin.saveLines, i = R->TermWin.nrow; i--; k++) {
1262         R->screen.tlen[k] = R->TermWin.ncol;    /* make the `E's selectable */
1263         fill_text (R->screen.text[k], 'E', R->TermWin.ncol);
1264         for (r1 = R->screen.rend[k], j = R->TermWin.ncol; j--; )
1265             *r1++ = fs;
1266     }
1267 }
1268
1269 /* ------------------------------------------------------------------------- */
1270 /*
1271  * Insert/Delete <count> lines
1272  */
1273 /* EXTPROTO */
1274 void
1275 rxvt_scr_insdel_lines(pR_ int count, int insdel)
1276 {
1277     int             end;
1278
1279     ZERO_SCROLLBACK(R);
1280     RESET_CHSTAT(R);
1281
1282 #ifdef RXVT_GRAPHICS
1283     if (rxvt_Gr_Displayed(aR))
1284         rxvt_Gr_scroll(aR_ 0);
1285 #endif
1286
1287     rxvt_selection_check(aR_ 1);
1288
1289     if (R->screen.cur.row > R->screen.bscroll)
1290         return;
1291
1292     end = R->screen.bscroll - R->screen.cur.row + 1;
1293     if (count > end) {
1294         if (insdel == DELETE)
1295             return;
1296         else if (insdel == INSERT)
1297             count = end;
1298     }
1299     R->screen.flags &= ~Screen_WrapNext;
1300
1301     rxvt_scroll_text(aR_ R->screen.cur.row, R->screen.bscroll, insdel * count,
1302                      0);
1303 }
1304
1305 /* ------------------------------------------------------------------------- */
1306 /*
1307  * Insert/Delete <count> characters from the current position
1308  */
1309 /* EXTPROTO */
1310 void
1311 rxvt_scr_insdel_chars(pR_ int count, int insdel)
1312 {
1313     int             col, row;
1314     rend_t          tr;
1315     text_t         *stp;
1316     rend_t         *srp;
1317     int16_t        *slp;
1318
1319     R->want_refresh = 1;
1320     ZERO_SCROLLBACK(R);
1321 #if 0
1322     RESET_CHSTAT(R);
1323 #endif
1324
1325 #ifdef RXVT_GRAPHICS
1326     if (rxvt_Gr_Displayed(aR))
1327         rxvt_Gr_scroll(aR_ 0);
1328 #endif
1329
1330     if (count <= 0)
1331         return;
1332
1333     rxvt_selection_check(aR_ 1);
1334     MIN_IT(count, (R->TermWin.ncol - R->screen.cur.col));
1335
1336     row = R->screen.cur.row + R->TermWin.saveLines;
1337     R->screen.flags &= ~Screen_WrapNext;
1338
1339     stp = R->screen.text[row];
1340     srp = R->screen.rend[row];
1341     slp = &(R->screen.tlen[row]);
1342     switch (insdel) {
1343     case INSERT:
1344         for (col = R->TermWin.ncol - 1; (col - count) >= R->screen.cur.col;
1345              col--) {
1346             stp[col] = stp[col - count];
1347             srp[col] = srp[col - count];
1348         }
1349         if (*slp != -1) {
1350             *slp += count;
1351             MIN_IT(*slp, R->TermWin.ncol);
1352         }
1353         if (R->selection.op && R->current_screen == R->selection.screen
1354             && ROWCOL_IN_ROW_AT_OR_AFTER(R->selection.beg, R->screen.cur)) {
1355             if (R->selection.end.row != R->screen.cur.row
1356                 || (R->selection.end.col + count >= R->TermWin.ncol))
1357                 CLEAR_SELECTION(R);
1358             else {              /* shift selection */
1359                 R->selection.beg.col += count;
1360                 R->selection.mark.col += count; /* XXX: yes? */
1361                 R->selection.end.col += count;
1362             }
1363         }
1364         rxvt_blank_line(aR_ &(stp[R->screen.cur.col]), &(srp[R->screen.cur.col]),
1365                         (unsigned int)count, R->rstyle);
1366         break;
1367     case ERASE:
1368         R->screen.cur.col += count;     /* don't worry if > R->TermWin.ncol */
1369         rxvt_selection_check(aR_ 1);
1370         R->screen.cur.col -= count;
1371         rxvt_blank_line(aR_ &(stp[R->screen.cur.col]), &(srp[R->screen.cur.col]),
1372                         (unsigned int)count, R->rstyle);
1373         break;
1374     case DELETE:
1375         tr = srp[R->TermWin.ncol - 1]
1376              & (RS_fgMask | RS_bgMask | RS_baseattrMask);
1377         for (col = R->screen.cur.col; (col + count) < R->TermWin.ncol; col++) {
1378             stp[col] = stp[col + count];
1379             srp[col] = srp[col + count];
1380         }
1381         rxvt_blank_line(aR_
1382                         &(stp[R->TermWin.ncol - count]),
1383                         &(srp[R->TermWin.ncol - count]),
1384                         (unsigned int)count, tr);
1385         if (*slp == -1) /* break line continuation */
1386             *slp = R->TermWin.ncol;
1387         *slp -= count;
1388         MAX_IT(*slp, 0);
1389         if (R->selection.op && R->current_screen == R->selection.screen
1390             && ROWCOL_IN_ROW_AT_OR_AFTER(R->selection.beg, R->screen.cur)) {
1391             if (R->selection.end.row != R->screen.cur.row
1392                 || (R->screen.cur.col >= R->selection.beg.col - count)
1393                 || R->selection.end.col >= R->TermWin.ncol)
1394                 CLEAR_SELECTION(R);
1395             else {
1396                 /* shift selection */
1397                 R->selection.beg.col -= count;
1398                 R->selection.mark.col -= count; /* XXX: yes? */
1399                 R->selection.end.col -= count;
1400             }
1401         }
1402         break;
1403     }
1404 }
1405
1406 /* ------------------------------------------------------------------------- */
1407 /*
1408  * Set the scrolling region
1409  * XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
1410  */
1411 /* EXTPROTO */
1412 void
1413 rxvt_scr_scroll_region(pR_ int top, int bot)
1414 {
1415     MAX_IT(top, 0);
1416     MIN_IT(bot, (int)R->TermWin.nrow - 1);
1417     if (top > bot)
1418         return;
1419     R->screen.tscroll = top;
1420     R->screen.bscroll = bot;
1421     rxvt_scr_gotorc(aR_ 0, 0, 0);
1422 }
1423
1424 /* ------------------------------------------------------------------------- */
1425 /*
1426  * Make the cursor visible/invisible
1427  * XTERM_SEQ: Make cursor visible  : ESC [ ? 25 h
1428  * XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
1429  */
1430 /* EXTPROTO */
1431 void
1432 rxvt_scr_cursor_visible(pR_ int mode)
1433 {
1434     R->want_refresh = 1;
1435     if (mode)
1436         R->screen.flags |= Screen_VisibleCursor;
1437     else
1438         R->screen.flags &= ~Screen_VisibleCursor;
1439 }
1440
1441 /* ------------------------------------------------------------------------- */
1442 /*
1443  * Set/unset automatic wrapping
1444  * XTERM_SEQ: Set Wraparound  : ESC [ ? 7 h
1445  * XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
1446  */
1447 /* EXTPROTO */
1448 void
1449 rxvt_scr_autowrap(pR_ int mode)
1450 {
1451     if (mode)
1452         R->screen.flags |= Screen_Autowrap;
1453     else
1454         R->screen.flags &= ~(Screen_Autowrap | Screen_WrapNext);
1455 }
1456
1457 /* ------------------------------------------------------------------------- */
1458 /*
1459  * Set/unset margin origin mode
1460  * Absolute mode: line numbers are counted relative to top margin of screen
1461  *      and the cursor can be moved outside the scrolling region.
1462  * Relative mode: line numbers are relative to top margin of scrolling region
1463  *      and the cursor cannot be moved outside.
1464  * XTERM_SEQ: Set Absolute: ESC [ ? 6 h
1465  * XTERM_SEQ: Set Relative: ESC [ ? 6 l
1466  */
1467 /* EXTPROTO */
1468 void
1469 rxvt_scr_relative_origin(pR_ int mode)
1470 {
1471     if (mode)
1472         R->screen.flags |= Screen_Relative;
1473     else
1474         R->screen.flags &= ~Screen_Relative;
1475     rxvt_scr_gotorc(aR_ 0, 0, 0);
1476 }
1477
1478 /* ------------------------------------------------------------------------- */
1479 /*
1480  * Set insert/replace mode
1481  * XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
1482  * XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
1483  */
1484 /* EXTPROTO */
1485 void
1486 rxvt_scr_insert_mode(pR_ int mode)
1487 {
1488     if (mode)
1489         R->screen.flags |= Screen_Insert;
1490     else
1491         R->screen.flags &= ~Screen_Insert;
1492 }
1493
1494 /* ------------------------------------------------------------------------- */
1495 /*
1496  * Set/Unset tabs
1497  * XTERM_SEQ: Set tab at current column  : ESC H
1498  * XTERM_SEQ: Clear tab at current column: ESC [ 0 g
1499  * XTERM_SEQ: Clear all tabs             : ESC [ 3 g
1500  */
1501 /* EXTPROTO */
1502 void
1503 rxvt_scr_set_tab(pR_ int mode)
1504 {
1505     if (mode < 0)
1506         MEMSET(R->tabs, 0, R->TermWin.ncol * sizeof(char));
1507     else if (R->screen.cur.col < R->TermWin.ncol)
1508         R->tabs[R->screen.cur.col] = (mode ? 1 : 0);
1509 }
1510
1511 /* ------------------------------------------------------------------------- */
1512 /*
1513  * Set reverse/normal video
1514  * XTERM_SEQ: Reverse video: ESC [ ? 5 h
1515  * XTERM_SEQ: Normal video : ESC [ ? 5 l
1516  */
1517 /* EXTPROTO */
1518 void
1519 rxvt_scr_rvideo_mode(pR_ int mode)
1520 {
1521     XGCValues       gcvalue;
1522
1523     if (R->rvideo != mode) {
1524         R->rvideo = mode;
1525         SWAP_IT(R->PixColors[Color_fg], R->PixColors[Color_bg], rxvt_color);
1526 #if defined(XPM_BACKGROUND)
1527         if (R->bgPixmap.pixmap == None)
1528 #endif
1529 #if defined(TRANSPARENT)
1530             if (!(R->Options & Opt_transparent) || R->am_transparent == 0)
1531 #endif
1532             XSetWindowBackground(R->Xdisplay, R->TermWin.vt,
1533                                  R->PixColors[Color_bg]);
1534
1535         gcvalue.foreground = R->PixColors[Color_fg];
1536         gcvalue.background = R->PixColors[Color_bg];
1537         XChangeGC(R->Xdisplay, R->TermWin.gc, GCBackground | GCForeground,
1538                   &gcvalue);
1539         rxvt_scr_clear(aR);
1540         rxvt_scr_touch(aR_ True);
1541     }
1542 }
1543
1544 /* ------------------------------------------------------------------------- */
1545 /*
1546  * Report current cursor position
1547  * XTERM_SEQ: Report position: ESC [ 6 n
1548  */
1549 /* EXTPROTO */
1550 void
1551 rxvt_scr_report_position(pR)
1552 {
1553     rxvt_tt_printf(aR_ "\033[%d;%dR", R->screen.cur.row + 1,
1554                    R->screen.cur.col + 1);
1555 }
1556 \f
1557 /* ------------------------------------------------------------------------- *
1558  *                                  FONTS                                    *
1559  * ------------------------------------------------------------------------- */
1560
1561 /*
1562  * Set font style
1563  */
1564 /* INTPROTO */
1565 void
1566 rxvt_set_font_style(pR)
1567 {
1568     switch (R->charsets[R->screen.charset]) {
1569     case '0':                   /* DEC Special Character & Line Drawing Set */
1570         break;
1571     case 'A':                   /* United Kingdom (UK) */
1572         break;
1573     case 'B':                   /* United States (USASCII) */
1574         break;
1575     case '<':                   /* Multinational character set */
1576         break;
1577     case '5':                   /* Finnish character set */
1578         break;
1579     case 'C':                   /* Finnish character set */
1580         break;
1581     case 'K':                   /* German character set */
1582         break;
1583     }
1584 }
1585
1586 /* ------------------------------------------------------------------------- */
1587 /*
1588  * Choose a font
1589  * XTERM_SEQ: Invoke G0 character set: CTRL-O
1590  * XTERM_SEQ: Invoke G1 character set: CTRL-N
1591  * XTERM_SEQ: Invoke G2 character set: ESC N
1592  * XTERM_SEQ: Invoke G3 character set: ESC O
1593  */
1594 /* EXTPROTO */
1595 void
1596 rxvt_scr_charset_choose(pR_ int set)
1597 {
1598     R->screen.charset = set;
1599     rxvt_set_font_style(aR);
1600 }
1601
1602 /* ------------------------------------------------------------------------- */
1603 /*
1604  * Set a font
1605  * XTERM_SEQ: Set G0 character set: ESC ( <C>
1606  * XTERM_SEQ: Set G1 character set: ESC ) <C>
1607  * XTERM_SEQ: Set G2 character set: ESC * <C>
1608  * XTERM_SEQ: Set G3 character set: ESC + <C>
1609  * See set_font_style for possible values for <C>
1610  */
1611 /* EXTPROTO */
1612 void
1613 rxvt_scr_charset_set(pR_ int set, unsigned int ch)
1614 {
1615     R->charsets[set] = (unsigned char)ch;
1616     rxvt_set_font_style(aR);
1617 }
1618
1619 \f
1620 /* ------------------------------------------------------------------------- *
1621  *                           GRAPHICS COLOURS                                *
1622  * ------------------------------------------------------------------------- */
1623
1624 #ifdef RXVT_GRAPHICS
1625 /* EXTPROTO */
1626 int
1627 rxvt_scr_get_fgcolor(pR)
1628 {
1629     return GET_FGCOLOR(R->rstyle);
1630 }
1631
1632 /* ------------------------------------------------------------------------- */
1633 /* EXTPROTO */
1634 int
1635 rxvt_scr_get_bgcolor(pR)
1636 {
1637     return GET_BGCOLOR(R->rstyle);
1638 }
1639 #endif
1640 \f
1641 /* ------------------------------------------------------------------------- *
1642  *                        MAJOR SCREEN MANIPULATION                          *
1643  * ------------------------------------------------------------------------- */
1644
1645 /*
1646  * Refresh an area
1647  */
1648 enum {
1649     PART_BEG = 0,
1650     PART_END,
1651     RC_COUNT
1652 };
1653
1654 /* EXTPROTO */
1655 void
1656 rxvt_scr_expose(pR_ int x, int y, int width, int height, Bool refresh)
1657 {
1658     int             i;
1659     row_col_t       rc[RC_COUNT];
1660
1661     if (R->drawn_text == NULL)  /* sanity check */
1662         return;
1663
1664 #ifdef DEBUG_STRICT
1665     x = max(x, (int)R->TermWin.int_bwidth);
1666     x = min(x, (int)R->TermWin.width);
1667     y = max(y, (int)R->TermWin.int_bwidth);
1668     y = min(y, (int)R->TermWin.height);
1669 #endif
1670
1671 /* round down */
1672     rc[PART_BEG].col = Pixel2Col(x);
1673     rc[PART_BEG].row = Pixel2Row(y);
1674 /* round up */
1675     rc[PART_END].col = Pixel2Width(x + width + R->TermWin.fwidth - 1);
1676     rc[PART_END].row = Pixel2Row(y + height + R->TermWin.fheight - 1);
1677
1678 /* sanity checks */
1679     for (i = PART_BEG; i < RC_COUNT; i++) {
1680         MIN_IT(rc[i].col, R->TermWin.ncol - 1);
1681         MIN_IT(rc[i].row, R->TermWin.nrow - 1);
1682     }
1683
1684     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));
1685
1686     for (i = rc[PART_BEG].row; i <= rc[PART_END].row; i++)
1687         fill_text (&(R->drawn_text[i][rc[PART_BEG].col]), 0,
1688                    (rc[PART_END].col - rc[PART_BEG].col + 1));
1689
1690     if (refresh)
1691         rxvt_scr_refresh(aR_ SLOW_REFRESH | REFRESH_BOUNDS);
1692 }
1693
1694 /* ------------------------------------------------------------------------- */
1695 /*
1696  * Refresh the entire screen
1697  */
1698 /* EXTPROTO */
1699 void
1700 rxvt_scr_touch(pR_ Bool refresh)
1701 {
1702     rxvt_scr_expose(aR_ 0, 0, R->TermWin.width, R->TermWin.height, refresh);
1703 }
1704
1705 /* ------------------------------------------------------------------------- */
1706 /*
1707  * Move the display so that the line represented by scrollbar value Y is at
1708  * the top of the screen
1709  */
1710 /* EXTPROTO */
1711 int
1712 rxvt_scr_move_to(pR_ int y, int len)
1713 {
1714     long            p = 0;
1715     uint16_t       oldviewstart;
1716
1717     oldviewstart = R->TermWin.view_start;
1718     if (y < len) {
1719         p = (R->TermWin.nrow + R->TermWin.nscrolled) * (len - y) / len;
1720         p -= (long)(R->TermWin.nrow - 1);
1721         p = max(p, 0);
1722     }
1723     R->TermWin.view_start = (uint16_t)min(p, R->TermWin.nscrolled);
1724     D_SCREEN((stderr, "rxvt_scr_move_to(%d, %d) view_start:%d", y, len, R->TermWin.view_start));
1725
1726     return rxvt_scr_changeview(aR_ oldviewstart);
1727 }
1728
1729 /* ------------------------------------------------------------------------- */
1730 /*
1731  * Page the screen up/down nlines
1732  * direction should be UP or DN
1733  */
1734 /* EXTPROTO */
1735 int
1736 rxvt_scr_page(pR_ enum page_dirn direction, int nlines)
1737 {
1738     int             n;
1739     uint16_t       oldviewstart;
1740
1741     D_SCREEN((stderr, "rxvt_scr_page(%s, %d) view_start:%d", ((direction == UP) ? "UP" : "DN"), nlines, R->TermWin.view_start));
1742 #ifdef DEBUG_STRICT
1743     assert((nlines >= 0) && (nlines <= R->TermWin.nrow));
1744 #endif
1745     oldviewstart = R->TermWin.view_start;
1746     if (direction == UP) {
1747         n = R->TermWin.view_start + nlines;
1748         R->TermWin.view_start = min(n, R->TermWin.nscrolled);
1749     } else {
1750         n = R->TermWin.view_start - nlines;
1751         R->TermWin.view_start = max(n, 0);
1752     }
1753     return rxvt_scr_changeview(aR_ oldviewstart);
1754 }
1755
1756 /* INTPROTO */
1757 int
1758 rxvt_scr_changeview(pR_ uint16_t oldviewstart)
1759 {
1760     if (R->TermWin.view_start != oldviewstart) {
1761         R->want_refresh = 1;
1762 #ifdef RXVT_GRAPHICS
1763         if (rxvt_Gr_Displayed(aR))
1764             rxvt_Gr_scroll(aR_ 0);
1765 #endif
1766         R->num_scr -= (R->TermWin.view_start - oldviewstart);
1767     }
1768     return (int)(R->TermWin.view_start - oldviewstart);
1769 }
1770
1771 /* ------------------------------------------------------------------------- */
1772 /* EXTPROTO */
1773 void
1774 rxvt_scr_bell(pR)
1775 {
1776 #ifndef NO_BELL
1777 # ifndef NO_MAPALERT
1778 #  ifdef MAPALERT_OPTION
1779     if (R->Options & Opt_mapAlert)
1780 #  endif
1781         XMapWindow(R->Xdisplay, R->TermWin.parent[0]);
1782 # endif
1783     if (R->Options & Opt_visualBell) {
1784         rxvt_scr_rvideo_mode(aR_ !R->rvideo); /* refresh also done */
1785         rxvt_scr_rvideo_mode(aR_ !R->rvideo); /* refresh also done */
1786     } else
1787         XBell(R->Xdisplay, 0);
1788 #endif
1789 }
1790
1791 /* ------------------------------------------------------------------------- */
1792 /* ARGSUSED */
1793 /* EXTPROTO */
1794 void
1795 rxvt_scr_printscreen(pR_ int fullhist)
1796 {
1797 #ifdef PRINTPIPE
1798     int             i, r1, nrows, row_offset;
1799     text_t         *t;
1800     FILE           *fd;
1801
1802     if ((fd = rxvt_popen_printer(aR)) == NULL)
1803         return;
1804     nrows = R->TermWin.nrow;
1805     row_offset = R->TermWin.saveLines;
1806     if (!fullhist)
1807         row_offset -= R->TermWin.view_start;
1808     else {
1809         nrows += R->TermWin.nscrolled;
1810         row_offset -= R->TermWin.nscrolled;
1811     }
1812
1813     for (r1 = 0; r1 < nrows; r1++) {
1814         t = R->screen.text[r1 + row_offset];
1815         for (i = R->TermWin.ncol - 1; i >= 0; i--)
1816             if (!isspace(t[i]))
1817                 break;
1818         fprintf(fd, "%.*s\n", (i + 1), t);
1819     }
1820     rxvt_pclose_printer(fd);
1821 #endif
1822 }
1823
1824 /* ------------------------------------------------------------------------- */
1825 /*
1826  * Refresh the screen
1827  * R->drawn_text/R->drawn_rend contain the screen information before the update.
1828  * R->screen.text/R->screen.rend contain what the screen will change to.
1829  */
1830
1831 #if defined (NO_BRIGHTCOLOR) || defined (VERYBOLD)
1832 # define MONO_BOLD(x)           ((x) & (RS_Bold|RS_Blink))
1833 # define MONO_BOLD_FG(x, fg)    MONO_BOLD(x)
1834 #else
1835 # define MONO_BOLD(x)                                           \
1836     (((x) & (RS_Bold | RS_fgMask)) == (RS_Bold | Color_fg))
1837 # define MONO_BOLD_FG(x, fg)    (((x) & RS_Bold) && (fg) == Color_fg)
1838 #endif
1839
1840 #define FONT_WIDTH(X, Y)                                                \
1841     (X)->per_char[(Y) - (X)->min_char_or_byte2].width
1842 #define FONT_RBEAR(X, Y)                                                \
1843     (X)->per_char[(Y) - (X)->min_char_or_byte2].rbearing
1844 #define FONT_LBEAR(X, Y)                                                \
1845     (X)->per_char[(Y) - (X)->min_char_or_byte2].lbearing
1846 #define IS_FONT_CHAR(X, Y)                                              \
1847     ((Y) >= (X)->min_char_or_byte2 && (Y) <= (X)->max_char_or_byte2)
1848
1849 /* EXTPROTO */
1850 void
1851 rxvt_scr_refresh(pR_ unsigned char refresh_type)
1852 {
1853     unsigned char   clearfirst, /* first character writes before cell        */
1854                     clearlast,  /* last character writes beyond cell         */
1855                     must_clear, /* use draw_string not draw_image_string     */
1856 #ifndef NO_BOLDFONT
1857                     bfont,      /* we've changed font to bold font           */
1858 #endif
1859                     rvid,       /* reverse video this position               */
1860                     wbyte,      /* we're in multibyte                        */
1861                     showcursor; /* show the cursor                           */
1862     int             fore, back; /* desired foreground/background             */
1863     int16_t         col, row,   /* column/row we're processing               */
1864                     ocrow;      /* old cursor row                            */
1865     int             cursorwidth;
1866     int             i,          /* tmp                                       */
1867                     row_offset; /* basic offset in screen structure          */
1868 #ifndef NO_CURSORCOLOR
1869     rend_t          cc1;        /* store colours at cursor position(s)       */
1870     rend_t          cc2;        /* store colours at cursor position(s)       */
1871 #endif
1872     rend_t         *drp, *srp;  /* drawn-rend-pointer, screen-rend-pointer   */
1873     text_t         *dtp, *stp;  /* drawn-text-pointer, screen-text-pointer   */
1874     char           *buffer;     /* local copy of R->buffer                */
1875
1876     if (refresh_type == NO_REFRESH || !R->TermWin.mapped)
1877       return;
1878
1879     /*
1880      * A: set up vars
1881      */
1882     clearfirst = clearlast = must_clear = wbyte = 0;
1883 #ifndef NO_BOLDFONT
1884     bfont = 0;
1885 #endif
1886
1887     if (R->currmaxcol < R->TermWin.ncol)
1888       {
1889         R->currmaxcol = R->TermWin.ncol;
1890         R->buffer = (char *)rxvt_realloc (R->buffer,
1891                                           sizeof(char) * (R->currmaxcol + 1) * MB_CUR_MAX);
1892       }
1893
1894     buffer = R->buffer;
1895     R->refresh_count = 0;
1896
1897     row_offset = R->TermWin.saveLines - R->TermWin.view_start;
1898
1899     if ((refresh_type & REFRESH_BOUNDS))
1900       {
1901         clearfirst = clearlast = 1;
1902         R->refresh_type &= ~REFRESH_BOUNDS;
1903       }
1904
1905 #if defined(XPM_BACKGROUND)
1906     must_clear |= (R->bgPixmap.pixmap != None);
1907 #endif
1908 #if defined(TRANSPARENT)
1909     must_clear |= ((R->Options & Opt_transparent) && R->am_transparent);
1910 #endif
1911     ocrow = R->oldcursor.row; /* is there an old outline cursor on screen? */
1912
1913 /*
1914  * B: reverse any characters which are selected
1915  */
1916     rxvt_scr_reverse_selection(aR);
1917
1918 /*
1919  * C: set the cursor character(s)
1920  */
1921     {
1922       unsigned char   setoldcursor;
1923       rend_t          ccol1,  /* Cursor colour       */
1924                       ccol2;  /* Cursor colour2      */
1925
1926       showcursor = (R->screen.flags & Screen_VisibleCursor);
1927       cursorwidth = 0;
1928 #ifdef CURSOR_BLINK
1929       if (R->hidden_cursor)
1930           showcursor = 0;
1931 #endif
1932
1933       cursorwidth = 0;
1934
1935       if (showcursor)
1936         {
1937           cursorwidth++;
1938
1939           srp = &(R->screen.rend[R->screen.cur.row + R->TermWin.saveLines]
1940                                 [R->screen.cur.col]);
1941
1942           if (showcursor && R->TermWin.focus)
1943             {
1944               *srp ^= RS_RVid;
1945 #ifndef NO_CURSORCOLOR
1946               cc1 = *srp & (RS_fgMask | RS_bgMask);
1947               if (XDEPTH > 2 && ISSET_PIXCOLOR(R, Color_cursor))
1948                   ccol1 = Color_cursor;
1949               else
1950 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
1951                   ccol1 = GET_FGCOLOR(R->rstyle);
1952 #else
1953                   ccol1 = Color_fg;
1954 #endif
1955               if (XDEPTH > 2 && ISSET_PIXCOLOR(R, Color_cursor2))
1956                   ccol2 = Color_cursor2;
1957               else
1958 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
1959                   ccol2 = GET_BGCOLOR(R->rstyle);
1960 #else
1961                   ccol2 = Color_bg;
1962 #endif
1963               *srp = SET_FGCOLOR(*srp, ccol1);
1964               *srp = SET_BGCOLOR(*srp, ccol2);
1965 #endif
1966             }
1967
1968           while (IS_WIDE (*srp))
1969             cursorwidth++, srp++;
1970         }
1971
1972       /* make sure no outline cursor is left around */
1973       setoldcursor = 0;
1974       if (ocrow != -1) {
1975           if (R->screen.cur.row + R->TermWin.view_start != ocrow
1976               || R->screen.cur.col != R->oldcursor.col) {
1977               if (ocrow < R->TermWin.nrow
1978                   && R->oldcursor.col < R->TermWin.ncol) {
1979                   R->drawn_rend[ocrow][R->oldcursor.col] ^= (RS_RVid | RS_Uline);
1980               }
1981               if (R->TermWin.focus || !showcursor)
1982                   R->oldcursor.row = -1;
1983               else
1984                   setoldcursor = 1;
1985           }
1986       } else if (!R->TermWin.focus)
1987           setoldcursor = 1;
1988       if (setoldcursor) {
1989           if (R->screen.cur.row + R->TermWin.view_start >= R->TermWin.nrow)
1990               R->oldcursor.row = -1;
1991           else {
1992               R->oldcursor.row = R->screen.cur.row + R->TermWin.view_start;
1993               R->oldcursor.col = R->screen.cur.col;
1994           }
1995       }
1996     }
1997
1998 #ifndef NO_SLOW_LINK_SUPPORT
1999 /*
2000  * D: CopyArea pass - very useful for slower links
2001  *    This has been deliberately kept simple.
2002  */
2003     i = R->num_scr;
2004     if (refresh_type == FAST_REFRESH && R->num_scr_allow && i
2005         && abs(i) < R->TermWin.nrow && !must_clear)
2006       {
2007         int16_t         nits;
2008         int             j;
2009         rend_t         *drp2;
2010         text_t         *dtp2;
2011         int             len, wlen;
2012
2013         j = R->TermWin.nrow;
2014         wlen = len = -1;
2015         row = i > 0 ? 0 : j - 1;
2016         for (; j-- >= 0; row += (i > 0 ? 1 : -1))
2017           {
2018             if (row + i >= 0 && row + i < R->TermWin.nrow && row + i != ocrow)
2019               {
2020                 stp = R->screen.text[row + row_offset];
2021                 srp = R->screen.rend[row + row_offset];
2022                 dtp = R->drawn_text[row];
2023                 dtp2 = R->drawn_text[row + i];
2024                 drp = R->drawn_rend[row];
2025                 drp2 = R->drawn_rend[row + i];
2026
2027                 for (nits = 0, col = R->TermWin.ncol; col--; )
2028                   if (stp[col] != dtp2[col] || srp[col] != drp2[col])
2029                     nits--;
2030                   else if (stp[col] != dtp[col] || srp[col] != drp[col])
2031                     nits++;
2032
2033                 if (nits > 8) /* XXX: arbitrary choice */
2034                   {
2035                     for (col = R->TermWin.ncol; col--; )
2036                       {
2037                         *dtp++ = *dtp2++;
2038                         *drp++ = *drp2++;
2039                       }
2040
2041                     if (len == -1)
2042                       len = row;
2043
2044                     wlen = row;
2045                     continue;
2046                   }
2047               }
2048
2049             if (len != -1)
2050               {
2051                 /* also comes here at end if needed because of >= above */
2052                 if (wlen < len)
2053                   SWAP_IT(wlen, len, int);
2054                 D_SCREEN((stderr, "rxvt_scr_refresh(): XCopyArea: %d -> %d (height: %d)", len + i, len, wlen - len + 1));
2055                 XCopyArea(R->Xdisplay, R->TermWin.vt, R->TermWin.vt,
2056                           R->TermWin.gc, 0, Row2Pixel(len + i),
2057                           (unsigned int)TermWin_TotalWidth(),
2058                           (unsigned int)Height2Pixel(wlen - len + 1),
2059                           0, Row2Pixel(len));
2060                 len = -1;
2061               }
2062           }
2063     }
2064 #endif
2065
2066 /*
2067  * E: main pass across every character
2068  */
2069     for (row = 0; row < R->TermWin.nrow; row++)
2070       {
2071         stp = R->screen.text[row + row_offset];
2072         srp = R->screen.rend[row + row_offset];
2073         dtp = R->drawn_text[row];
2074         drp = R->drawn_rend[row];
2075
2076 /*
2077  * E2: OK, now the real pass
2078  */
2079         int ypixel = (int)Row2Pixel(row);
2080
2081         for (col = 0; col < R->TermWin.ncol; col++)
2082           {
2083             /* compare new text with old - if exactly the same then continue */
2084             rend_t rend = srp[col];     /* screen rendition (target rendtion) */
2085
2086             if (stp[col] == dtp[col]    /* Must match characters to skip. */
2087                 && (rend == drp[col]    /* Either rendition the same or   */
2088                     || (stp[col] == ' ' /* space w/ no background change  */
2089                         && GET_BGATTR(rend) == GET_BGATTR(drp[col]))))
2090               continue;
2091
2092             text_t *text = stp + col;
2093             int count = 1;
2094
2095             /* redraw one or more characters */
2096
2097             dtp[col] = stp[col];
2098             drp[col] = rend;
2099
2100             if (*text == NOCHAR) // never start redrawing at invisible characters. */
2101               continue;
2102
2103             int xpixel = Col2Pixel(col);
2104
2105             // this loop looks very messy, it can probably be optimized
2106             // and cleaned a bit by you?
2107             for (i = 0; ++col < R->TermWin.ncol; )
2108               {
2109                 if (stp[col] == NOCHAR)
2110                   {
2111                     dtp[col] = stp[col];
2112                     drp[col] = rend;
2113                     count++;
2114
2115                     if (i) // only possible skip if char unchanged
2116                       i++;
2117
2118                     continue;
2119                   }
2120
2121                 if (((rend ^ srp[col]) & ~RS_wide) != 0)
2122                   break;
2123
2124                 count++;
2125
2126                 if (stp[col] != dtp[col]
2127                     || srp[col] != drp[col])
2128                   {
2129                     if (must_clear && (i++ > (count / 2)))
2130                       break;
2131
2132                     dtp[col] = stp[col];
2133                     drp[col] = rend;
2134                     i = 0;
2135                   }
2136                 else if (must_clear || (stp[col] != ' ' && ++i >= 32))
2137                   break;
2138               }
2139
2140             col--;      /* went one too far.  move back */
2141             count -= i; /* dump any matching trailing chars */
2142
2143 /*
2144  * Determine the attributes for the string
2145  */
2146             int fid = GET_FONT(rend);
2147             fore = GET_FGCOLOR(rend);
2148             back = GET_BGCOLOR(rend);
2149             rend = GET_ATTR(rend);
2150
2151             rvid = (rend & RS_RVid) ? 1 : 0;
2152 #ifdef OPTION_HC
2153             if (!rvid && (rend & RS_Blink))
2154               {
2155                 if (XDEPTH > 2 && ISSET_PIXCOLOR(R, Color_HC))
2156                   back = Color_HC;
2157                 else
2158                   rvid = !rvid; /* fall back */
2159               }
2160 #endif
2161             if (rvid)
2162               {
2163                 SWAP_IT(fore, back, int);
2164
2165 #ifndef NO_BOLD_UNDERLINE_REVERSE
2166                 if (XDEPTH > 2 && ISSET_PIXCOLOR(R, Color_RV)
2167 # ifndef NO_CURSORCOLOR
2168                     && !ISSET_PIXCOLOR(R, Color_cursor)
2169 # endif
2170                     )
2171                   back = Color_RV;
2172 #endif
2173               }
2174 #ifndef NO_BOLD_UNDERLINE_REVERSE
2175             else if (rend & RS_Bold)
2176               {
2177                 if (XDEPTH > 2 && ISSET_PIXCOLOR(R, Color_BD))
2178                   {
2179                     fore = Color_BD;
2180 # ifndef VERYBOLD
2181                     rend &= ~RS_Bold;   /* we've taken care of it */
2182 # endif
2183                   }
2184               }
2185             else if (rend & RS_Uline)
2186               {
2187                 if (XDEPTH > 2 && ISSET_PIXCOLOR(R, Color_UL))
2188                   {
2189                     fore = Color_UL;
2190                     rend &= ~RS_Uline;  /* we've taken care of it */
2191                   }
2192               }
2193 #endif
2194
2195 /*
2196  * Actually do the drawing of the string here
2197  */
2198             rxvt_font *font = (*R->TermWin.fontset)[fid];
2199
2200             if (back == Color_bg)
2201               {
2202                 if (must_clear)
2203                   {
2204                     for (i = 0; i < count; i++) /* don't draw empty strings */
2205                       if (text[i] != ' ')
2206                         {
2207                           font->draw (xpixel, ypixel, text, count, fore, -1);
2208                           goto nodraw;
2209                         }
2210
2211                     CLEAR_CHARS(xpixel, ypixel, count);
2212 nodraw: ;
2213                   }
2214                 else
2215                   font->draw (xpixel, ypixel, text, count, fore, Color_bg);
2216               }
2217             else
2218               font->draw (xpixel, ypixel, text, count, fore, back);
2219
2220             if ((rend & RS_Uline) && (font->descent > 1))
2221                 XDrawLine(R->Xdisplay, drawBuffer, R->TermWin.gc,
2222                           xpixel, ypixel + font->ascent + 1,
2223                           xpixel + Width2Pixel(count) - 1, ypixel + font->ascent + 1);
2224           }                     /* for (col....) */
2225       }                         /* for (row....) */
2226
2227 /*
2228  * G: cleanup cursor and display outline cursor if necessary
2229  */
2230     if (showcursor) {
2231         if (R->TermWin.focus) {
2232             srp = &(R->screen.rend[R->screen.cur.row + R->TermWin.saveLines]
2233                                   [R->screen.cur.col]);
2234             *srp ^= RS_RVid;
2235 #ifndef NO_CURSORCOLOR
2236             *srp = (*srp & ~(RS_fgMask | RS_bgMask)) | cc1;
2237 #endif
2238         } else if (R->oldcursor.row >= 0) {
2239 #ifndef NO_CURSORCOLOR
2240             unsigned long   gcmask;     /* Graphics Context mask */
2241
2242             if (XDEPTH > 2 && ISSET_PIXCOLOR(R, Color_cursor)) {
2243                 XSetForeground (R->Xdisplay, R->TermWin.gc, R->PixColors[Color_cursor]);
2244             }
2245 #endif
2246             XDrawRectangle(R->Xdisplay, drawBuffer, R->TermWin.gc,
2247                            Col2Pixel(R->oldcursor.col),
2248                            Row2Pixel(R->oldcursor.row),
2249                            (unsigned int)(Width2Pixel(cursorwidth) - 1),
2250                            (unsigned int)(Height2Pixel(1) - R->TermWin.lineSpace - 1));
2251         }
2252     }
2253 /*
2254  * H: cleanup selection
2255  */
2256     rxvt_scr_reverse_selection(aR);
2257
2258 /*
2259  * I: other general cleanup
2260  */
2261     if (clearfirst && R->TermWin.int_bwidth)
2262         /* 
2263          * clear the whole screen height, note that width == 0 is treated
2264          * specially by XClearArea
2265          */
2266         XClearArea(R->Xdisplay, R->TermWin.vt, 0, 0,
2267                    (unsigned int)R->TermWin.int_bwidth,
2268                    (unsigned int)TermWin_TotalHeight(), False);
2269     if (clearlast && R->TermWin.int_bwidth)
2270         /* 
2271          * clear the whole screen height, note that width == 0 is treated
2272          * specially by XClearArea
2273          */
2274         XClearArea(R->Xdisplay, R->TermWin.vt,
2275                    R->TermWin.width + R->TermWin.int_bwidth, 0,
2276                    (unsigned int)R->TermWin.int_bwidth,
2277                    (unsigned int)TermWin_TotalHeight(), False);
2278     if (refresh_type & SMOOTH_REFRESH)
2279         XSync(R->Xdisplay, False);
2280
2281     R->num_scr = 0;
2282     R->num_scr_allow = 1;
2283     R->want_refresh = 0;        /* screen is current */
2284 }
2285 /* ------------------------------------------------------------------------- */
2286
2287 /* EXTPROTO */
2288 void
2289 rxvt_scr_clear(pR)
2290 {
2291     if (!R->TermWin.mapped)
2292         return;
2293     R->num_scr_allow = 0;
2294     R->want_refresh = 1;
2295 #ifdef TRANSPARENT
2296     if ((R->Options & Opt_transparent) && (R->am_pixmap_trans == 0)) {
2297         int             i;
2298
2299         if (!(R->Options & Opt_transparent_all))
2300             i = 0;
2301         else
2302             i = (int)(sizeof(R->TermWin.parent) / sizeof(Window));
2303         for (; i--;)
2304             if (R->TermWin.parent[i] != None)
2305                 XClearWindow(R->Xdisplay, R->TermWin.parent[i]);
2306     }
2307 #endif
2308     XClearWindow(R->Xdisplay, R->TermWin.vt);
2309 }
2310
2311 /* ------------------------------------------------------------------------- */
2312 /* INTPROTO */
2313 void
2314 rxvt_scr_reverse_selection(pR)
2315 {
2316     int             i, col, row, end_row;
2317     rend_t         *srp;
2318
2319     if (R->selection.op && R->current_screen == R->selection.screen) {
2320         end_row = R->TermWin.saveLines - R->TermWin.view_start;
2321         i = R->selection.beg.row + R->TermWin.saveLines;
2322         row = R->selection.end.row + R->TermWin.saveLines;
2323         if (i >= end_row)
2324             col = R->selection.beg.col;
2325         else {
2326             col = 0;
2327             i = end_row;
2328         }
2329         end_row += R->TermWin.nrow;
2330         for (; i < row && i < end_row; i++, col = 0)
2331             for (srp = R->screen.rend[i]; col < R->TermWin.ncol; col++)
2332 #ifndef OPTION_HC
2333                 srp[col] ^= RS_RVid;
2334 #else
2335                 srp[col] ^= RS_Blink;
2336 #endif
2337         if (i == row && i < end_row)
2338             for (srp = R->screen.rend[i]; col < R->selection.end.col; col++)
2339 #ifndef OPTION_HC
2340                 srp[col] ^= RS_RVid;
2341 #else
2342                 srp[col] ^= RS_Blink;
2343 #endif
2344     }
2345 }
2346
2347 /* ------------------------------------------------------------------------- */
2348 /*
2349  * Dump the whole scrollback and screen to the passed filedescriptor.  The
2350  * invoking routine must close the fd.
2351  */
2352 #if 0
2353 /* EXTPROTO */
2354 void
2355 rxvt_scr_dump(pR_ int fd)
2356 {
2357     int             row, wrote;
2358     unsigned int    width, towrite;
2359     char            r1[] = "\n";
2360
2361     for (row = R->TermWin.saveLines - R->TermWin.nscrolled;
2362          row < R->TermWin.saveLines + R->TermWin.nrow - 1; row++) {
2363         width = R->screen.tlen[row] >= 0 ? R->screen.tlen[row]
2364                                          : R->TermWin.ncol;
2365         for (towrite = width; towrite; towrite -= wrote) {
2366             wrote = write(fd, &(R->screen.text[row][width - towrite]),
2367                           towrite);
2368             if (wrote < 0)
2369                 return;         /* XXX: death, no report */
2370         }
2371         if (R->screen.tlen[row] >= 0)
2372             if (write(fd, r1, 1) <= 0)
2373                 return; /* XXX: death, no report */
2374     }
2375 }
2376 #endif
2377 \f
2378 /* ------------------------------------------------------------------------- *
2379  *                           CHARACTER SELECTION                             *
2380  * ------------------------------------------------------------------------- */
2381
2382 /*
2383  * -R->TermWin.nscrolled <= (selection row) <= R->TermWin.nrow - 1
2384  */
2385 /* EXTPROTO */
2386 void
2387 rxvt_selection_check(pR_ int check_more)
2388 {
2389     row_col_t       pos;
2390
2391     if (!R->selection.op)
2392         return;
2393
2394     pos.row = pos.col = 0;
2395     if ((R->selection.beg.row < -(int32_t)R->TermWin.nscrolled)
2396         || (R->selection.beg.row >= R->TermWin.nrow)
2397         || (R->selection.mark.row < -(int32_t)R->TermWin.nscrolled)
2398         || (R->selection.mark.row >= R->TermWin.nrow)
2399         || (R->selection.end.row < -(int32_t)R->TermWin.nscrolled)
2400         || (R->selection.end.row >= R->TermWin.nrow)
2401         || (check_more == 1
2402             && R->current_screen == R->selection.screen
2403             && !ROWCOL_IS_BEFORE(R->screen.cur, R->selection.beg)
2404             && ROWCOL_IS_BEFORE(R->screen.cur, R->selection.end))
2405         || (check_more == 2
2406             && ROWCOL_IS_BEFORE(R->selection.beg, pos)
2407             && ROWCOL_IS_AFTER(R->selection.end, pos))
2408         || (check_more == 3
2409             && ROWCOL_IS_AFTER(R->selection.end, pos))
2410         || (check_more == 4     /* screen width change */
2411             && (R->selection.beg.row != R->selection.end.row
2412                 || R->selection.end.col > R->TermWin.ncol)))
2413         CLEAR_SELECTION(R);
2414 }
2415
2416 /* ------------------------------------------------------------------------- */
2417 /*
2418  * Paste a selection direct to the command fd
2419  */
2420 /* INTPROTO */
2421 void
2422 rxvt_PasteIt(pR_ const unsigned char *data, unsigned int nitems)
2423 {
2424     unsigned int    i, j, n;
2425     unsigned char  *ds = (unsigned char *)rxvt_malloc(PROP_SIZE);
2426     
2427 /* convert normal newline chars into common keyboard Return key sequence */
2428     for (i = 0; i < nitems; i += PROP_SIZE) {
2429         n = min(nitems - i, PROP_SIZE);
2430         MEMCPY(ds, data + i, n);
2431         for (j = 0; j < n; j++)
2432             if (ds[j] == '\n')
2433                 ds[j] = '\r';
2434         rxvt_tt_write(aR_ ds, (int)n);
2435     }
2436     free(ds);
2437 }
2438
2439 /* ------------------------------------------------------------------------- */
2440 /*
2441  * Respond to a notification that a primary selection has been sent
2442  * EXT: SelectionNotify
2443  */
2444 /* EXTPROTO */
2445 int
2446 rxvt_selection_paste(pR_ Window win, Atom prop, Bool delete_prop)
2447 {
2448     long            nread = 0;
2449     unsigned long   bytes_after;
2450     XTextProperty   ct;
2451 #ifdef MULTICHAR_SET
2452     int             dummy_count;
2453     char          **cl;
2454 #endif
2455
2456     D_SELECT((stderr, "rxvt_selection_paste(%08lx, %lu, %d), wait=%2x", win, (unsigned long)prop, (int)delete_prop, R->selection_wait));
2457
2458     if (prop == None) {         /* check for failed XConvertSelection */
2459 #ifdef MULTICHAR_SET
2460         if ((R->selection_type & Sel_CompoundText)) {
2461             int             selnum = R->selection_type & Sel_whereMask;
2462
2463             R->selection_type = 0;
2464             if (selnum != Sel_direct)
2465                 rxvt_selection_request_other(aR_ XA_STRING, selnum);
2466         }
2467 #endif
2468         return 0;
2469     }
2470     for (;;) {
2471         if (XGetWindowProperty(R->Xdisplay, win, prop, (long)(nread / 4),
2472                                (long)(PROP_SIZE / 4), delete_prop,
2473                                AnyPropertyType, &ct.encoding, &ct.format,
2474                                &ct.nitems, &bytes_after,
2475                                &ct.value) != Success)
2476             break;
2477         if (ct.encoding == 0) {
2478             D_SELECT((stderr, "rxvt_selection_paste: property didn't exist!"));
2479             break;
2480         }
2481         if (ct.value == NULL) {
2482             D_SELECT((stderr, "rxvt_selection_paste: property shooting blanks!"));
2483             continue;
2484         }
2485         if (ct.nitems == 0) {
2486             D_SELECT((stderr, "rxvt_selection_paste: property empty - also INCR end"));
2487             if (R->selection_wait == Sel_normal && nread == 0) {
2488             /*
2489              * pass through again trying CUT_BUFFER0 if we've come from
2490              * XConvertSelection() but nothing was presented
2491              */
2492                 D_SELECT((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2493                 rxvt_selection_paste(aR_ Xroot, XA_CUT_BUFFER0, False);
2494             }
2495             nread = -1;         /* discount any previous stuff */
2496             break;
2497         }
2498         nread += ct.nitems;
2499 #ifdef MULTICHAR_SET
2500         if (XmbTextPropertyToTextList(R->Xdisplay, &ct, &cl,
2501                                       &dummy_count) == Success && cl) {
2502             rxvt_PasteIt(aR_ cl[0], STRLEN(cl[0]));
2503             XFreeStringList(cl);
2504         } else
2505 #endif
2506             rxvt_PasteIt(aR_ ct.value, (unsigned int)ct.nitems);
2507         if (bytes_after == 0)
2508             break;
2509         XFree(ct.value);
2510     }
2511     if (ct.value)
2512         XFree(ct.value);
2513     if (R->selection_wait == Sel_normal)
2514         R->selection_wait = Sel_none;
2515     D_SELECT((stderr, "rxvt_selection_paste: bytes written: %ld", nread));
2516     return (int)nread;
2517 }
2518
2519 /*
2520  * INCR support originally provided by Paul Sheer <psheer@obsidian.co.za>
2521  */
2522 /* EXTPROTO */
2523 void
2524 rxvt_selection_property(pR_ Window win, Atom prop)
2525 {
2526     int             reget_time = 0;
2527
2528     if (prop == None)
2529         return;
2530     D_SELECT((stderr, "rxvt_selection_property(%08lx, %lu)", win, (unsigned long)prop));
2531     if (R->selection_wait == Sel_normal) {
2532         int             a, afmt;
2533         Atom            atype;
2534         unsigned long   bytes_after, nitems;
2535         unsigned char  *s = NULL;
2536
2537         a = XGetWindowProperty(R->Xdisplay, win, prop, 0L, 1L, False,
2538                                R->xa[XA_INCR], &atype, &afmt, &nitems,
2539                                &bytes_after, &s);
2540         if (s)
2541             XFree(s);
2542         if (a != Success)
2543             return;
2544 #ifndef __CYGWIN32__
2545         if (atype == R->xa[XA_INCR]) {  /* start an INCR transfer */
2546             D_SELECT((stderr, "rxvt_selection_property: INCR: starting transfer"));
2547             XDeleteProperty(R->Xdisplay, win, prop);
2548             XFlush(R->Xdisplay);
2549             reget_time = 1;
2550             R->selection_wait = Sel_incr;
2551         }
2552 #endif
2553     } else if (R->selection_wait == Sel_incr) {
2554         reget_time = 1;
2555         if (rxvt_selection_paste(aR_ win, prop, True) == -1) {
2556             D_SELECT((stderr, "rxvt_selection_property: INCR: clean end"));
2557             R->selection_wait = Sel_none;
2558             R->timeout[TIMEOUT_INCR].tv_sec = 0;        /* turn off timer */
2559         }
2560     }
2561     if (reget_time) {   /* received more data so reget time */
2562         (void)gettimeofday(&(R->timeout[TIMEOUT_INCR]), NULL);
2563         R->timeout[TIMEOUT_INCR].tv_sec += 10;  /* ten seconds wait */
2564     }
2565 }
2566 /* ------------------------------------------------------------------------- */
2567 /*
2568  * Request the current selection: 
2569  * Order: > internal selection if available
2570  *        > PRIMARY, SECONDARY, CLIPBOARD if ownership is claimed (+)
2571  *        > CUT_BUFFER0
2572  * (+) if ownership is claimed but property is empty, rxvt_selection_paste()
2573  *     will auto fallback to CUT_BUFFER0
2574  * EXT: button 2 release
2575  */
2576 /* EXTPROTO */
2577 void
2578 rxvt_selection_request(pR_ Time tm, int x, int y)
2579 {
2580     D_SELECT((stderr, "rxvt_selection_request(%lu, %d, %d)", tm, x, y));
2581     if (x < 0 || x >= R->TermWin.width || y < 0 || y >= R->TermWin.height)
2582         return;                 /* outside window */
2583
2584     if (R->selection.text != NULL) {    /* internal selection */
2585         D_SELECT((stderr, "rxvt_selection_request: pasting internal"));
2586         rxvt_PasteIt(aR_ R->selection.text, R->selection.len);
2587         return;
2588     } else {
2589         int             i;
2590
2591         R->selection_request_time = tm;
2592         R->selection_wait = Sel_normal;
2593         for (i = Sel_Primary; i <= Sel_Clipboard; i++) {
2594 #ifdef MULTICHAR_SET
2595             R->selection_type = Sel_CompoundText;
2596 #else
2597             R->selection_type = 0;
2598 #endif
2599             if (rxvt_selection_request_other(aR_
2600 #ifdef MULTICHAR_SET
2601                                              R->xa[XA_COMPOUND_TEXT],
2602 #else
2603                                              XA_STRING,
2604 #endif
2605                                              i))
2606                 return;
2607         }
2608     }
2609     R->selection_wait = Sel_none;       /* don't loop in rxvt_selection_paste() */
2610     D_SELECT((stderr, "rxvt_selection_request: pasting CUT_BUFFER0"));
2611     rxvt_selection_paste(aR_ Xroot, XA_CUT_BUFFER0, False);
2612 }
2613
2614 /* INTPROTO */
2615 int
2616 rxvt_selection_request_other(pR_ Atom target, int selnum)
2617 {
2618     Atom            sel;
2619 #ifdef DEBUG_SELECT
2620     char           *debug_xa_names[] = { "PRIMARY", "SECONDARY", "CLIPBOARD" };
2621 #endif
2622
2623     R->selection_type |= selnum;
2624     if (selnum == Sel_Primary)
2625         sel = XA_PRIMARY;
2626     else if (selnum == Sel_Secondary)
2627         sel = XA_SECONDARY;
2628     else
2629         sel = R->xa[XA_CLIPBOARD];
2630     if (XGetSelectionOwner(R->Xdisplay, sel) != None) {
2631         D_SELECT((stderr, "rxvt_selection_request_other: pasting %s", debug_xa_names[selnum]));
2632         XConvertSelection(R->Xdisplay, sel, target, R->xa[XA_VT_SELECTION],
2633                           R->TermWin.vt, R->selection_request_time);
2634         return 1;
2635     }
2636     return 0;
2637 }
2638
2639 /* ------------------------------------------------------------------------- */
2640 /*
2641  * Clear all selected text
2642  * EXT: SelectionClear
2643  */
2644 /* EXTPROTO */
2645 void
2646 rxvt_selection_clear(pR)
2647 {
2648     D_SELECT((stderr, "rxvt_selection_clear()"));
2649
2650     R->want_refresh = 1;
2651     if (R->selection.text)
2652         free(R->selection.text);
2653     R->selection.text = NULL;
2654     R->selection.len = 0;
2655     CLEAR_SELECTION(R);
2656 }
2657
2658 /* ------------------------------------------------------------------------- */
2659 /*
2660  * Copy a selection into the cut buffer
2661  * EXT: button 1 or 3 release
2662  */
2663 /* EXTPROTO */
2664 void
2665 rxvt_selection_make(pR_ Time tm)
2666 {
2667     int             i, col, end_col, row, end_row;
2668     unsigned char  *new_selection_text;
2669     char           *str;
2670     text_t         *t;
2671 #ifdef ACS_ASCII
2672     rend_t         *re;
2673 #endif
2674
2675     D_SELECT((stderr, "rxvt_selection_make(): R->selection.op=%d, R->selection.clicks=%d", R->selection.op, R->selection.clicks));
2676     switch (R->selection.op) {
2677     case SELECTION_CONT:
2678         break;
2679     case SELECTION_INIT:
2680         CLEAR_SELECTION(R);
2681     /* FALLTHROUGH */
2682     case SELECTION_BEGIN:
2683         R->selection.op = SELECTION_DONE;
2684     /* FALLTHROUGH */
2685     default:
2686         return;
2687     }
2688     R->selection.op = SELECTION_DONE;
2689
2690     if (R->selection.clicks == 4)
2691         return;                 /* nothing selected, go away */
2692
2693     i = (R->selection.end.row - R->selection.beg.row + 1) * (R->TermWin.ncol + 1) + 1;
2694     str = (char *)rxvt_malloc(i * MB_CUR_MAX + 1);
2695
2696     new_selection_text = (unsigned char *)str;
2697
2698     col = R->selection.beg.col;
2699     MAX_IT(col, 0);
2700     row = R->selection.beg.row + R->TermWin.saveLines;
2701     end_row = R->selection.end.row + R->TermWin.saveLines;
2702
2703     for (; row <= end_row; row++, col = 0)
2704       {
2705         t = &(R->screen.text[row][col]);
2706
2707         end_col = R->screen.tlen[row];
2708
2709         if (end_col == -1)
2710           end_col = R->TermWin.ncol;
2711
2712         if (row == end_row)
2713           MIN_IT (end_col, R->selection.end.col);
2714
2715         for (; col < end_col; col++)
2716           if (*t == NOCHAR)
2717             t++;
2718           else
2719             {
2720               int len = wctomb (str, *t++);
2721               if (len > 0)
2722                 str += len;
2723             }
2724
2725         if (R->screen.tlen[row] != -1 && row != end_row)
2726             *str++ = '\n';
2727       }
2728
2729 #ifndef NO_OLD_SELECTION
2730     if (R->selection_style == OLD_SELECT)
2731         if (end_col == R->TermWin.ncol)
2732             *str++ = '\n';
2733 #endif
2734 #ifndef NO_NEW_SELECTION
2735     if (R->selection_style != OLD_SELECT)
2736         if (end_col != R->selection.end.col)
2737             *str++ = '\n';
2738 #endif
2739     *str = '\0';
2740
2741     i = str - (char *)new_selection_text;
2742     if (i == 0)
2743       {
2744         free (new_selection_text);
2745         return;
2746       }
2747
2748     // due to MB_MAX_CUR, selection wastage is usually high
2749     if (str - (char *)new_selection_text > 1024)
2750       new_selection_text = (unsigned char *)rxvt_realloc (new_selection_text, i + 1);
2751
2752     R->selection.len = i;
2753     if (R->selection.text)
2754         free (R->selection.text);
2755
2756     R->selection.text = new_selection_text;
2757
2758     XSetSelectionOwner(R->Xdisplay, XA_PRIMARY, R->TermWin.vt, tm);
2759     if (XGetSelectionOwner(R->Xdisplay, XA_PRIMARY) != R->TermWin.vt)
2760         rxvt_print_error("can't get primary selection");
2761     XChangeProperty(R->Xdisplay, Xroot, XA_CUT_BUFFER0, XA_STRING, 8,
2762                     PropModeReplace, R->selection.text, (int)R->selection.len);
2763     R->selection_time = tm;
2764     D_SELECT((stderr, "rxvt_selection_make(): R->selection.len=%d", R->selection.len));
2765 }
2766
2767 /* ------------------------------------------------------------------------- */
2768 /*
2769  * Mark or select text based upon number of clicks: 1, 2, or 3
2770  * EXT: button 1 press
2771  */
2772 /* EXTPROTO */
2773 void
2774 rxvt_selection_click(pR_ int clicks, int x, int y)
2775 {
2776     D_SELECT((stderr, "rxvt_selection_click(%d, %d, %d)", clicks, x, y));
2777
2778     clicks = ((clicks - 1) % 3) + 1;
2779     R->selection.clicks = clicks;       /* save clicks so extend will work */
2780
2781     rxvt_selection_start_colrow(aR_ Pixel2Col(x), Pixel2Row(y));
2782     if (clicks == 2 || clicks == 3)
2783         rxvt_selection_extend_colrow(aR_ R->selection.mark.col,
2784                                      R->selection.mark.row
2785                                      + R->TermWin.view_start,
2786                                      0, /* button 3     */
2787                                      1, /* button press */
2788                                      0);        /* click change */
2789 }
2790
2791 /* ------------------------------------------------------------------------- */
2792 /*
2793  * Mark a selection at the specified col/row
2794  */
2795 /* INTPROTO */
2796 void
2797 rxvt_selection_start_colrow(pR_ int col, int row)
2798 {
2799     R->want_refresh = 1;
2800     R->selection.mark.col = col;
2801     R->selection.mark.row = row - R->TermWin.view_start;
2802     MAX_IT(R->selection.mark.row, -(int32_t)R->TermWin.nscrolled);
2803     MIN_IT(R->selection.mark.row, (int32_t)R->TermWin.nrow - 1);
2804     MAX_IT(R->selection.mark.col, 0);
2805     MIN_IT(R->selection.mark.col, (int32_t)R->TermWin.ncol - 1);
2806
2807     if (R->selection.op) {      /* clear the old selection */
2808         R->selection.beg.row = R->selection.end.row = R->selection.mark.row;
2809         R->selection.beg.col = R->selection.end.col = R->selection.mark.col;
2810     }
2811     R->selection.op = SELECTION_INIT;
2812     R->selection.screen = R->current_screen;
2813 }
2814
2815 /* ------------------------------------------------------------------------- */
2816 /*
2817  * Word select: select text for 2 clicks
2818  * We now only find out the boundary in one direction
2819  */
2820
2821 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
2822 #define DELIMIT_TEXT(x) \
2823     (((x) == ' ' || (x) == '\t') ? 2 : (STRCHR(R->rs[Rs_cutchars], (x)) != NULL))
2824 #ifdef MULTICHAR_SET
2825 # define DELIMIT_REND(x)        (((x) & RS_multiMask) ? 1 : 0)
2826 #else
2827 # define DELIMIT_REND(x)        1
2828 #endif
2829
2830 /* INTPROTO */
2831 void
2832 rxvt_selection_delimit_word(pR_ enum page_dirn dirn, const row_col_t *mark, row_col_t *ret)
2833 {
2834     int             col, row, dirnadd, tcol, trow, w1, w2;
2835     row_col_t       bound;
2836     text_t         *stp;
2837     rend_t         *srp;
2838
2839     if (dirn == UP) {
2840         bound.row = R->TermWin.saveLines - R->TermWin.nscrolled - 1;
2841         bound.col = 0;
2842         dirnadd = -1;
2843     } else {
2844         bound.row = R->TermWin.saveLines + R->TermWin.nrow;
2845         bound.col = R->TermWin.ncol - 1;
2846         dirnadd = 1;
2847     }
2848     row = mark->row + R->TermWin.saveLines;
2849     col = mark->col;
2850     MAX_IT(col, 0);
2851 /* find the edge of a word */
2852     stp = &(R->screen.text[row][col]);
2853     w1 = DELIMIT_TEXT(*stp);
2854
2855     if (R->selection_style != NEW_SELECT) {
2856         if (w1 == 1) {
2857             stp += dirnadd;
2858             if (DELIMIT_TEXT(*stp) == 1)
2859                 goto Old_Word_Selection_You_Die;
2860             col += dirnadd;
2861         }
2862         w1 = 0;
2863     }
2864     srp = (&R->screen.rend[row][col]);
2865     w2 = DELIMIT_REND(*srp);
2866
2867     for (;;) {
2868         for (; col != bound.col; col += dirnadd) {
2869             stp += dirnadd;
2870             if (DELIMIT_TEXT(*stp) != w1)
2871                 break;
2872             srp += dirnadd;
2873             if (DELIMIT_REND(*srp) != w2)
2874                 break;
2875         }
2876         if ((col == bound.col) && (row != bound.row)) {
2877             if (R->screen.tlen[(row - (dirn == UP ? 1 : 0))] == -1) {
2878                 trow = row + dirnadd;
2879                 tcol = dirn == UP ? R->TermWin.ncol - 1 : 0;
2880                 if (R->screen.text[trow] == NULL)
2881                     break;
2882                 stp = &(R->screen.text[trow][tcol]);
2883                 srp = &(R->screen.rend[trow][tcol]);
2884                 if (DELIMIT_TEXT(*stp) != w1 || DELIMIT_REND(*srp) != w2)
2885                     break;
2886                 row = trow;
2887                 col = tcol;
2888                 continue;
2889             }
2890         }
2891         break;
2892     }
2893   Old_Word_Selection_You_Die:
2894     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 - R->TermWin.saveLines, col));
2895
2896     if (dirn == DN)
2897         col++;                  /* put us on one past the end */
2898
2899 /* Poke the values back in */
2900     ret->row = row - R->TermWin.saveLines;
2901     ret->col = col;
2902 }
2903
2904 /* ------------------------------------------------------------------------- */
2905 /*
2906  * Extend the selection to the specified x/y pixel location
2907  * EXT: button 3 press; button 1 or 3 drag
2908  * flag == 0 ==> button 1
2909  * flag == 1 ==> button 3 press
2910  * flag == 2 ==> button 3 motion
2911  */
2912 /* EXTPROTO */
2913 void
2914 rxvt_selection_extend(pR_ int x, int y, int flag)
2915 {
2916     int             col, row;
2917
2918     col = Pixel2Col(x);
2919     row = Pixel2Row(y);
2920     MAX_IT(row, 0);
2921     MIN_IT(row, (int)R->TermWin.nrow - 1);
2922     MAX_IT(col, 0);
2923     MIN_IT(col, (int)R->TermWin.ncol);
2924 #ifndef NO_NEW_SELECTION
2925 /*
2926  * If we're selecting characters (single click) then we must check first
2927  * if we are at the same place as the original mark.  If we are then
2928  * select nothing.  Otherwise, if we're to the right of the mark, you have to
2929  * be _past_ a character for it to be selected.
2930  */
2931     if (R->selection_style != OLD_SELECT) {
2932         if (((R->selection.clicks % 3) == 1) && !flag
2933             && (col == R->selection.mark.col
2934                 && (row == R->selection.mark.row + R->TermWin.view_start))) {
2935             /* select nothing */
2936             R->selection.beg.row = R->selection.end.row = 0;
2937             R->selection.beg.col = R->selection.end.col = 0;
2938             R->selection.clicks = 4;
2939             R->want_refresh = 1;
2940             D_SELECT((stderr, "rxvt_selection_extend() R->selection.clicks = 4"));
2941             return;
2942         }
2943     }
2944 #endif
2945     if (R->selection.clicks == 4)
2946         R->selection.clicks = 1;
2947     rxvt_selection_extend_colrow(aR_ col, row, !!flag,  /* ? button 3      */
2948                                  flag == 1 ? 1 : 0,     /* ? button press  */
2949                                  0);    /* no click change */
2950 }
2951
2952 /* ------------------------------------------------------------------------- */
2953 /*
2954  * Extend the selection to the specified col/row
2955  */
2956 /* INTPROTO */
2957 void
2958 rxvt_selection_extend_colrow(pR_ int32_t col, int32_t row, int button3, int buttonpress, int clickchange)
2959 {
2960     int16_t         ncol = R->TermWin.ncol;
2961     int             end_col;
2962     row_col_t       pos;
2963     enum {
2964         LEFT, RIGHT
2965     } closeto = RIGHT;
2966
2967     D_SELECT((stderr, "rxvt_selection_extend_colrow(c:%d, r:%d, %d, %d) clicks:%d, op:%d", col, row, button3, buttonpress, R->selection.clicks, R->selection.op));
2968     D_SELECT((stderr, "rxvt_selection_extend_colrow() ENT  b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)", R->selection.beg.row, R->selection.beg.col, R->selection.mark.row, R->selection.mark.col, R->selection.end.row, R->selection.end.col));
2969
2970     R->want_refresh = 1;
2971     switch (R->selection.op) {
2972     case SELECTION_INIT:
2973         CLEAR_SELECTION(R);
2974         R->selection.op = SELECTION_BEGIN;
2975     /* FALLTHROUGH */
2976     case SELECTION_BEGIN:
2977         if (row != R->selection.mark.row || col != R->selection.mark.col
2978             || (!button3 && buttonpress))
2979             R->selection.op = SELECTION_CONT;
2980         break;
2981     case SELECTION_DONE:
2982         R->selection.op = SELECTION_CONT;
2983     /* FALLTHROUGH */
2984     case SELECTION_CONT:
2985         break;
2986     case SELECTION_CLEAR:
2987         rxvt_selection_start_colrow(aR_ col, row);
2988     /* FALLTHROUGH */
2989     default:
2990         return;
2991     }
2992     if (R->selection.beg.col == R->selection.end.col
2993         && R->selection.beg.col != R->selection.mark.col
2994         && R->selection.beg.row == R->selection.end.row
2995         && R->selection.beg.row != R->selection.mark.row) {
2996         R->selection.beg.col = R->selection.end.col = R->selection.mark.col;
2997         R->selection.beg.row = R->selection.end.row = R->selection.mark.row;
2998         D_SELECT((stderr, "rxvt_selection_extend_colrow() ENT2 b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)", R->selection.beg.row, R->selection.beg.col, R->selection.mark.row, R->selection.mark.col, R->selection.end.row, R->selection.end.col));
2999     }
3000
3001     pos.col = col;
3002     pos.row = row;
3003
3004     pos.row -= R->TermWin.view_start;   /* adjust for scroll */
3005
3006 #ifndef NO_OLD_SELECTION
3007 /*
3008  * This mimics some of the selection behaviour of version 2.20 and before.
3009  * There are no ``selection modes'', button3 is always character extension.
3010  * Note: button3 drag is always available, c.f. v2.20
3011  * Selection always terminates (left or right as appropriate) at the mark.
3012  */
3013     if (R->selection_style == OLD_SELECT) {
3014         if (R->selection.clicks == 1 || button3) {
3015             if (R->hate_those_clicks) {
3016                 R->hate_those_clicks = 0;
3017                 if (R->selection.clicks == 1) {
3018                     R->selection.beg.row = R->selection.mark.row;
3019                     R->selection.beg.col = R->selection.mark.col;
3020                 } else {
3021                     R->selection.mark.row = R->selection.beg.row;
3022                     R->selection.mark.col = R->selection.beg.col;
3023                 }
3024             }
3025             if (ROWCOL_IS_BEFORE(pos, R->selection.mark)) {
3026                 R->selection.end.row = R->selection.mark.row;
3027                 R->selection.end.col = R->selection.mark.col + 1;
3028                 R->selection.beg.row = pos.row;
3029                 R->selection.beg.col = pos.col;
3030             } else {
3031                 R->selection.beg.row = R->selection.mark.row;
3032                 R->selection.beg.col = R->selection.mark.col;
3033                 R->selection.end.row = pos.row;
3034                 R->selection.end.col = pos.col + 1;
3035             }
3036         } else if (R->selection.clicks == 2) {
3037             rxvt_selection_delimit_word(aR_ UP, &(R->selection.mark),
3038                                         &(R->selection.beg));
3039             rxvt_selection_delimit_word(aR_ DN, &(R->selection.mark),
3040                                         &(R->selection.end));
3041             R->hate_those_clicks = 1;
3042         } else if (R->selection.clicks == 3) {
3043             R->selection.beg.row = R->selection.end.row = R->selection.mark.row;
3044             R->selection.beg.col = 0;
3045             R->selection.end.col = ncol;
3046             R->hate_those_clicks = 1;
3047         }
3048         D_SELECT((stderr, "rxvt_selection_extend_colrow() EXIT b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)", R->selection.beg.row, R->selection.beg.col, R->selection.mark.row, R->selection.mark.col, R->selection.end.row, R->selection.end.col));
3049         return;
3050     }
3051 #endif                          /* ! NO_OLD_SELECTION */
3052 #ifndef NO_NEW_SELECTION
3053 /* selection_style must not be OLD_SELECT to get here */
3054 /*
3055  * This is mainly xterm style selection with a couple of differences, mainly
3056  * in the way button3 drag extension works.
3057  * We're either doing: button1 drag; button3 press; or button3 drag
3058  *  a) button1 drag : select around a midpoint/word/line - that point/word/line
3059  *     is always at the left/right edge of the R->selection.
3060  *  b) button3 press: extend/contract character/word/line at whichever edge of
3061  *     the selection we are closest to.
3062  *  c) button3 drag : extend/contract character/word/line - we select around
3063  *     a point/word/line which is either the start or end of the selection
3064  *     and it was decided by whichever point/word/line was `fixed' at the
3065  *     time of the most recent button3 press
3066  */
3067     if (button3 && buttonpress) {       /* button3 press */
3068         /*
3069          * first determine which edge of the selection we are closest to
3070          */
3071         if (ROWCOL_IS_BEFORE(pos, R->selection.beg)
3072             || (!ROWCOL_IS_AFTER(pos, R->selection.end)
3073                 && (((pos.col - R->selection.beg.col)
3074                      + ((pos.row - R->selection.beg.row) * ncol))
3075                     < ((R->selection.end.col - pos.col)
3076                        + ((R->selection.end.row - pos.row) * ncol)))))
3077              closeto = LEFT;
3078         if (closeto == LEFT) {
3079             R->selection.beg.row = pos.row;
3080             R->selection.beg.col = pos.col;
3081             R->selection.mark.row = R->selection.end.row;
3082             R->selection.mark.col = R->selection.end.col
3083                                     - (R->selection.clicks == 2);
3084         } else {
3085             R->selection.end.row = pos.row;
3086             R->selection.end.col = pos.col;
3087             R->selection.mark.row = R->selection.beg.row;
3088             R->selection.mark.col = R->selection.beg.col;
3089         }
3090     } else {                    /* button1 drag or button3 drag */
3091         if (ROWCOL_IS_AFTER(R->selection.mark, pos)) {
3092             if ((R->selection.mark.row == R->selection.end.row)
3093                 && (R->selection.mark.col == R->selection.end.col)
3094                 && clickchange && R->selection.clicks == 2)
3095                 R->selection.mark.col--;
3096             R->selection.beg.row = pos.row;
3097             R->selection.beg.col = pos.col;
3098             R->selection.end.row = R->selection.mark.row;
3099             R->selection.end.col = R->selection.mark.col
3100                                    + (R->selection.clicks == 2);
3101         } else {
3102             R->selection.beg.row = R->selection.mark.row;
3103             R->selection.beg.col = R->selection.mark.col;
3104             R->selection.end.row = pos.row;
3105             R->selection.end.col = pos.col;
3106         }
3107     }
3108
3109     if (R->selection.clicks == 1) {
3110         end_col = R->screen.tlen[R->selection.beg.row + R->TermWin.saveLines];
3111         if (end_col != -1 && R->selection.beg.col > end_col) {
3112 #if 1
3113             R->selection.beg.col = ncol;
3114 #else
3115             if (R->selection.beg.row != R->selection.end.row)
3116                 R->selection.beg.col = ncol;
3117             else
3118                 R->selection.beg.col = R->selection.mark.col;
3119 #endif
3120         }
3121         end_col = R->screen.tlen[R->selection.end.row + R->TermWin.saveLines];
3122         if (end_col != -1 && R->selection.end.col > end_col)
3123             R->selection.end.col = ncol;
3124
3125     } else if (R->selection.clicks == 2) {
3126         if (ROWCOL_IS_AFTER(R->selection.end, R->selection.beg))
3127             R->selection.end.col--;
3128         rxvt_selection_delimit_word(aR_ UP, &(R->selection.beg),
3129                                     &(R->selection.beg));
3130         rxvt_selection_delimit_word(aR_ DN, &(R->selection.end),
3131                                     &(R->selection.end));
3132     } else if (R->selection.clicks == 3) {
3133 #ifndef NO_FRILLS
3134         if ((R->Options & Opt_tripleclickwords)) {
3135             int             end_row;
3136
3137             rxvt_selection_delimit_word(aR_ UP, &(R->selection.beg),
3138                                         &(R->selection.beg));
3139             end_row = R->screen.tlen[R->selection.mark.row
3140                                      + R->TermWin.saveLines];
3141             for (end_row = R->selection.mark.row; end_row < R->TermWin.nrow;
3142                  end_row++) {
3143                 end_col = R->screen.tlen[end_row + R->TermWin.saveLines];
3144                 if (end_col != -1) {
3145                     R->selection.end.row = end_row;
3146                     R->selection.end.col = end_col;
3147                     rxvt_selection_remove_trailing_spaces(aR);
3148                     break;
3149                 }
3150             }
3151         } else
3152 #endif
3153         {
3154             if (ROWCOL_IS_AFTER(R->selection.mark, R->selection.beg))
3155                 R->selection.mark.col++;
3156             R->selection.beg.col = 0;
3157             R->selection.end.col = ncol;
3158         }
3159     }
3160     if (button3 && buttonpress) {       /* mark may need to be changed */
3161         if (closeto == LEFT) {
3162             R->selection.mark.row = R->selection.end.row;
3163             R->selection.mark.col = R->selection.end.col
3164                                     - (R->selection.clicks == 2);
3165         } else {
3166             R->selection.mark.row = R->selection.beg.row;
3167             R->selection.mark.col = R->selection.beg.col;
3168         }
3169     }
3170     D_SELECT((stderr, "rxvt_selection_extend_colrow() EXIT b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)", R->selection.beg.row, R->selection.beg.col, R->selection.mark.row, R->selection.mark.col, R->selection.end.row, R->selection.end.col));
3171 #endif                          /* ! NO_NEW_SELECTION */
3172 }
3173
3174 #ifndef NO_FRILLS
3175 /* INTPROTO */
3176 void
3177 rxvt_selection_remove_trailing_spaces(pR)
3178 {
3179     int32_t         end_col, end_row;
3180     text_t         *stp; 
3181
3182     end_col = R->selection.end.col;
3183     end_row = R->selection.end.row;
3184     for ( ; end_row >= R->selection.beg.row; ) {
3185         stp = R->screen.text[end_row + R->TermWin.saveLines];
3186         while (--end_col >= 0) {
3187             if (stp[end_col] != ' ' && stp[end_col] != '\t')
3188                 break;
3189         }
3190         if (end_col >= 0
3191             || R->screen.tlen[end_row - 1 + R->TermWin.saveLines] != -1) {
3192             R->selection.end.col = end_col + 1;
3193             R->selection.end.row = end_row;
3194             break;
3195         }
3196         end_row--;
3197         end_col = R->TermWin.ncol;
3198     }
3199     if (R->selection.mark.row > R->selection.end.row) {
3200         R->selection.mark.row = R->selection.end.row;
3201         R->selection.mark.col = R->selection.end.col;
3202     } else if (R->selection.mark.row == R->selection.end.row
3203                && R->selection.mark.col > R->selection.end.col)
3204         R->selection.mark.col = R->selection.end.col;
3205 }
3206 #endif
3207
3208 /* ------------------------------------------------------------------------- */
3209 /*
3210  * Double click on button 3 when already selected
3211  * EXT: button 3 double click
3212  */
3213 /* EXTPROTO */
3214 void
3215 rxvt_selection_rotate(pR_ int x, int y)
3216 {
3217     R->selection.clicks = R->selection.clicks % 3 + 1;
3218     rxvt_selection_extend_colrow(aR_ Pixel2Col(x), Pixel2Row(y), 1, 0, 1);
3219 }
3220
3221 /* ------------------------------------------------------------------------- */
3222 /*
3223  * On some systems, the Atom typedef is 64 bits wide.  We need to have a type
3224  * that is exactly 32 bits wide, because a format of 64 is not allowed by
3225  * the X11 protocol.
3226  */
3227 typedef CARD32  Atom32;
3228
3229 /* ------------------------------------------------------------------------- */
3230 /*
3231  * Respond to a request for our current selection
3232  * EXT: SelectionRequest
3233  */
3234 /* EXTPROTO */
3235 void
3236 rxvt_selection_send(pR_ const XSelectionRequestEvent *rq)
3237 {
3238     XSelectionEvent ev;
3239 #ifdef USE_XIM
3240     Atom32          target_list[4];
3241 #else
3242     Atom32          target_list[3];
3243 #endif
3244     Atom            target;
3245     XTextProperty   ct;
3246     XICCEncodingStyle style;
3247     char           *cl[2], dummy[1];
3248
3249     ev.type = SelectionNotify;
3250     ev.property = None;
3251     ev.display = rq->display;
3252     ev.requestor = rq->requestor;
3253     ev.selection = rq->selection;
3254     ev.target = rq->target;
3255     ev.time = rq->time;
3256
3257     if (rq->target == R->xa[XA_TARGETS]) {
3258         target_list[0] = (Atom32) R->xa[XA_TARGETS];
3259         target_list[1] = (Atom32) XA_STRING;
3260         target_list[2] = (Atom32) R->xa[XA_TEXT];
3261 #ifdef USE_XIM
3262         target_list[3] = (Atom32) R->xa[XA_COMPOUND_TEXT];
3263 #endif
3264         XChangeProperty(R->Xdisplay, rq->requestor, rq->property, XA_ATOM,
3265                         (8 * sizeof(target_list[0])), PropModeReplace,
3266                         (unsigned char *)target_list,
3267                         (sizeof(target_list) / sizeof(target_list[0])));
3268         ev.property = rq->property;
3269     } else if (rq->target == R->xa[XA_MULTIPLE]) {
3270         /* TODO: Handle MULTIPLE */
3271     } else if (rq->target == R->xa[XA_TIMESTAMP] && R->selection.text) {
3272         XChangeProperty(R->Xdisplay, rq->requestor, rq->property, XA_INTEGER,
3273                         (8 * sizeof(Time)), PropModeReplace,
3274                         (unsigned char *)&R->selection_time, 1);
3275         ev.property = rq->property;
3276     } else if (rq->target == XA_STRING
3277                || rq->target == R->xa[XA_COMPOUND_TEXT]
3278                || rq->target == R->xa[XA_TEXT]) {
3279 #ifdef USE_XIM
3280         short           freect = 0;
3281 #endif
3282         int             selectlen;
3283
3284 #ifdef USE_XIM
3285         if (rq->target != XA_STRING) {
3286             target = R->xa[XA_COMPOUND_TEXT];
3287             style = (rq->target == R->xa[XA_COMPOUND_TEXT])
3288                     ? XCompoundTextStyle : XStdICCTextStyle;
3289         } else
3290 #endif
3291         {
3292             target = XA_STRING;
3293             style = XStringStyle;
3294         }
3295         if (R->selection.text) {
3296             cl[0] = (char *)R->selection.text;
3297             selectlen = R->selection.len;
3298         } else {
3299             cl[0] = dummy;
3300             *dummy = '\0';
3301             selectlen = 0;
3302         }
3303 #ifdef USE_XIM
3304         if (XmbTextListToTextProperty(R->Xdisplay, cl, 1, style, &ct)
3305             == Success)         /* if we failed to convert then send it raw */
3306             freect = 1;
3307         else
3308 #endif
3309         {
3310             ct.value = (unsigned char *)cl[0];
3311             ct.nitems = selectlen;
3312         }
3313         XChangeProperty(R->Xdisplay, rq->requestor, rq->property,
3314                         target, 8, PropModeReplace,
3315                         ct.value, (int)ct.nitems);
3316         ev.property = rq->property;
3317 #ifdef USE_XIM
3318         if (freect)
3319             XFree(ct.value);
3320 #endif
3321     }
3322     XSendEvent(R->Xdisplay, rq->requestor, False, 0L, (XEvent *)&ev);
3323 }
3324 \f
3325 /* ------------------------------------------------------------------------- *
3326  *                              MOUSE ROUTINES                               *
3327  * ------------------------------------------------------------------------- */
3328
3329 /*
3330  * return col/row values corresponding to x/y pixel values
3331  */
3332 /* EXTPROTO */
3333 void
3334 rxvt_pixel_position(pR_ int *x, int *y)
3335 {
3336     *x = Pixel2Col(*x);
3337 /* MAX_IT(*x, 0); MIN_IT(*x, (int)R->TermWin.ncol - 1); */
3338     *y = Pixel2Row(*y);
3339 /* MAX_IT(*y, 0); MIN_IT(*y, (int)R->TermWin.nrow - 1); */
3340 }
3341 /* ------------------------------------------------------------------------- */
3342 #ifdef USE_XIM
3343 /* EXTPROTO */
3344 void
3345 rxvt_setPosition(pR_ XPoint *pos)
3346 {
3347     XWindowAttributes xwa;
3348
3349     XGetWindowAttributes(R->Xdisplay, R->TermWin.vt, &xwa);
3350     pos->x = Col2Pixel(R->screen.cur.col) + xwa.x;
3351     pos->y = Height2Pixel((R->screen.cur.row + 1)) + xwa.y
3352              - R->TermWin.lineSpace;
3353 }
3354 #endif
3355 /* ------------------------------------------------------------------------- */
3356 \f
3357 /* ------------------------------------------------------------------------- *
3358  *                              DEBUG ROUTINES                               *
3359  * ------------------------------------------------------------------------- */
3360 #if 0
3361 /* INTPROTO */
3362 void
3363 rxvt_debug_colors(void)
3364 {
3365     int             color;
3366     const char     *name[] = {
3367         "fg", "bg",
3368         "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
3369     };
3370
3371     fprintf(stderr, "Color ( ");
3372     if (R->rstyle & RS_RVid)
3373         fprintf(stderr, "rvid ");
3374     if (R->rstyle & RS_Bold)
3375         fprintf(stderr, "bold ");
3376     if (R->rstyle & RS_Blink)
3377         fprintf(stderr, "blink ");
3378     if (R->rstyle & RS_Uline)
3379         fprintf(stderr, "uline ");
3380     fprintf(stderr, "): ");
3381
3382     color = GET_FGCOLOR(R->rstyle);
3383 #ifndef NO_BRIGHTCOLOR
3384     if (color >= minBrightCOLOR && color <= maxBrightCOLOR) {
3385         color -= (minBrightCOLOR - minCOLOR);
3386         fprintf(stderr, "bright ");
3387     }
3388 #endif
3389     fprintf(stderr, "%s on ", name[color]);
3390
3391     color = GET_BGCOLOR(R->rstyle);
3392 #ifndef NO_BRIGHTCOLOR
3393     if (color >= minBrightCOLOR && color <= maxBrightCOLOR) {
3394         color -= (minBrightCOLOR - minCOLOR);
3395         fprintf(stderr, "bright ");
3396     }
3397 #endif
3398     fprintf(stderr, "%s\n", name[color]);
3399 }
3400 #endif