1 /*--------------------------------*-C-*---------------------------------*
2  * File:    screen.c
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 1997-2001   Geoff Wing <gcw@pobox.com>
7  * Copyright (C) 2000,2001   Teepanis Chachiyo <teepanis@physics.purdue.edu>
8  * Copyright (c) 2001        Marius Gedminas <marius.gedminas@uosis.mif.vu.lt>
9  * Copyright (c) 2003        David Hull
10  * Copyright (c) 2003        Yamanobe Kiichiro <yamky@cocoa.freemail.ne.jp>
11  * Copyright (c) 2003        Mamoru Komachi <usata@usata.org>
12  * Copyright (c) 2005        William P. Y. Hadisoeseno <williampoetra@users.sourceforge.net>
13  * Copyright (c) 2004-2006   Jingmin Zhou <jimmyzhou@users.sourceforge.net>
14  * Copyright (c) 2005-2006   Gautam Iyer <gi1242@users.sourceforge.net>
15  * Copyright (c) 2007		  Jehan Hysseo <hysseo@users.sourceforge.net>
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 2 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30  *----------------------------------------------------------------------*/
31 
32 #include "../config.h"
33 #define INTERN_SCREEN
34 #include "rxvt.h"
35 
36 #ifdef XFT_SUPPORT
37 # include <xftacs.h>
38 #endif
39 
40 #ifdef HAVE_WORDEXP_H
41 # include <wordexp.h>
42 #endif
43 
44 /* ------------------------------------------------------------------------- */
45 #ifdef MULTICHAR_SET
46 #define RESET_CHSTAT(R, P)		    \
47     if (PVTS((R),(P))->chstat == WBYTE)	\
48 	PVTS((R),(P))->chstat = SBYTE, PVTS((R),(P))->lost_multi = 1
49 #else
50 # define RESET_CHSTAT(R, P)
51 #endif
52 
53 /* ------------------------------------------------------------------------- */
54 #define PROP_SIZE	16384
55 
56 /* ------------------------------------------------------------------------- *
57  *	     GENERAL SCREEN AND SELECTION UPDATE ROUTINES		  *
58  * ------------------------------------------------------------------------- */
59 
60 /*
61 ** If inhibit scrolling on tty output, we should keep the view_start.
62 ** Otherwise, we set it to zero.
63 */
64 #define ZERO_SCROLLBACK(R, P)			    \
65     if (NOTSET_OPTION(R, Opt_scrollTtyOutputInhibit))	\
66 	(R)->vts[(P)]->view_start = 0
67 
68 #define CLEAR_SELECTION(R)			\
69     (R)->selection.beg.row = \
70     (R)->selection.beg.col = \
71     (R)->selection.end.row = \
72     (R)->selection.end.col = 0
73 
74 #define CLEAR_ALL_SELECTION(R)			\
75     (R)->selection.beg.row = \
76     (R)->selection.beg.col = \
77     (R)->selection.mark.row = \
78     (R)->selection.mark.col = \
79     (R)->selection.end.row = \
80     (R)->selection.end.col = 0
81 
82 #define ROW_AND_COL_IS_AFTER(A, B, C, D)		\
83     (((A) > (C)) || (((A) == (C)) && ((B) > (D))))
84 #define ROW_AND_COL_IS_BEFORE(A, B, C, D)		\
85     (((A) < (C)) || (((A) == (C)) && ((B) < (D))))
86 #define ROW_AND_COL_IN_ROW_AFTER(A, B, C, D)		\
87     (((A) == (C)) && ((B) > (D)))
88 #define ROW_AND_COL_IN_ROW_AT_OR_AFTER(A, B, C, D)	\
89     (((A) == (C)) && ((B) >= (D)))
90 #define ROW_AND_COL_IN_ROW_BEFORE(A, B, C, D)		\
91     (((A) == (C)) && ((B) < (D)))
92 #define ROW_AND_COL_IN_ROW_AT_OR_BEFORE(A, B, C, D)	\
93     (((A) == (C)) && ((B) <= (D)))
94 
95 /* these must be row_col_t */
96 #define RC_AFTER(X, Y)			    \
97     ROW_AND_COL_IS_AFTER((X).row, (X).col, (Y).row, (Y).col)
98 #define RC_BEFORE(X, Y)			    \
99     ROW_AND_COL_IS_BEFORE((X).row, (X).col, (Y).row, (Y).col)
100 #define RC_ROW_AFTER(X, Y)		    \
101     ROW_AND_COL_IN_ROW_AFTER((X).row, (X).col, (Y).row, (Y).col)
102 #define RC_ROW_BEFORE(X, Y)		    \
103     ROW_AND_COL_IN_ROW_BEFORE((X).row, (X).col, (Y).row, (Y).col)
104 #define RC_ROW_ATAFTER(X, Y)			\
105     ROW_AND_COL_IN_ROW_AT_OR_AFTER((X).row, (X).col, (Y).row, (Y).col)
106 #define RC_ROW_ATBEFORE(X, Y)		    \
107     ROW_AND_COL_IN_ROW_AT_OR_BEFORE((X).row, (X).col, (Y).row, (Y).col)
108 
109 /*
110  * CLEAR_ROWS : clear <num> rows starting from row <row>
111  * CLEAR_CHARS: clear <num> chars starting from pixel position <x,y>
112  * ERASE_ROWS : set <num> rows starting from row <row> to the foreground colour
113  */
114 #define drawBuffer  (PVTS(r, page)->vt)
115 
116 #define CLEAR_ROWS(row, num)				\
117     if (r->TermWin.mapped)				\
118 	rxvt_clear_area (r, page,			\
119 	    r->TermWin.int_bwidth, Row2Pixel(row),	\
120 	    VT_WIDTH(r), (unsigned int)Height2Pixel(num))
121 
122 
123 /*
124  * If already_cleared, then we got here during a clipped refresh. In this case,
125  * areas we draw into are already cleared by the server. We don't need to clear
126  * it ourself. (Doing so will cause problems under transparency: E.g. We want to
127  * clear an entire character cell, but an expose event was generated for only
128  * half the character cell. By our wonderful clippings, we redraw only half the
129  * char cell. However requests to this function will clear an ENTIRE char cell,
130  * causing problems.
131  *
132  * If garbage is found due to clipped refreshes, then we should explicitly call
133  * XClearArea before generating a clipped refresh.
134  */
135 #define CLEAR_CHARS(r, page, already_cleared, x, y, num)	\
136 	if( !already_cleared )					\
137 	    rxvt_clear_area( (r), (page), (x), (y),		\
138 		(unsigned) Width2Pixel((num)),			\
139 		(unsigned) Height2Pixel(1));
140 
141 
142 #define ERASE_ROWS(row, num)			    \
143     rxvt_fill_rectangle (r, page,		    \
144 	   r->TermWin.int_bwidth, Row2Pixel(row),   \
145 	   VT_WIDTH(r),	(unsigned int)Height2Pixel(num))
146 
147 
148 #ifdef DONT_SELECT_TRAILING_SPACES
149 # define STRIP_TRAILING_SPACE(str, fence)	    \
150     while (str > fence && ' ' == str[-1])	    \
151 	str --;
152 #endif
153 
154 
155 /* Here are some simple macros for convenience */
156 #undef CURROW
157 #undef CURCOL
158 #undef SVLINES
159 #undef VSTART
160 #define CURROW	    (PSCR(r, page).cur.row)
161 #define CURCOL	    (PSCR(r, page).cur.col)
162 #define SVLINES	    (PVTS(r, page)->saveLines)
163 #define VSTART	    (PVTS(r, page)->view_start)
164 
165 
166 /*--------------------------------------------------------------------*
167  *         BEGIN `INTERNAL' ROUTINE PROTOTYPES                        *
168  *--------------------------------------------------------------------*/
169 void rxvt_blank_line              (text_t*, rend_t*, unsigned int, rend_t);
170 void rxvt_blank_screen_mem        (rxvt_t*, int, text_t**, rend_t **, unsigned int, rend_t);
171 void rxvt_scr_reset_realloc       (rxvt_t*, int);
172 void rxvt_scr_delete_row          (rxvt_t*, int);
173 void rxvt_scr_add_row             (rxvt_t*, int, unsigned int, unsigned int);
174 void static inline rxvt_clear_area       (rxvt_t*, int page, int x, int y, unsigned int w, unsigned int h);
175 void static inline rxvt_fill_rectangle   (rxvt_t*, int page, int x, int y, unsigned int w, unsigned int h);
176 void
177 rxvt_scr_draw_string (rxvt_t* r, int page,
178 	int x, int y, char* str, int len, int drawfunc,
179 	uint16_t fore, uint16_t back,
180 	__attribute__((unused)) rend_t rend, Region refreshRegion);
181 void rxvt_scr_adjust_col          (rxvt_t*, int, unsigned int);
182 void rxvt_set_font_style          (rxvt_t*, int);
183 int  rxvt_scr_change_view         (rxvt_t*, int, uint16_t);
184 void rxvt_scr_reverse_selection   (rxvt_t*, int);
185 void rxvt_paste_str                 (rxvt_t*, int, const unsigned char*, unsigned int);
186 int  rxvt_selection_request_other (rxvt_t*, int, Atom, int);
187 void rxvt_selection_start_colrow  (rxvt_t*, int, int, int);
188 void rxvt_selection_delimit_word  (rxvt_t*, int, enum page_dirn, const row_col_t*, row_col_t*);
189 #ifdef MULTICHAR_SET
190 void rxvt_selection_adjust_kanji  (rxvt_t*, int);
191 #endif
192 void rxvt_selection_extend_colrow (rxvt_t*, int, int32_t, int32_t, int, int, int);
193 #ifndef NO_FRILLS
194 void rxvt_selection_trim          (rxvt_t*, int);
195 #endif
196 #ifdef TEXT_SHADOW
197 # ifdef XFT_SUPPORT
198 void rxvt_set_clipping		  (rxvt_t*, XftDraw*, GC, Region, int, int, unsigned, unsigned, int*, int*);
199 void rxvt_free_clipping		  (rxvt_t*, XftDraw*, GC, Region);
200 # else
201 void rxvt_set_clipping		  (rxvt_t*, __attribute__((unused)) void*, GC, Region, int, int, unsigned, unsigned, int*, int*);
202 void rxvt_free_clipping		  (rxvt_t*, __attribute__((unused)) void*, GC, Region);
203 # endif
204 #endif
205 #ifdef XFT_SUPPORT
206 #endif	/* XFT_SUPPORT */
207 /*--------------------------------------------------------------------*
208  *         END   `INTERNAL' ROUTINE PROTOTYPES                        *
209  *--------------------------------------------------------------------*/
210 
211 
212 
213 /* ------------------------------------------------------------------------- *
214  *			SCREEN `COMMON' ROUTINES			   *
215  * ------------------------------------------------------------------------- */
216 
217 /* Fill part/all of a line with blanks. */
218 /* INTPROTO */
219 void
rxvt_blank_line(text_t * et,rend_t * er,unsigned int width,rend_t efs)220 rxvt_blank_line(text_t *et, rend_t *er, unsigned int width, rend_t efs)
221 {
222     MEMSET(et, ' ', (size_t)width);
223     efs &= ~RS_baseattrMask;
224     for (; width--;)
225 	*er++ = efs;
226 }
227 
228 /* ------------------------------------------------------------------------- */
229 /* Fill a full line with blanks - make sure it is allocated first */
230 /* INTPROTO */
231 void
rxvt_blank_screen_mem(rxvt_t * r,int page,text_t ** tp,rend_t ** rp,unsigned int row,rend_t efs)232 rxvt_blank_screen_mem(rxvt_t* r, int page, text_t **tp, rend_t **rp,
233 	unsigned int row, rend_t efs)
234 {
235     int		 width = r->TermWin.ncol;
236     rend_t	 *er;
237 
238     assert ((tp[row] && rp[row]) ||
239 	(tp[row] == NULL && rp[row] == NULL));
240 
241     /* possible integer overflow? */
242     assert (width > 0);
243     assert (sizeof (text_t) * width > 0);
244     assert (sizeof (rend_t) * width > 0);
245 
246     if (tp[row] == NULL)
247     {
248 	tp[row] = rxvt_malloc(sizeof(text_t) * width);
249 	rp[row] = rxvt_malloc(sizeof(rend_t) * width);
250     }
251     MEMSET(tp[row], ' ', width);
252     efs &= ~RS_baseattrMask;
253     for (er = rp[row]; width--;)
254 	*er++ = efs;
255 }
256 
257 
258 
259 /* ------------------------------------------------------------------------- *
260  *			  SCREEN INITIALISATION				*
261  * ------------------------------------------------------------------------- */
262 /* EXTPROTO */
263 void
rxvt_init_screen(rxvt_t * r)264 rxvt_init_screen (rxvt_t* r)
265 {
266     int	    p;
267     int	    ncol = r->TermWin.ncol;
268 
269     /* first time, we don't have r->tabstop yet */
270     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "allocate r->tabstop as %d\n", ncol));
271     assert (ncol > 0);	/* possible integer overflow? */
272     r->tabstop = rxvt_malloc(ncol * sizeof(char));
273     for (p = 0; p < ncol; p++)
274 	r->tabstop[p] = (p % TABSTOP_SIZE == 0) ? 1 : 0;
275 }
276 
277 
278 void
rxvt_scr_alloc(rxvt_t * r,int page)279 rxvt_scr_alloc (rxvt_t* r, int page)
280 {
281     unsigned int    ncol, nrow, total_rows;
282     unsigned int    p, q;
283 
284 
285     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_alloc %d ()\n", page));
286     ncol = r->TermWin.ncol;
287     nrow = r->TermWin.nrow;
288     total_rows = nrow + SVLINES;
289 
290     /*
291     ** First time called so just malloc everything : don't rely on
292     ** realloc
293     ** Note: this is still needed so that all the scrollback lines
294     ** are NULL
295     */
296     PVTS(r, page)->buf_text = rxvt_calloc(total_rows, sizeof(text_t*));
297     PVTS(r, page)->buf_rend = rxvt_calloc(total_rows, sizeof(rend_t*));
298 
299     PVTS(r, page)->drawn_text = rxvt_calloc(nrow, sizeof(text_t*));
300     PVTS(r, page)->drawn_rend = rxvt_calloc(nrow, sizeof(rend_t*));
301 
302     PSCR(r, page).text = rxvt_calloc(total_rows, sizeof(text_t*));
303     PSCR(r, page).tlen = rxvt_calloc(total_rows, sizeof(int16_t));
304     PSCR(r, page).rend = rxvt_calloc(total_rows, sizeof(rend_t*));
305 
306 #if NSCREENS
307     PVTS(r, page)->swap.text = rxvt_calloc(nrow, sizeof(text_t*));
308     PVTS(r, page)->swap.tlen = rxvt_calloc(nrow, sizeof(int16_t));
309     PVTS(r, page)->swap.rend = rxvt_calloc(nrow, sizeof(rend_t*));
310 #endif
311 
312     for (p = 0; p < nrow; p++)
313     {
314 	q = p + SVLINES;
315 	rxvt_blank_screen_mem (r, page, PSCR(r, page).text,
316 	    PSCR(r, page).rend, q, DEFAULT_RSTYLE);
317 	PSCR(r, page).tlen[q] = 0;
318 #if NSCREENS
319 	rxvt_blank_screen_mem (r, page, PVTS(r, page)->swap.text,
320 	    PVTS(r, page)->swap.rend, p, DEFAULT_RSTYLE);
321 	PVTS(r, page)->swap.tlen[p] = 0;
322 #endif
323 
324 	rxvt_blank_screen_mem (r, page, PVTS(r, page)->drawn_text,
325 	    PVTS(r, page)->drawn_rend, p, DEFAULT_RSTYLE);
326     }
327     PVTS(r, page)->nscrolled = 0;   /* no saved lines */
328     PSCR(r, page).flags = Screen_DefaultFlags;
329     PSCR(r, page).cur.row = 0;
330     PSCR(r, page).cur.col = 0;
331     PSCR(r, page).charset = 0;
332     PVTS(r, page)->current_screen = PRIMARY;
333     rxvt_scr_cursor(r, page, SAVE);
334 #if NSCREENS
335     PVTS(r, page)->swap.flags = Screen_DefaultFlags;
336     PVTS(r, page)->swap.cur.row = 0;
337     PVTS(r, page)->swap.cur.col = 0;
338     PVTS(r, page)->swap.charset = 0;
339     PVTS(r, page)->current_screen = SECONDARY;
340     rxvt_scr_cursor(r, page, SAVE);
341     PVTS(r, page)->current_screen = PRIMARY;
342 #endif
343 
344     PVTS(r, page)->rstyle = DEFAULT_RSTYLE;
345     PVTS(r, page)->rvideo = 0;
346     MEMSET(&(PVTS(r, page)->charsets), 'B', sizeof(PVTS(r, page)->charsets));
347 #ifdef MULTICHAR_SET
348     PVTS(r, page)->multi_byte = 0;
349     PVTS(r, page)->lost_multi = 0;
350     PVTS(r, page)->chstat = SBYTE;
351 #endif
352 
353     /* Now set screen initialization flag */
354     PVTS(r, page)->init_screen = 1;
355 }
356 
357 
358 
359 /* INTPROTO */
360 void
rxvt_scr_reset_realloc(rxvt_t * r,int page)361 rxvt_scr_reset_realloc(rxvt_t* r, int page)
362 {
363     unsigned int   total_rows, nrow;
364 
365 
366     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_reset_realloc %d ()\n", page));
367     nrow = r->TermWin.nrow;
368     total_rows = nrow + SVLINES;
369 
370     PSCR(r, page).text = rxvt_realloc (
371 	PSCR(r, page).text, total_rows * sizeof(text_t *));
372     PSCR(r, page).tlen = rxvt_realloc (
373 	PSCR(r, page).tlen, total_rows * sizeof(int16_t));
374     PSCR(r, page).rend = rxvt_realloc (
375 	PSCR(r, page).rend, total_rows * sizeof(rend_t *));
376 
377 #if NSCREENS
378     PVTS(r, page)->swap.text   = rxvt_realloc (
379 	PVTS(r, page)->swap.text, nrow * sizeof(text_t *));
380     PVTS(r, page)->swap.tlen   = rxvt_realloc (
381 	PVTS(r, page)->swap.tlen  , total_rows * sizeof(int16_t));
382     PVTS(r, page)->swap.rend   = rxvt_realloc (
383 	PVTS(r, page)->swap.rend, nrow * sizeof(rend_t *));
384 #endif
385 
386     PVTS(r, page)->buf_text = rxvt_realloc (
387 	PVTS(r, page)->buf_text, total_rows * sizeof(text_t *));
388     PVTS(r, page)->buf_rend = rxvt_realloc (
389 	PVTS(r, page)->buf_rend, total_rows * sizeof(rend_t *));
390 
391     PVTS(r, page)->drawn_text  = rxvt_realloc (
392 	PVTS(r, page)->drawn_text, nrow * sizeof(text_t *));
393     PVTS(r, page)->drawn_rend  = rxvt_realloc (
394 	PVTS(r, page)->drawn_rend, nrow * sizeof(rend_t *));
395 }
396 
397 
398 /* INTPROTO */
399 void
rxvt_scr_delete_row(rxvt_t * r,int page)400 rxvt_scr_delete_row (rxvt_t* r, int page)
401 {
402     unsigned int    nrow, prev_nrow;
403     unsigned int    p, q;
404     register int    i;
405 
406 
407     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_delete_row %d ()\n", page));
408     nrow = r->TermWin.nrow;
409     prev_nrow = PVTS(r, page)->prev_nrow;
410 
411     /* delete rows */
412     i = min(PVTS(r, page)->nscrolled, prev_nrow - nrow);
413     rxvt_scroll_text(r, page, 0, (int)prev_nrow - 1, i, 1);
414 
415     for (p = nrow; p < prev_nrow; p++)
416     {
417 	q = p + SVLINES;
418 	if (PSCR(r, page).text[q])
419 	{
420 	    assert(PSCR(r, page).rend[q]);
421 	    rxvt_free(PSCR(r, page).text[q]);
422 	    PSCR(r, page).text[q] = NULL;
423 	    rxvt_free(PSCR(r, page).rend[q]);
424 	    PSCR(r, page).rend[q] = NULL;
425 	}
426 #if NSCREENS
427 	if (PVTS(r, page)->swap.text[p])
428 	{
429 	    assert(PVTS(r, page)->swap.rend[p]);
430 	    rxvt_free(PVTS(r, page)->swap.text[p]);
431 	    PVTS(r, page)->swap.text[p] = NULL;
432 	    rxvt_free(PVTS(r, page)->swap.rend[p]);
433 	    PVTS(r, page)->swap.rend[p] = NULL;
434 	}
435 #endif
436 	assert (PVTS(r, page)->drawn_text[p]);
437 	assert (PVTS(r, page)->drawn_rend[p]);
438 	rxvt_free(PVTS(r, page)->drawn_text[p]);
439 	PVTS(r, page)->drawn_text[p] = NULL;
440 	rxvt_free(PVTS(r, page)->drawn_rend[p]);
441 	PVTS(r, page)->drawn_rend[p] = NULL;
442     }
443 
444     /* we have fewer rows so fix up cursor position */
445     MIN_IT(PSCR(r, page).cur.row, (int32_t)nrow - 1);
446 #if NSCREENS
447     MIN_IT(PVTS(r, page)->swap.cur.row, (int32_t)nrow - 1);
448 #endif
449 
450     rxvt_scr_reset_realloc (r, page);	/* realloc _last_ */
451 }
452 
453 
454 /* INTPROTO */
455 void
rxvt_scr_add_row(rxvt_t * r,int page,unsigned int total_rows,unsigned int prev_total_rows)456 rxvt_scr_add_row (rxvt_t* r, int page, unsigned int total_rows, unsigned int prev_total_rows)
457 {
458     unsigned int    nrow, prev_nrow;
459     unsigned int    p;
460     register int    i;
461 
462 
463     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "%s( page=%d, total_rows=%u, prev_total_rows=%u )\n", __func__, page, total_rows, prev_total_rows ));
464 
465     nrow = r->TermWin.nrow;
466     prev_nrow = PVTS(r, page)->prev_nrow;
467 
468     /* add rows */
469     rxvt_scr_reset_realloc(r, page);	/* realloc _first_ */
470 
471     i = min(PVTS(r, page)->nscrolled, nrow - prev_nrow);
472     for (p = prev_total_rows; p < total_rows; p++)
473     {
474 	PSCR(r, page).tlen[p] = 0;
475 	PSCR(r, page).text[p] = NULL;
476 	PSCR(r, page).rend[p] = NULL;
477     }
478 
479     for (p = prev_total_rows; p < total_rows - i; p++)
480 	rxvt_blank_screen_mem (r, page, PSCR(r, page).text,
481 	    PSCR(r, page).rend, p, DEFAULT_RSTYLE);
482 
483     for (p = prev_nrow; p < nrow; p++)
484     {
485 #if NSCREENS
486 	PVTS(r, page)->swap.tlen[p] = 0;
487 	PVTS(r, page)->swap.text[p] = NULL;
488 	PVTS(r, page)->swap.rend[p] = NULL;
489 	rxvt_blank_screen_mem (r, page, PVTS(r, page)->swap.text,
490 	    PVTS(r, page)->swap.rend, p, DEFAULT_RSTYLE);
491 #endif
492 
493 	PVTS(r, page)->drawn_text[p] = NULL;
494 	PVTS(r, page)->drawn_rend[p] = NULL;
495 	rxvt_blank_screen_mem (r, page, PVTS(r, page)->drawn_text,
496 	    PVTS(r, page)->drawn_rend, p, DEFAULT_RSTYLE);
497     }
498 
499     if (i > 0)
500     {
501 	rxvt_scroll_text(r, page, 0, (int)nrow - 1, -i, 1);
502 	PSCR(r, page).cur.row += i;
503 	PSCR(r, page).s_cur.row += i;
504 	PVTS(r, page)->nscrolled -= i;
505     }
506 
507     assert(PSCR(r, page).cur.row < r->TermWin.nrow);
508     MIN_IT(PSCR(r, page).cur.row, nrow - 1);
509 #if NSCREENS
510     assert(PVTS(r, page)->swap.cur.row < r->TermWin.nrow);
511     MIN_IT(PVTS(r, page)->swap.cur.row, nrow - 1);
512 #endif
513 }
514 
515 
516 
517 /* INTPROTO */
518 void
rxvt_scr_adjust_col(rxvt_t * r,int page,unsigned int total_rows)519 rxvt_scr_adjust_col (rxvt_t* r, int page, unsigned int total_rows)
520 {
521     unsigned int    nrow, ncol, prev_ncol;
522     unsigned int    p;
523 
524 
525     nrow = r->TermWin.nrow;
526     ncol = r->TermWin.ncol;
527     prev_ncol = PVTS(r, page)->prev_ncol;
528 
529     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "%s( r, page=%d, total_rows=%u ):" "ncol=%d, prev_ncol=%d, nrow=%d\n", __func__, page, total_rows, ncol, prev_ncol, nrow ));
530 
531 
532     for (p = 0; p < total_rows; p++)
533     {
534 	if (PSCR(r, page).text[p])
535 	{
536 	    PSCR(r, page).text[p] = rxvt_realloc (
537 		PSCR(r, page).text[p], ncol * sizeof(text_t));
538 	    PSCR(r, page).rend[p] = rxvt_realloc (
539 		PSCR(r, page).rend[p], ncol * sizeof(rend_t));
540 	    MIN_IT(PSCR(r, page).tlen[p], (int16_t)ncol);
541 	    if (ncol > prev_ncol)
542 		rxvt_blank_line (
543 		    &(PSCR(r, page).text[p][prev_ncol]),
544 		    &(PSCR(r, page).rend[p][prev_ncol]),
545 		    ncol - prev_ncol, DEFAULT_RSTYLE);
546 	}
547     }
548 
549     for (p = 0; p < nrow; p++)
550     {
551 	PVTS(r, page)->drawn_text[p] = rxvt_realloc (
552 	    PVTS(r, page)->drawn_text[p], ncol * sizeof(text_t));
553 	PVTS(r, page)->drawn_rend[p] = rxvt_realloc (
554 	    PVTS(r, page)->drawn_rend[p], ncol * sizeof(rend_t));
555 #if NSCREENS
556 	if (PVTS(r, page)->swap.text[p])
557 	{
558 	    PVTS(r, page)->swap.text[p] = rxvt_realloc (
559 		PVTS(r, page)->swap.text[p], ncol * sizeof(text_t));
560 	    PVTS(r, page)->swap.rend[p] = rxvt_realloc (
561 		PVTS(r, page)->swap.rend[p], ncol * sizeof(rend_t));
562 	    MIN_IT(PVTS(r, page)->swap.tlen[p], (int16_t)ncol);
563 	    if (ncol > prev_ncol)
564 		rxvt_blank_line(
565 		    &(PVTS(r, page)->swap.text[p][prev_ncol]),
566 		    &(PVTS(r, page)->swap.rend[p][prev_ncol]),
567 		    ncol - prev_ncol, DEFAULT_RSTYLE);
568 	}
569 #endif
570 	if (ncol > prev_ncol)
571 	    rxvt_blank_line(
572 		&(PVTS(r, page)->drawn_text[p][prev_ncol]),
573 		&(PVTS(r, page)->drawn_rend[p][prev_ncol]),
574 		ncol - prev_ncol, DEFAULT_RSTYLE);
575     }
576     MIN_IT(PSCR(r, page).cur.col, (int16_t)ncol - 1);
577 #if NSCREENS
578     MIN_IT(PVTS(r, page)->swap.cur.col, (int16_t)ncol - 1);
579 #endif
580 
581 
582     /*
583     ** Only reset tabstop if expanding columns, save realloc in
584     ** shrinking columns
585     */
586     if (r->tabstop && ncol > prev_ncol)
587     {
588 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "expand r->tabstop to %d\n", ncol));
589 	r->tabstop = rxvt_realloc(r->tabstop, ncol * sizeof(char));
590 	for (p = prev_ncol; p < ncol; p++)
591 	    r->tabstop[p] = (p % TABSTOP_SIZE == 0) ? 1 : 0;
592     }
593 }
594 
595 
596 
597 /* EXTPROTO */
598 void
rxvt_scr_reset(rxvt_t * r,int page)599 rxvt_scr_reset(rxvt_t* r, int page)
600 {
601     unsigned int    ncol, nrow, prev_ncol, prev_nrow,
602 		    total_rows, prev_total_rows;
603 
604 
605     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_reset %d ()\n", page));
606 
607     VSTART = 0;
608     RESET_CHSTAT(r, page);
609     PVTS(r, page)->num_scr = 0;	/* number of lines scrolled */
610 
611     prev_ncol = PVTS(r, page)->prev_ncol;
612     prev_nrow = PVTS(r, page)->prev_nrow;
613     if (r->TermWin.ncol == 0)
614 	r->TermWin.ncol = 80;
615     if (r->TermWin.nrow == 0)
616 	r->TermWin.nrow = 24;
617     ncol = r->TermWin.ncol;
618     nrow = r->TermWin.nrow;
619     if (PVTS(r, page)->init_screen &&
620 	ncol == prev_ncol && nrow == prev_nrow)
621 	return;
622 
623     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_reset %d () refresh screen\n", page));
624     PVTS(r, page)->want_refresh = 1;
625 
626     total_rows = nrow + SVLINES;
627     prev_total_rows = prev_nrow + SVLINES;
628 
629     PSCR(r, page).tscroll = 0;
630     PSCR(r, page).bscroll = nrow - 1;
631 
632     if (PVTS(r, page)->init_screen == 0)
633     {
634 	/* Initialize the screen structures */
635 	rxvt_scr_alloc (r, page);
636     }
637     else
638     {
639 	/* B1: resize rows */
640 	if (nrow < prev_nrow)
641 	{
642 	    rxvt_scr_delete_row (r, page);
643 	}
644 	else if (nrow > prev_nrow)
645 	{
646 	    rxvt_scr_add_row (r, page, total_rows, prev_total_rows);
647 	}
648 	/* B2: resize columns */
649 	if (ncol != prev_ncol)
650 	{
651 	    rxvt_scr_adjust_col (r, page, total_rows);
652 	}
653     }
654 
655     PVTS(r, page)->prev_nrow = nrow;
656     PVTS(r, page)->prev_ncol = ncol;
657 
658     rxvt_tt_winsize(PVTS(r, page)->cmd_fd, r->TermWin.ncol, r->TermWin.nrow, PVTS(r, page)->cmd_pid);
659 }
660 
661 
662 
663 
664 /* ------------------------------------------------------------------------- */
665 /*
666  * Free everything.  That way malloc debugging can find leakage.
667  */
668 /* EXTPROTO */
669 void
rxvt_scr_release(rxvt_t * r,int page)670 rxvt_scr_release(rxvt_t* r, int page)
671 {
672     unsigned int    total_rows;
673     int		    i;
674 
675 
676     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_release %d ()\n", page));
677     total_rows = r->TermWin.nrow + SVLINES;
678 
679     for (i = 0; i < total_rows; i++)
680     {
681 	if (PSCR(r, page).text[i])
682 	{
683 	    /* then so is PSCR(r, page).rend[i] */
684 	    rxvt_free(PSCR(r, page).text[i]);
685 	    PSCR(r, page).text[i] = NULL;
686 	    assert(PSCR(r, page).rend[i]);
687 	    rxvt_free(PSCR(r, page).rend[i]);
688 	    PSCR(r, page).rend[i] = NULL;
689 	}
690     }
691 
692     for (i = 0; i < r->TermWin.nrow; i++)
693     {
694 	/* if (PVTS(r, page)->drawn_text[i]) */
695 	    rxvt_free(PVTS(r, page)->drawn_text[i]);
696 	PVTS(r, page)->drawn_text[i] = NULL;
697 	/* if (PVTS(r, page)->drawn_rend[i]) */
698 	    rxvt_free(PVTS(r, page)->drawn_rend[i]);
699 	PVTS(r, page)->drawn_rend[i] = NULL;
700 #if NSCREENS
701 	/* if (PVTS(r, page)->swap.text[i]) */
702 	    rxvt_free(PVTS(r, page)->swap.text[i]);
703 	PVTS(r, page)->swap.text[i] = NULL;
704 	/* if (PVTS(r, page)->swap.rend[i])) */
705 	    rxvt_free(PVTS(r, page)->swap.rend[i]);
706 	PVTS(r, page)->swap.rend[i] = NULL;
707 #endif
708     }
709 
710     rxvt_free(PSCR(r, page).text); PSCR(r, page).text = NULL;
711     rxvt_free(PSCR(r, page).tlen); PSCR(r, page).tlen = NULL;
712     rxvt_free(PSCR(r, page).rend); PSCR(r, page).rend = NULL;
713     rxvt_free(PVTS(r, page)->drawn_text);  PVTS(r, page)->drawn_text = NULL;
714     rxvt_free(PVTS(r, page)->drawn_rend);  PVTS(r, page)->drawn_rend = NULL;
715 #if NSCREENS
716     rxvt_free(PVTS(r, page)->swap.text);   PVTS(r, page)->swap.text = NULL;
717     rxvt_free(PVTS(r, page)->swap.tlen);   PVTS(r, page)->swap.tlen = NULL;
718     rxvt_free(PVTS(r, page)->swap.rend);   PVTS(r, page)->swap.rend = NULL;
719 #endif
720     rxvt_free(PVTS(r, page)->buf_text);	PVTS(r, page)->buf_text = NULL;
721     rxvt_free(PVTS(r, page)->buf_rend);	PVTS(r, page)->buf_rend = NULL;
722 
723     /* next rxvt_scr_reset will be the first time initialization */
724     PVTS(r, page)->init_screen = 0;
725 
726     /* clear selection if necessary */
727     if (page == r->selection.vt)
728     {
729 	rxvt_process_selectionclear (r, page);
730     }
731 }
732 
733 /* ------------------------------------------------------------------------- */
734 /*
735  * Hard reset
736  */
737 /* EXTPROTO */
738 void
rxvt_scr_poweron(rxvt_t * r,int page)739 rxvt_scr_poweron(rxvt_t* r, int page)
740 {
741     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_poweron %d ()\n", page));
742 
743     rxvt_scr_release(r, page);
744     PVTS(r, page)->prev_nrow = PVTS(r, page)->prev_ncol = 0;
745     rxvt_scr_reset(r, page);
746 
747     rxvt_scr_clear(r, page);
748     rxvt_scr_refresh(r, page, SLOW_REFRESH);
749 }
750 
751 
752 /* ------------------------------------------------------------------------- *
753  *			 PROCESS SCREEN COMMANDS			   *
754  * ------------------------------------------------------------------------- */
755 /*
756  * Save and Restore cursor
757  * XTERM_SEQ: Save cursor   : ESC 7
758  * XTERM_SEQ: Restore cursor: ESC 8
759  */
760 /* EXTPROTO */
761 void
rxvt_scr_cursor(rxvt_t * r,int page,int mode)762 rxvt_scr_cursor(rxvt_t* r, int page, int mode)
763 {
764     screen_t	   *s;
765 
766     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_cursor %d (%c)\n", page, mode));
767 
768 #if NSCREENS && !defined(NO_SECONDARY_SCREEN_CURSOR)
769     if (PVTS(r, page)->current_screen == SECONDARY)
770 	s = &(PVTS(r, page)->swap);
771     else
772 #endif
773     s = &(PSCR(r, page));
774     switch (mode)
775     {
776 	case SAVE:
777 	    s->s_cur.row = s->cur.row;
778 	    s->s_cur.col = s->cur.col;
779 	    s->s_rstyle = PVTS(r, page)->rstyle;
780 	    s->s_charset = s->charset;
781 	    s->s_charset_char = PVTS(r, page)->charsets[s->charset];
782 	    break;
783 	case RESTORE:
784 	    PVTS(r, page)->want_refresh = 1;
785 	    s->cur.row = s->s_cur.row;
786 	    s->cur.col = s->s_cur.col;
787 	    s->flags &= ~Screen_WrapNext;
788 	    PVTS(r, page)->rstyle = s->s_rstyle;
789 	    s->charset = s->s_charset;
790 	    PVTS(r, page)->charsets[s->charset] = s->s_charset_char;
791 	    rxvt_set_font_style(r, page);
792 	    break;
793     }
794 /* boundary check in case screen size changed between SAVE and RESTORE */
795     MIN_IT(s->cur.row, r->TermWin.nrow - 1);
796     MIN_IT(s->cur.col, r->TermWin.ncol - 1);
797     assert(s->cur.row >= 0);
798     assert(s->cur.col >= 0);
799     MAX_IT(s->cur.row, 0);
800     MAX_IT(s->cur.col, 0);
801 }
802 
803 /* ------------------------------------------------------------------------- */
804 /*
805  * Swap between primary and secondary screens
806  * XTERM_SEQ: Primary screen  : ESC [ ? 4 7 h
807  * XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
808  */
809 /* EXTPROTO */
810 int
rxvt_scr_change_screen(rxvt_t * r,int page,int scrn)811 rxvt_scr_change_screen(rxvt_t* r, int page, int scrn)
812 {
813 #if NSCREENS
814     unsigned int	i, offset;
815 #endif
816 
817     PVTS(r, page)->want_refresh = 1;
818 
819     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_change_screen %d (%d)\n", page, scrn));
820 
821     VSTART = 0;
822     RESET_CHSTAT(r, page);
823 
824     if (PVTS(r, page)->current_screen == scrn)
825 	return PVTS(r, page)->current_screen;
826 
827     rxvt_selection_check(r, page, 2);	/* check for boundary cross */
828 
829     SWAP_IT(PVTS(r, page)->current_screen, scrn, int);
830 
831 #if NSCREENS
832     PVTS(r, page)->num_scr = 0;
833     offset = SVLINES;
834 
835     for (i = PVTS(r, page)->prev_nrow; i--;)
836     {
837 	SWAP_IT(PSCR(r, page).text[i + offset],
838 	    PVTS(r, page)->swap.text[i], text_t *);
839 	SWAP_IT(PSCR(r, page).tlen[i + offset],
840 	    PVTS(r, page)->swap.tlen[i], int16_t);
841 	SWAP_IT(PSCR(r, page).rend[i + offset],
842 	    PVTS(r, page)->swap.rend[i], rend_t *);
843     }
844     SWAP_IT(CURROW, PVTS(r, page)->swap.cur.row, int16_t);
845     SWAP_IT(CURCOL, PVTS(r, page)->swap.cur.col, int16_t);
846     assert (CURROW >= 0);
847     assert (CURROW < PVTS(r, page)->prev_nrow);
848     assert (CURCOL >= 0);
849     assert (CURCOL < PVTS(r, page)->prev_ncol);
850     MAX_IT(CURROW, 0);
851     MIN_IT(CURROW, (int32_t)PVTS(r, page)->prev_nrow - 1);
852     MAX_IT(CURCOL, 0);
853     MIN_IT(CURCOL, (int32_t)PVTS(r, page)->prev_ncol - 1);
854 
855     SWAP_IT(PSCR(r, page).charset, PVTS(r, page)->swap.charset,
856 	int16_t);
857     SWAP_IT(PSCR(r, page).flags, PVTS(r, page)->swap.flags, int);
858     PSCR(r, page).flags |= Screen_VisibleCursor;
859     PVTS(r, page)->swap.flags |= Screen_VisibleCursor;
860 
861 #else
862 # ifdef SCROLL_ON_NO_SECONDARY
863     if (PVTS(r, page)->current_screen == PRIMARY)
864 	rxvt_scroll_text(r, page, 0, (PVTS(r, page)->prev_nrow - 1),
865 	    PVTS(r, page)->prev_nrow, 0);
866 # endif
867 #endif
868 
869     /* Need to update tabbar buttons */
870     if (ISSET_OPTION(r, Opt2_protectSecondary))
871 	rxvt_tabbar_draw_buttons (r);
872 
873     return scrn;
874 }
875 
876 /* ------------------------------------------------------------------------- */
877 /*
878  * Change the colour for following text
879  */
880 /* EXTPROTO */
881 void
rxvt_scr_color(rxvt_t * r,int page,unsigned int color,int fgbg)882 rxvt_scr_color(rxvt_t* r, int page, unsigned int color, int fgbg)
883 {
884     color &= RS_fgMask;
885     if (Color_fg == fgbg)
886 	PVTS(r, page)->rstyle=SET_FGCOLOR(PVTS(r, page)->rstyle, color);
887     else
888 	PVTS(r, page)->rstyle=SET_BGCOLOR(PVTS(r, page)->rstyle, color);
889 }
890 
891 
892 /* ------------------------------------------------------------------------- */
893 /*
894  * Change the rendition style for following text
895  */
896 /* EXTPROTO */
897 void
rxvt_scr_rendition(rxvt_t * r,int page,int set,int style)898 rxvt_scr_rendition(rxvt_t* r, int page, int set, int style)
899 {
900     if (set)
901 	PVTS(r, page)->rstyle |= style;
902     else if (style == ~RS_None)
903 	PVTS(r, page)->rstyle = DEFAULT_RSTYLE | (PVTS(r, page)->rstyle & RS_fontMask);
904     else
905 	PVTS(r, page)->rstyle &= ~style;
906 }
907 
908 /* ------------------------------------------------------------------------- */
909 /*
910  * Scroll text between <row1> and <row2> inclusive, by <count> lines
911  * count positive ==> scroll up
912  * count negative ==> scroll down
913  * spec == 0 for normal routines
914  */
915 /* EXTPROTO */
916 int
rxvt_scroll_text(rxvt_t * r,int page,int row1,int row2,int count,int spec)917 rxvt_scroll_text(rxvt_t* r, int page, int row1, int row2, int count, int spec)
918 {
919     int		    i, j, ret;
920     unsigned int    nscrolled;
921     size_t 	    size;
922 
923     if (count == 0 || (row1 > row2))
924 	return 0;
925 
926     PVTS(r, page)->want_refresh = 1;
927     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN,
928 		"rxvt_scroll_text %d (%d,%d,%d,%d): %s\n", page, row1, row2,
929 		count, spec, (PVTS(r, page)->current_screen == PRIMARY) ?
930 		    "Primary" : "Secondary" ));
931 
932     if (
933 	  (count > 0)
934 	  && (row1 == 0)
935 	  && (PVTS(r, page)->current_screen == PRIMARY)
936        )
937     {
938 	nscrolled = (unsigned int) PVTS(r, page)->nscrolled
939 					    + (unsigned int) count;
940 
941 	if (nscrolled > (unsigned int)SVLINES)
942 	    PVTS(r, page)->nscrolled = SVLINES;
943 	else
944 	    PVTS(r, page)->nscrolled = (uint16_t)nscrolled;
945     }
946     else if (!spec)
947 	row1 += SVLINES;
948     row2 += SVLINES;
949 
950     if (
951 	  SEL(r).op
952 	  && SEL(r).vt == page
953 	  && PVTS(r, page)->current_screen == SEL(r).screen
954        )
955     {
956 	i = SEL(r).beg.row + SVLINES;
957 	j = SEL(r).end.row + SVLINES;
958 	if (
959 	      (i < row1 && j > row1)
960 	      || (i < row2 && j > row2)
961 	      || (i - count < row1 && i >= row1)
962 	      || (i - count > row2 && i <= row2)
963 	      || (j - count < row1 && j >= row1)
964 	      || (j - count > row2 && j <= row2)
965 	   )
966 	{
967 	    CLEAR_ALL_SELECTION(r);
968 	    /* XXX: too aggressive? */
969 	    SEL(r).op = SELECTION_CLEAR;
970 	}
971 	else if (j >= row1 && j <= row2)
972 	{
973 	    /* move selected region too */
974 	    SEL(r).beg.row -= count;
975 	    SEL(r).end.row -= count;
976 	    SEL(r).mark.row -= count;
977 	}
978     }
979 
980     /* _after_ PVTS(r, page)->nscrolled update */
981     rxvt_selection_check(r, page, 0);
982 
983     PVTS(r, page)->num_scr += count;
984     j = count;
985     if (count < 0)
986 	count = -count;
987     i = row2 - row1 + 1;
988     MIN_IT(count, i);
989 
990     if (j > 0)
991     {
992 	/* A: scroll up */
993 
994 	/* A1: Copy lines that will get clobbered by the rotation */
995 	for (i = count - 1, j = row1; i >= 0; i--, j++)
996 	{
997 	    PVTS(r, page)->buf_text[i] = PSCR(r, page).text[j];
998 	    PVTS(r, page)->buf_rend[i] = PSCR(r, page).rend[j];
999 	}
1000 
1001 	/* A2: Rotate lines */
1002 	size = sizeof(*PSCR(r, page).tlen);
1003 	MEMMOVE(&(PSCR(r, page).tlen[row1]), &(PSCR(r, page).tlen[row1+count]),
1004 			(row2 - row1 - count + 1) * size);
1005 	size = sizeof(*PSCR(r, page).text);
1006 	MEMMOVE(&(PSCR(r, page).text[row1]), &(PSCR(r, page).text[row1+count]),
1007 			(row2 - row1 - count + 1) * size);
1008 	size = sizeof(*PSCR(r, page).rend);
1009 	MEMMOVE(&(PSCR(r, page).rend[row1]), &(PSCR(r, page).rend[row1+count]),
1010 			(row2 - row1 - count + 1) * size);
1011 
1012 	j = row2 - count + 1;
1013 	ret = i = count;
1014     }
1015     else /* if (j < 0) */
1016     {
1017 	/* B: scroll down */
1018 
1019 	/* B1: Copy lines that will get clobbered by the rotation */
1020 	size = sizeof(*PSCR(r, page).text);
1021 	MEMCPY(PVTS(r, page)->buf_text, &PSCR(r, page).text[row2 - count + 1], count * size);
1022 	size = sizeof(*PSCR(r, page).rend);
1023 	MEMCPY(PVTS(r, page)->buf_rend, &PSCR(r, page).rend[row2 - count + 1], count * size);
1024 
1025 	/* B2: Rotate lines */
1026 	size = sizeof(*PSCR(r, page).tlen);
1027 	MEMMOVE(&(PSCR(r, page).tlen[row1 + count]), &(PSCR(r, page).tlen[row1]),
1028 			(row2 - row1 - count + 1) * size);
1029 	size = sizeof(*PSCR(r, page).text);
1030 	MEMMOVE(&(PSCR(r, page).text[row1 + count]), &(PSCR(r, page).text[row1]),
1031 			(row2 - row1 - count + 1) * size);
1032 	size = sizeof(*PSCR(r, page).rend);
1033 	MEMMOVE(&(PSCR(r, page).rend[row1 + count]), &(PSCR(r, page).rend[row1]),
1034 			(row2 - row1 - count + 1) * size);
1035 
1036 	j = row1, i = count;
1037 	ret = -count;
1038     }
1039 
1040     /* C: Resurrect lines */
1041     size = sizeof(*PSCR(r, page).tlen);
1042     MEMSET(&PSCR(r, page).tlen[j], 0, count * size);
1043     size = sizeof(*PSCR(r, page).text);
1044     MEMCPY(&PSCR(r, page).text[j], PVTS(r, page)->buf_text, count * size);
1045     size = sizeof(*PSCR(r, page).rend);
1046     MEMCPY(&PSCR(r, page).rend[j], PVTS(r, page)->buf_rend, count * size);
1047 
1048     for (; i--; j++)
1049     {
1050 	if (!spec)	/* line length may not equal TermWin.ncol */
1051 	    rxvt_blank_screen_mem(r, page, PSCR(r, page).text,
1052 		    PSCR(r, page).rend, (unsigned int)j,
1053 		    PVTS(r, page)->rstyle);
1054     }
1055 
1056     return ret;
1057 }
1058 
1059 /* ------------------------------------------------------------------------- */
1060 /*
1061  * Adjust the PVTS(r, page)->view_start so that the if nlines of text are added,
1062  * the view will not change.
1063  */
1064 void static inline
adjust_view_start(rxvt_t * r,int page,int nlines)1065 adjust_view_start( rxvt_t *r, int page, int nlines)
1066 {
1067     if(
1068 	 ISSET_OPTION( r, Opt_scrollTtyOutputInhibit) &&
1069 	 VSTART != 0 && VSTART + nlines <= PVTS( r, page)->nscrolled
1070       )
1071 	VSTART += nlines;
1072 }
1073 
1074 /*
1075  * Add text given in <str> of length <len> to screen struct
1076  */
1077 /* EXTPROTO */
1078 void
rxvt_scr_add_lines(rxvt_t * r,int page,const unsigned char * str,int nlines,int len)1079 rxvt_scr_add_lines(rxvt_t* r, int page, const unsigned char *str, int nlines,
1080 	int len)
1081 {
1082     unsigned char   checksel, clearsel;
1083     char	    c;
1084     int		 i, row, last_col;
1085     text_t	 *stp;
1086     rend_t	 *srp;
1087 
1088     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_add_lines( r, %d, %.*s, %d, %d)\n", page, min(len, 36), str, nlines, len ));
1089 
1090     if (len <= 0)	/* sanity */
1091 	return;
1092 
1093     PVTS(r, page)->want_refresh = 1;
1094     last_col = r->TermWin.ncol;
1095 
1096     ZERO_SCROLLBACK(r, page);
1097     if (nlines > 0)
1098     {
1099 	/*
1100 	 * 2006-09-02 gi1242 TODO: The code below is *horrible*. When we call
1101 	 * rxvt_scroll_text(), we might end up with a negative CURROW. We try
1102 	 * and be clever using this information, but rxvt_scr_gotorc() will
1103 	 * reset this information!
1104 	 */
1105 	nlines += (CURROW - PSCR(r, page).bscroll);
1106 	if (
1107 	      (nlines > 0)
1108 	      && (PSCR(r, page).tscroll == 0)
1109 	      && (PSCR(r, page).bscroll == (r->TermWin.nrow - 1))
1110 	   )
1111 	{
1112 	    /* _at least_ this many lines need to be scrolled */
1113 	    rxvt_scroll_text(r, page, PSCR(r, page).tscroll,
1114 		PSCR(r, page).bscroll, nlines, 0);
1115 	    adjust_view_start(r, page, nlines );
1116 
1117 	    CURROW -= nlines;
1118 
1119 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "\e[32mScrolling %d lines. CURROW=%d\e[0m\n", nlines, CURROW ));
1120 	}
1121     }
1122 
1123     assert(CURCOL < last_col);
1124     assert(CURROW < r->TermWin.nrow);
1125 
1126 #if 0 /*{{{ Possibly incorrection assertion */
1127     /*
1128      * XXX 2006-09-12 gi1242: I think this assertion is wrong! Note that a few
1129      * lines later we set CURROW to be the max of CURROW and -PVTS()->nscrolled
1130      */
1131     assert(CURROW >= -(int32_t)PVTS(r, page)->nscrolled);
1132 #endif /*}}}*/
1133 
1134     MIN_IT(CURCOL, last_col - 1);
1135     MIN_IT(CURROW, (int32_t)r->TermWin.nrow - 1);
1136     MAX_IT(CURROW, -(int32_t)PVTS(r, page)->nscrolled);
1137 
1138     row = CURROW + SVLINES;
1139 
1140     checksel = (SEL(r).op && SEL(r).vt == page &&
1141 	PVTS(r, page)->current_screen == SEL(r).screen) ? 1 : 0;
1142     clearsel = 0;
1143 
1144     stp = PSCR(r, page).text[row];
1145     srp = PSCR(r, page).rend[row];
1146 
1147 #ifdef MULTICHAR_SET
1148     if(
1149 	 PVTS(r, page)->lost_multi && CURCOL > 0 &&
1150 	 IS_MULTI1(srp[CURCOL - 1]) &&
1151 	 *str != '\n' && *str != '\r' && *str != '\t'
1152       )
1153     {
1154 	PVTS(r, page)->chstat = WBYTE;
1155     }
1156 #endif
1157 
1158     for (i = 0; i < len;)
1159     {
1160 	c = str[i++];
1161 	switch (c)
1162 	{
1163 	    case '\t':
1164 		rxvt_scr_tab(r, page, 1);
1165 		continue;
1166 
1167 	    case '\n':
1168 		/* XXX: think about this */
1169 		if( PSCR(r, page).tlen[row] != -1 )
1170 		    MAX_IT(PSCR(r, page).tlen[row], CURCOL);
1171 
1172 		PSCR(r, page).flags &= ~Screen_WrapNext;
1173 		if (CURROW == PSCR(r, page).bscroll)
1174 		{
1175 		    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "%s:%d ",
1176 				__FILE__, __LINE__ ));
1177 		    rxvt_scroll_text(r, page, PSCR(r, page).tscroll,
1178 			    PSCR(r, page).bscroll, 1, 0);
1179 		    adjust_view_start( r, page, 1 );
1180 		}
1181 		else if (CURROW < (r->TermWin.nrow - 1))
1182 		    row = (++CURROW) + SVLINES;
1183 
1184 		stp = PSCR(r, page).text[row];  /* _must_ refresh */
1185 		srp = PSCR(r, page).rend[row];  /* _must_ refresh */
1186 		RESET_CHSTAT(r, page);
1187 		continue;
1188 
1189 	    case '\r':
1190 		/* XXX: think about this */
1191 		if (PSCR(r, page).tlen[row] != -1)
1192 		    MAX_IT(PSCR(r, page).tlen[row], CURCOL);
1193 		PSCR(r, page).flags &= ~Screen_WrapNext;
1194 		CURCOL = 0;
1195 		RESET_CHSTAT(r, page);
1196 		continue;
1197 
1198 	    default:
1199 #ifdef MULTICHAR_SET
1200 		if (r->encoding_method == ENC_NOENC)
1201 		{
1202 		    if (c == 127)
1203 			continue;
1204 		    break;
1205 		}
1206 		PVTS(r, page)->rstyle &= ~RS_multiMask;
1207 
1208 		/* multibyte 2nd byte */
1209 		if (PVTS(r, page)->chstat == WBYTE)
1210 		{
1211 		    /* set flag of second byte in style */
1212 		    PVTS(r, page)->rstyle |= RS_multi2;
1213 		    /* switch back to single byte for next char */
1214 		    PVTS(r, page)->chstat = SBYTE;
1215 		    if (
1216 			  (r->encoding_method == ENC_EUCJ)
1217 			  && ((char) stp[CURCOL-1] == (char) 0x8e)
1218 		       )
1219 		    {
1220 			PVTS(r, page)->rstyle &= ~RS_multiMask;
1221 			CURCOL --;
1222 		    }
1223 		    else
1224 		    /* maybe overkill, but makes it selectable */
1225 		    if ((r->encoding_method == ENC_EUCJ) ||
1226 			(r->encoding_method == ENC_GBK) ||
1227 			(r->encoding_method == ENC_GB))
1228 			c |= 0x80;
1229 		}
1230 		/* multibyte 1st byte */
1231 		else if (PVTS(r, page)->chstat == SBYTE)
1232 		{
1233 		    if (r->encoding_method == ENC_SJIS)
1234 		    {
1235 			if (
1236 			      PVTS(r, page)->multi_byte
1237 			      || (
1238 				  (
1239 				   (unsigned char) c >= (unsigned char) 0x81
1240 				   && (unsigned char) c <= (unsigned char) 0x9f
1241 				  )
1242 				  ||
1243 				  (
1244 				   (unsigned char) c >= (unsigned char) 0xe0
1245 				   && (unsigned char) c <= (unsigned char) 0xfc
1246 				  )
1247 				 )
1248 			   )
1249 			{
1250 			    PVTS(r, page)->rstyle |= RS_multi1;
1251 			    PVTS(r, page)->chstat = WBYTE;
1252 			}
1253 		    }
1254 		    else if (PVTS(r, page)->multi_byte || (c & 0x80))
1255 		    {
1256 			/* set flag of first byte in style */
1257 			PVTS(r, page)->rstyle |= RS_multi1;
1258 			/* switch to multiple byte for next char */
1259 			PVTS(r, page)->chstat = WBYTE;
1260 			/* maybe overkill, but makes selectable */
1261 			if (
1262 			      (r->encoding_method == ENC_EUCJ)
1263 			      || (r->encoding_method == ENC_GBK)
1264 			      || (r->encoding_method == ENC_GB)
1265 			   )
1266 			    c |= 0x80;
1267 		    }
1268 		}
1269 		else
1270 #endif
1271 		if (c == 127)
1272 		    continue;	/* yummmm..... */
1273 		break;
1274 	}   /* switch */
1275 
1276 	if (
1277 	      checksel	    /* see if we're writing within selection */
1278 	      && !RC_BEFORE(PSCR(r, page).cur, SEL(r).beg)
1279 	      && RC_BEFORE(PSCR(r, page).cur, SEL(r).end)
1280 	   )
1281 	{
1282 	    checksel = 0;
1283 	    clearsel = 1;
1284 	}
1285 
1286 	if (PSCR(r, page).flags & Screen_WrapNext)
1287 	{
1288 	    PSCR(r, page).tlen[row] = -1;
1289 	    if (CURROW == PSCR(r, page).bscroll)
1290 	    {
1291 		rxvt_scroll_text(r, page, PSCR(r, page).tscroll,
1292 			PSCR(r, page).bscroll, 1, 0);
1293 		adjust_view_start( r, page, 1 );
1294 	    }
1295 	    else if (CURROW < (r->TermWin.nrow - 1))
1296 		row = (++CURROW) + SVLINES;
1297 	    stp = PSCR(r, page).text[row];  /* _must_ refresh */
1298 	    srp = PSCR(r, page).rend[row];  /* _must_ refresh */
1299 	    CURCOL = 0;
1300 	    PSCR(r, page).flags &= ~Screen_WrapNext;
1301 	}
1302 
1303 	if (PSCR(r, page).flags & Screen_Insert)
1304 	    rxvt_scr_insdel_chars(r, page, 1, INSERT);
1305 
1306 #ifdef MULTICHAR_SET
1307 	if (
1308 	      IS_MULTI1(PVTS(r, page)->rstyle)
1309 	      && CURCOL > 0
1310 	      && IS_MULTI1(srp[CURCOL - 1])
1311 	   )
1312 	{
1313 	    stp[CURCOL - 1] = ' ';
1314 	    srp[CURCOL - 1] &= ~RS_multiMask;
1315 	}
1316 	else if (
1317 		  IS_MULTI2(PVTS(r, page)->rstyle)
1318 		  && CURCOL < (last_col - 1)
1319 		  && IS_MULTI2(srp[CURCOL + 1])
1320 		)
1321 	{
1322 	    stp[CURCOL + 1] = ' ';
1323 	    srp[CURCOL + 1] &= ~RS_multiMask;
1324 	}
1325 #endif
1326 
1327 	stp[CURCOL] = c;
1328 	srp[CURCOL] = PVTS(r, page)->rstyle;
1329 	if (CURCOL < (last_col - 1))
1330 	    CURCOL++;
1331 	else
1332 	{
1333 	    PSCR(r, page).tlen[row] = last_col;
1334 	    if (PSCR(r, page).flags & Screen_Autowrap)
1335 		PSCR(r, page).flags |= Screen_WrapNext;
1336 	}
1337     }	/* for */
1338 
1339     if (PSCR(r, page).tlen[row] != -1)	/* XXX: think about this */
1340 	MAX_IT(PSCR(r, page).tlen[row], CURCOL);
1341 
1342     /*
1343     ** If we wrote anywhere in the selected area, kill the selection
1344     ** XXX: should we kill the mark too?  Possibly, but maybe that
1345     **	  should be a similar check.
1346     */
1347     if (clearsel)
1348 	CLEAR_SELECTION(r);
1349 
1350     assert(CURROW >= 0);
1351     MAX_IT(CURROW, 0);
1352 }
1353 
1354 /* ------------------------------------------------------------------------- */
1355 /*
1356  * Process Backspace.  Move back the cursor back a position, wrap if have to
1357  * XTERM_SEQ: CTRL-H
1358  */
1359 /* EXTPROTO */
1360 void
rxvt_scr_backspace(rxvt_t * r,int page)1361 rxvt_scr_backspace(rxvt_t* r, int page)
1362 {
1363     RESET_CHSTAT(r, page);
1364     PVTS(r, page)->want_refresh = 1;
1365     if (CURCOL == 0)
1366     {
1367 	if (CURROW > 0)
1368 	{
1369 #ifdef TERMCAP_HAS_BW
1370 	    CURCOL = r->TermWin.ncol - 1;
1371 	    CURROW--;
1372 	    return;
1373 #endif
1374 	}
1375     } else if ((PSCR(r, page).flags & Screen_WrapNext) == 0)
1376 	rxvt_scr_gotorc(r, page, 0, -1, RELATIVE);
1377     PSCR(r, page).flags &= ~Screen_WrapNext;
1378 }
1379 
1380 /* ------------------------------------------------------------------------- */
1381 /*
1382  * Process Horizontal Tab
1383  * count: +ve = forward; -ve = backwards
1384  * XTERM_SEQ: CTRL-I
1385  */
1386 /* EXTPROTO */
1387 void
rxvt_scr_tab(rxvt_t * r,int page,int count)1388 rxvt_scr_tab(rxvt_t* r, int page, int count)
1389 {
1390     int		 i, x;
1391 
1392     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_tab %d (%d)\n", page, count));
1393     PVTS(r, page)->want_refresh = 1;
1394     RESET_CHSTAT(r, page);
1395     i = x = CURCOL;
1396     if (count == 0)
1397 	return;
1398     else if (count > 0)
1399     {
1400 	for (; ++i < r->TermWin.ncol; )
1401 	    if (r->tabstop[i])
1402 	    {
1403 		x = i;
1404 		if (!--count)
1405 		    break;
1406 	    }
1407 	;
1408 	if (count)
1409 	    x = r->TermWin.ncol - 1;
1410     }
1411     else /* if (count < 0) */
1412     {
1413 	for (; --i >= 0; )
1414 	    if (r->tabstop[i])
1415 	    {
1416 		x = i;
1417 		if (!++count)
1418 		    break;
1419 	    }
1420 	;
1421 	if (count)
1422 	    x = 0;
1423     }
1424 
1425 #if 0
1426     if (x != CURCOL)
1427 	rxvt_scr_gotorc(r, page, 0, x, R_RELATIVE);
1428 #else
1429     /*
1430      * 2006-09-02 gi1242: Don't call rxvt_scr_gotorc() because that might change
1431      * CURROW (if it was negative). If we're adding lines to the screen
1432      * structure, then CURROW is allowed to be negative.
1433      */
1434     CURCOL = x;
1435 #endif
1436 }
1437 
1438 /* ------------------------------------------------------------------------- */
1439 /*
1440  * Process DEC Back Index
1441  * XTERM_SEQ: ESC 6
1442  * Move cursor left in row.  If we're at the left boundary, shift everything
1443  * in that row right.  Clear left column.
1444  */
1445 #ifndef NO_FRILLS
1446 /* EXTPROTO */
1447 void
rxvt_scr_backindex(rxvt_t * r,int page)1448 rxvt_scr_backindex(rxvt_t* r, int page)
1449 {
1450     if (CURCOL > 0)
1451 	rxvt_scr_gotorc(r, page, 0, -1, R_RELATIVE | C_RELATIVE);
1452     else
1453     {
1454 	if (PSCR(r, page).tlen[CURROW + SVLINES] == 0)
1455 	    return;	/* um, yeah? */
1456 	rxvt_scr_insdel_chars(r, page, 1, INSERT);
1457     }
1458 }
1459 #endif
1460 /* ------------------------------------------------------------------------- */
1461 /*
1462  * Process DEC Forward Index
1463  * XTERM_SEQ: ESC 9
1464  * Move cursor right in row.  If we're at the right boundary, shift everything
1465  * in that row left.  Clear right column.
1466  */
1467 #ifndef NO_FRILLS
1468 /* EXTPROTO */
1469 void
rxvt_scr_forwardindex(rxvt_t * r,int page)1470 rxvt_scr_forwardindex(rxvt_t* r, int page)
1471 {
1472     int		 row;
1473 
1474     if (CURCOL < r->TermWin.ncol - 1)
1475 	rxvt_scr_gotorc(r, page, 0, 1, R_RELATIVE | C_RELATIVE);
1476     else
1477     {
1478 	row = CURROW + SVLINES;
1479 	if (PSCR(r, page).tlen[row] == 0)
1480 	    return;	/* um, yeah? */
1481 	else if (PSCR(r, page).tlen[row] == -1)
1482 	    PSCR(r, page).tlen[row] = r->TermWin.ncol;
1483 	rxvt_scr_gotorc(r, page, 0, 0, R_RELATIVE);
1484 	rxvt_scr_insdel_chars(r, page, 1, DELETE);
1485 	rxvt_scr_gotorc(r, 0, page, r->TermWin.ncol - 1, R_RELATIVE);
1486     }
1487 }
1488 #endif
1489 
1490 /* ------------------------------------------------------------------------- */
1491 /*
1492  * Goto Row/Column
1493  */
1494 /* EXTPROTO */
1495 void
rxvt_scr_gotorc(rxvt_t * r,int page,int row,int col,int relative)1496 rxvt_scr_gotorc(rxvt_t* r, int page, int row, int col, int relative)
1497 {
1498     PVTS(r, page)->want_refresh = 1;
1499     ZERO_SCROLLBACK(r, page);
1500     RESET_CHSTAT(r, page);
1501 
1502     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_gotorc %d (r:%s%d,c:%s%d): from (r:%d,c:%d)\n", page, (relative & R_RELATIVE ? "+" : ""), row, (relative & C_RELATIVE ? "+" : ""), col, CURROW, CURCOL));
1503 
1504     CURCOL = ((relative & C_RELATIVE) ? (CURCOL + col)
1505 			 : col);
1506     MAX_IT(CURCOL, 0);
1507     MIN_IT(CURCOL, (int32_t)r->TermWin.ncol - 1);
1508 
1509     PSCR(r, page).flags &= ~Screen_WrapNext;
1510     if (relative & R_RELATIVE)
1511     {
1512 	if (row > 0)
1513 	{
1514 	    if (CURROW <= PSCR(r, page).bscroll &&
1515 		(CURROW + row) > PSCR(r, page).bscroll)
1516 		CURROW = PSCR(r, page).bscroll;
1517 	    else
1518 		CURROW += row;
1519 	}
1520 
1521 	else if (row < 0)
1522 	{
1523 	    if (CURROW >= PSCR(r, page).tscroll &&
1524 		(CURROW + row) < PSCR(r, page).tscroll)
1525 		CURROW = PSCR(r, page).tscroll;
1526 	    else
1527 		CURROW += row;
1528 	}
1529     }
1530     else
1531     {
1532 	if (PSCR(r, page).flags & Screen_Relative)
1533 	{
1534 	    /* relative origin mode */
1535 	    CURROW = row + PSCR(r, page).tscroll;
1536 	    MIN_IT(CURROW, PSCR(r, page).bscroll);
1537 	}
1538 	else
1539 	    CURROW = row;
1540     }
1541     MAX_IT(CURROW, 0);
1542     MIN_IT(CURROW, (int32_t)r->TermWin.nrow - 1);
1543 }
1544 
1545 /* ------------------------------------------------------------------------- */
1546 /*
1547  * direction  should be UP or DN
1548  */
1549 /* EXTPROTO */
1550 void
rxvt_scr_index(rxvt_t * r,int page,enum page_dirn direction)1551 rxvt_scr_index(rxvt_t* r, int page, enum page_dirn direction)
1552 {
1553     int		 dirn;
1554 
1555     PVTS(r, page)->want_refresh = 1;
1556     dirn = ((direction == UP) ? 1 : -1);
1557     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_index %d (%d)\n", page, dirn));
1558 
1559     ZERO_SCROLLBACK(r, page);
1560     RESET_CHSTAT(r, page);
1561 
1562     PSCR(r, page).flags &= ~Screen_WrapNext;
1563     if ((CURROW == PSCR(r, page).bscroll && direction == UP) ||
1564 	(CURROW == PSCR(r, page).tscroll && direction == DN))
1565 	rxvt_scroll_text(r, page, PSCR(r, page).tscroll,
1566 		PSCR(r, page).bscroll, dirn, 0);
1567     else
1568 	CURROW += dirn;
1569     MAX_IT(CURROW, 0);
1570     MIN_IT(CURROW, (int32_t)r->TermWin.nrow - 1);
1571     rxvt_selection_check(r, page, 0);
1572 }
1573 
1574 /* ------------------------------------------------------------------------- */
1575 /*
1576  * Erase part or whole of a line
1577  * XTERM_SEQ: Clear line to right: ESC [ 0 K
1578  * XTERM_SEQ: Clear line to left : ESC [ 1 K
1579  * XTERM_SEQ: Clear whole line   : ESC [ 2 K
1580  */
1581 /* EXTPROTO */
1582 void
rxvt_scr_erase_line(rxvt_t * r,int page,int mode)1583 rxvt_scr_erase_line(rxvt_t* r, int page, int mode)
1584 {
1585     unsigned int    row, col, num;
1586 
1587     PVTS(r, page)->want_refresh = 1;
1588     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_erase_line %d (%d) at screen row: %d\n", page, mode, CURROW));
1589     ZERO_SCROLLBACK(r, page);
1590     RESET_CHSTAT(r, page);
1591     rxvt_selection_check(r, page, 1);
1592 
1593     PSCR(r, page).flags &= ~Screen_WrapNext;
1594 
1595     row = SVLINES + CURROW;
1596     switch (mode)
1597     {
1598 	case 0:		/* erase to end of line */
1599 	    col = CURCOL;
1600 	    num = r->TermWin.ncol - col;
1601 	    MIN_IT(PSCR(r, page).tlen[row], (int16_t)col);
1602 	    if (
1603 		  RC_ROW_ATAFTER(SEL(r).beg, PSCR(r, page).cur)
1604 		  || RC_ROW_ATAFTER(SEL(r).end, PSCR(r, page).cur)
1605 	       )
1606 		CLEAR_SELECTION(r);
1607 	    break;
1608 	case 1:		/* erase to beginning of line */
1609 	    col = 0;
1610 	    num = CURCOL + 1;
1611 	    if (
1612 		  RC_ROW_ATBEFORE(SEL(r).beg, PSCR(r, page).cur)
1613 		  || RC_ROW_ATBEFORE(SEL(r).end, PSCR(r, page).cur)
1614 	       )
1615 		CLEAR_SELECTION(r);
1616 	    break;
1617 	case 2:		/* erase whole line */
1618 	    col = 0;
1619 	    num = r->TermWin.ncol;
1620 	    PSCR(r, page).tlen[row] = 0;
1621 	    if (SEL(r).beg.row <= CURROW && SEL(r).end.row >= CURROW)
1622 		CLEAR_SELECTION(r);
1623 	    break;
1624 	default:
1625 	    return;
1626     }
1627 
1628     if (PSCR(r, page).text[row])
1629 	rxvt_blank_line(&(PSCR(r, page).text[row][col]),
1630 	    &(PSCR(r, page).rend[row][col]), num, PVTS(r, page)->rstyle);
1631     else
1632 	rxvt_blank_screen_mem(r, page, PSCR(r, page).text,
1633 	    PSCR(r, page).rend, row, PVTS(r, page)->rstyle);
1634 }
1635 
1636 /* ------------------------------------------------------------------------- */
1637 /*
1638  * Erase part of whole of the screen
1639  * XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
1640  * XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
1641  * XTERM_SEQ: Clear whole screen	: ESC [ 2 J
1642  */
1643 /* EXTPROTO */
1644 void
rxvt_scr_erase_screen(rxvt_t * r,int page,int mode)1645 rxvt_scr_erase_screen(rxvt_t* r, int page, int mode)
1646 {
1647     int		num;
1648     int32_t	row, row_offset;
1649     rend_t	ren;
1650     XGCValues	gcvalue;
1651 
1652     PVTS(r, page)->want_refresh = 1;
1653     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_erase_screen %d (%d) at screen row: %d\n", page, mode, CURROW));
1654     ZERO_SCROLLBACK(r, page);
1655     RESET_CHSTAT(r, page);
1656     row_offset = (int32_t)SVLINES;
1657 
1658     switch (mode)
1659     {
1660 	case 0:		/* erase to end of screen */
1661 	    rxvt_selection_check(r, page, 1);
1662 	    rxvt_scr_erase_line(r, page, 0);
1663 	    row = CURROW + 1;	/* possible OOB */
1664 	    num = r->TermWin.nrow - row;
1665 	    break;
1666 	case 1:		/* erase to beginning of screen */
1667 	    rxvt_selection_check(r, page, 3);
1668 	    rxvt_scr_erase_line(r, page, 1);
1669 	    row = 0;
1670 	    num = CURROW;
1671 	    break;
1672 	case 2:		/* erase whole screen */
1673 	    /*
1674 	     * 2006-02-15 gi1242: As pointed out by Sabit Sayeed, Gnome terminal
1675 	     * scrolls the text off screen, instead of wiping it out completely.
1676 	     * That's seems much better so let's do it here.
1677 	     */
1678 	    if( PVTS(r, page)->current_screen == PRIMARY )
1679 	    {
1680 		/*
1681 		 * Only scroll if the primary screen is bieng cleared.
1682 		 */
1683 		int sr;
1684 
1685 		/*
1686 		 * Find the last non-empty line to save.
1687 		 */
1688 		for( sr = SVLINES + r->TermWin.nrow - 1; sr >= SVLINES; sr--)
1689 		{
1690 		    int non_empty = 0, sc;
1691 
1692 		    for( sc = 0; sc < PSCR( r, page).tlen[sr]; sc++)
1693 			if (
1694 			      PSCR( r, page).text[sr][sc] != '\0'
1695 			      && PSCR( r, page).text[sr][sc] != ' '
1696 			   )
1697 			{
1698 			    non_empty = 1;
1699 			    break;
1700 			}
1701 		    ;
1702 		    if( non_empty ) break;
1703 		}
1704 
1705 		sr -=  SVLINES /* - 1 */;   /* Dump last non-empty line */
1706 		rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "Saving %d lines\n", sr));
1707 
1708 		if( sr > 0)
1709 		    rxvt_scroll_text(r, page,
1710 			    PSCR(r, page).tscroll, PSCR(r, page).bscroll,
1711 			    sr, 0);
1712 	    }
1713 	    else rxvt_selection_check(r, page, 3);
1714 
1715 	    row = 0;
1716 	    num = r->TermWin.nrow;
1717 	    break;
1718 	default:
1719 	    return;
1720     }
1721 
1722     r->h->refresh_type |= REFRESH_BOUNDS;
1723     if(
1724 	 SEL(r).op && SEL(r).vt == page &&
1725 	 PVTS(r, page)->current_screen == SEL(r).screen &&
1726 	 (
1727 	   (SEL(r).beg.row >= row && SEL(r).beg.row <= row + num) ||
1728 	   (SEL(r).end.row >= row && SEL(r).end.row <= row + num)
1729 	 )
1730       )
1731     {
1732 	CLEAR_SELECTION(r);
1733     }
1734 
1735     if (row >= r->TermWin.nrow)	/* Out Of Bounds */
1736 	return;
1737 
1738     MIN_IT(num, (r->TermWin.nrow - row));
1739     if (PVTS(r, page)->rstyle & (RS_RVid | RS_Uline))
1740 	ren = (rend_t) ~RS_None;
1741 
1742     else if (GET_BASEBG(PVTS(r, page)->rstyle) == Color_bg)
1743     {
1744 	ren = DEFAULT_RSTYLE;
1745 	CLEAR_ROWS(row, num);
1746     }
1747 
1748     else
1749     {
1750 	ren = (PVTS(r, page)->rstyle & (RS_fgMask | RS_bgMask));
1751 
1752 	gcvalue.foreground = r->pixColors[GET_BGCOLOR(PVTS(r, page)->rstyle)];
1753 	XChangeGC(r->Xdisplay, r->TermWin.gc, GCForeground, &gcvalue);
1754 	ERASE_ROWS(row, num);
1755 
1756 	gcvalue.foreground = r->pixColors[Color_fg];
1757 	XChangeGC(r->Xdisplay, r->TermWin.gc, GCForeground, &gcvalue);
1758     }
1759 
1760     for (; num--; row++)
1761     {
1762 	rxvt_blank_screen_mem(r, page, PSCR(r, page).text,
1763 		PSCR(r, page).rend,
1764 		(unsigned int)(row + row_offset), PVTS(r, page)->rstyle);
1765 	PSCR(r, page).tlen[row + row_offset] = 0;
1766 	rxvt_blank_line(PVTS(r, page)->drawn_text[row],
1767 		PVTS(r, page)->drawn_rend[row],
1768 		(unsigned int) r->TermWin.ncol, ren);
1769     }
1770 }
1771 
1772 /* ------------------------------------------------------------------------- */
1773 /*
1774  * Fill the screen with `E's
1775  * XTERM_SEQ: Screen Alignment Test: ESC # 8
1776  */
1777 /* EXTPROTO */
1778 void
rxvt_scr_E(rxvt_t * r,int page)1779 rxvt_scr_E(rxvt_t* r, int page)
1780 {
1781     int		 i, j, k;
1782     rend_t	 *r1, fs;
1783 
1784     PVTS(r, page)->want_refresh = 1;
1785     r->h->num_scr_allow = 0;
1786     ZERO_SCROLLBACK(r, page);
1787     RESET_CHSTAT(r, page);
1788     rxvt_selection_check(r, page, 3);
1789 
1790     fs = PVTS(r, page)->rstyle;
1791     for (k = SVLINES, i = r->TermWin.nrow; i--; k++)
1792     {
1793 	/* make the `E's selectable */
1794 	PSCR(r, page).tlen[k] = r->TermWin.ncol;
1795 	MEMSET(PSCR(r, page).text[k], 'E', r->TermWin.ncol);
1796 	for (r1 = PSCR(r, page).rend[k], j = r->TermWin.ncol; j--; )
1797 	    *r1++ = fs;
1798     }
1799 }
1800 
1801 /* ------------------------------------------------------------------------- */
1802 /*
1803  * Insert/Delete <count> lines
1804  */
1805 /* EXTPROTO */
1806 void
rxvt_scr_insdel_lines(rxvt_t * r,int page,int count,int insdel)1807 rxvt_scr_insdel_lines(rxvt_t* r, int page, int count, int insdel)
1808 {
1809     int		 end;
1810 
1811     ZERO_SCROLLBACK(r, page);
1812     RESET_CHSTAT(r, page);
1813     rxvt_selection_check(r, page, 1);
1814 
1815     if (CURROW > PSCR(r, page).bscroll)
1816 	return;
1817 
1818     end = PSCR(r, page).bscroll - CURROW + 1;
1819     if (count > end)
1820     {
1821 	if (insdel == DELETE)
1822 	    return;
1823 	else if (insdel == INSERT)
1824 	    count = end;
1825     }
1826     PSCR(r, page).flags &= ~Screen_WrapNext;
1827 
1828     rxvt_scroll_text(r, page, CURROW,
1829 	PSCR(r, page).bscroll, insdel * count, 0);
1830 }
1831 
1832 /* ------------------------------------------------------------------------- */
1833 /*
1834  * Insert/Delete <count> characters from the current position
1835  */
1836 /* EXTPROTO */
1837 void
rxvt_scr_insdel_chars(rxvt_t * r,int page,int count,int insdel)1838 rxvt_scr_insdel_chars(rxvt_t* r, int page, int count, int insdel)
1839 {
1840     int		col, row;
1841     rend_t	tr;
1842     text_t*	stp;
1843     rend_t*	srp;
1844     int16_t*	slp;
1845 
1846     PVTS(r, page)->want_refresh = 1;
1847     ZERO_SCROLLBACK(r, page);
1848 #if 0
1849     RESET_CHSTAT(r, page);
1850 #endif
1851     if (count <= 0)
1852 	return;
1853 
1854     rxvt_selection_check(r, page, 1);
1855     MIN_IT(count, (r->TermWin.ncol - CURCOL));
1856 
1857     row = CURROW + SVLINES;
1858     PSCR(r, page).flags &= ~Screen_WrapNext;
1859 
1860     stp = PSCR(r, page).text[row];
1861     srp = PSCR(r, page).rend[row];
1862     slp = &(PSCR(r, page).tlen[row]);
1863     switch (insdel)
1864     {
1865 	case INSERT:
1866 	    for (col = r->TermWin.ncol - 1; (col - count) >= CURCOL; col--)
1867 	    {
1868 		stp[col] = stp[col - count];
1869 		srp[col] = srp[col - count];
1870 	    }
1871 	    if (*slp != -1)
1872 	    {
1873 		*slp += count;
1874 		MIN_IT(*slp, r->TermWin.ncol);
1875 	    }
1876 	    if (
1877 		  SEL(r).op && SEL(r).vt == page
1878 		  && PVTS(r, page)->current_screen == SEL(r).screen
1879 		  && RC_ROW_ATAFTER(SEL(r).beg, PSCR(r, page).cur)
1880 	       )
1881 	    {
1882 		if (
1883 		      SEL(r).end.row != CURROW
1884 		      || (SEL(r).end.col + count >= r->TermWin.ncol)
1885 		   )
1886 		    CLEAR_SELECTION(r);
1887 		else	    /* shift selection */
1888 		{
1889 		    SEL(r).beg.col += count;
1890 		    SEL(r).mark.col += count;	/* XXX: yes? */
1891 		    SEL(r).end.col += count;
1892 		}
1893 	    }
1894 	    rxvt_blank_line(&(stp[CURCOL]),
1895 		&(srp[CURCOL]),
1896 		(unsigned int)count, PVTS(r, page)->rstyle);
1897 	    break;
1898 
1899 	case ERASE:
1900 	    CURCOL += count;	/* don't worry if > r->TermWin.ncol */
1901 	    rxvt_selection_check(r, page, 1);
1902 	    CURCOL -= count;
1903 	    rxvt_blank_line(&(stp[CURCOL]),
1904 		&(srp[CURCOL]),
1905 		(unsigned int)count, PVTS(r, page)->rstyle);
1906 	    break;
1907 
1908 	case DELETE:
1909 	    tr = srp[r->TermWin.ncol - 1]
1910 		 & (RS_fgMask | RS_bgMask | RS_baseattrMask);
1911 	    for (col = CURCOL; (col + count) < r->TermWin.ncol; col++)
1912 	    {
1913 		stp[col] = stp[col + count];
1914 		srp[col] = srp[col + count];
1915 	    }
1916 	    rxvt_blank_line(&(stp[r->TermWin.ncol - count]),
1917 		&(srp[r->TermWin.ncol - count]),
1918 		(unsigned int)count, tr);
1919 	    if (*slp == -1) /* break line continuation */
1920 		*slp = r->TermWin.ncol;
1921 	    *slp -= count;
1922 	    MAX_IT(*slp, 0);
1923 	    if (
1924 		  SEL(r).op && SEL(r).vt == page
1925 		  && PVTS(r, page)->current_screen == SEL(r).screen
1926 		  && RC_ROW_ATAFTER(SEL(r).beg, PSCR(r, page).cur)
1927 	       )
1928 	    {
1929 		if (
1930 		      SEL(r).end.row != CURROW
1931 		      || (CURCOL >= SEL(r).beg.col - count)
1932 		      || SEL(r).end.col >= r->TermWin.ncol
1933 		   )
1934 		    CLEAR_SELECTION(r);
1935 		else
1936 		{
1937 		    /* shift selection */
1938 		    SEL(r).beg.col -= count;
1939 		    SEL(r).mark.col -= count;	/* XXX: yes? */
1940 		    SEL(r).end.col -= count;
1941 		}
1942 	    }
1943 	    break;
1944     }
1945 #if 0
1946     if (IS_MULTI2(srp[0]))
1947     {
1948 	srp[0] &= ~RS_multiMask;
1949 	stp[0] = ' ';
1950     }
1951     if (IS_MULTI1(srp[r->TermWin.ncol - 1]))
1952     {
1953 	srp[r->TermWin.ncol - 1] &= ~RS_multiMask;
1954 	stp[r->TermWin.ncol - 1] = ' ';
1955     }
1956 #endif
1957 }
1958 
1959 /* ------------------------------------------------------------------------- */
1960 /*
1961  * Set the scrolling region
1962  * XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
1963  */
1964 /* EXTPROTO */
1965 void
rxvt_scr_scroll_region(rxvt_t * r,int page,int top,int bot)1966 rxvt_scr_scroll_region(rxvt_t* r, int page, int top, int bot)
1967 {
1968     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN,
1969 		"rxvt_scr_scroll_region( %d, %d, %d)\n", page, top, bot));
1970 
1971     MAX_IT(top, 0);
1972     MIN_IT(bot, (int)r->TermWin.nrow - 1);
1973     if (top > bot)
1974 	return;
1975     PSCR(r, page).tscroll = top;
1976     PSCR(r, page).bscroll = bot;
1977     rxvt_scr_gotorc(r, page, 0, 0, 0);
1978 }
1979 
1980 /* ------------------------------------------------------------------------- */
1981 /*
1982  * Make the cursor visible/invisible
1983  * XTERM_SEQ: Make cursor visible  : ESC [ ? 25 h
1984  * XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
1985  */
1986 /* EXTPROTO */
1987 void
rxvt_scr_cursor_visible(rxvt_t * r,int page,int mode)1988 rxvt_scr_cursor_visible(rxvt_t* r, int page, int mode)
1989 {
1990     PVTS(r, page)->want_refresh = 1;
1991     if (mode)
1992 	PSCR(r, page).flags |= Screen_VisibleCursor;
1993     else
1994 	PSCR(r, page).flags &= ~Screen_VisibleCursor;
1995 }
1996 
1997 /* ------------------------------------------------------------------------- */
1998 /*
1999  * Set/unset automatic wrapping
2000  * XTERM_SEQ: Set Wraparound  : ESC [ ? 7 h
2001  * XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
2002  */
2003 /* EXTPROTO */
2004 void
rxvt_scr_autowrap(rxvt_t * r,int page,int mode)2005 rxvt_scr_autowrap(rxvt_t* r, int page, int mode)
2006 {
2007     if (mode)
2008 	PSCR(r, page).flags |= Screen_Autowrap;
2009     else
2010 	PSCR(r, page).flags &= ~(Screen_Autowrap | Screen_WrapNext);
2011 }
2012 
2013 /* ------------------------------------------------------------------------- */
2014 /*
2015  * Set/unset margin origin mode
2016  * Absolute mode: line numbers are counted relative to top margin of screen
2017  *    and the cursor can be moved outside the scrolling region.
2018  * Relative mode: line numbers are relative to top margin of scrolling region
2019  *    and the cursor cannot be moved outside.
2020  * XTERM_SEQ: Set Absolute: ESC [ ? 6 h
2021  * XTERM_SEQ: Set Relative: ESC [ ? 6 l
2022  */
2023 /* EXTPROTO */
2024 void
rxvt_scr_relative_origin(rxvt_t * r,int page,int mode)2025 rxvt_scr_relative_origin(rxvt_t* r, int page, int mode)
2026 {
2027     if (mode)
2028 	PSCR(r, page).flags |= Screen_Relative;
2029     else
2030 	PSCR(r, page).flags &= ~Screen_Relative;
2031     rxvt_scr_gotorc(r, page, 0, 0, 0);
2032 }
2033 
2034 /* ------------------------------------------------------------------------- */
2035 /*
2036  * Set insert/replace mode
2037  * XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
2038  * XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
2039  */
2040 /* EXTPROTO */
2041 void
rxvt_scr_insert_mode(rxvt_t * r,int page,int mode)2042 rxvt_scr_insert_mode(rxvt_t* r, int page, int mode)
2043 {
2044     if (mode)
2045 	PSCR(r, page).flags |= Screen_Insert;
2046     else
2047 	PSCR(r, page).flags &= ~Screen_Insert;
2048 }
2049 
2050 /* ------------------------------------------------------------------------- */
2051 /*
2052  * Set/Unset tabs
2053  * XTERM_SEQ: Set tab at current column  : ESC H
2054  * XTERM_SEQ: Clear tab at current column: ESC [ 0 g
2055  * XTERM_SEQ: Clear all tabs		 : ESC [ 3 g
2056  */
2057 /* EXTPROTO */
2058 void
rxvt_scr_set_tab(rxvt_t * r,int page,int mode)2059 rxvt_scr_set_tab(rxvt_t* r, int page, int mode)
2060 {
2061     if (mode < 0)
2062 	MEMSET(r->tabstop, 0, r->TermWin.ncol * sizeof(char));
2063     else if (PSCR(r, page).cur.col < r->TermWin.ncol)
2064 	r->tabstop[PSCR(r, page).cur.col] = (mode ? 1 : 0);
2065 }
2066 
2067 /* ------------------------------------------------------------------------- */
2068 /*
2069  * Set reverse/normal video
2070  * XTERM_SEQ: Reverse video: ESC [ ? 5 h
2071  * XTERM_SEQ: Normal video : ESC [ ? 5 l
2072  */
2073 /* EXTPROTO */
2074 void
rxvt_scr_rvideo_mode(rxvt_t * r,int page,int mode)2075 rxvt_scr_rvideo_mode(rxvt_t* r, int page, int mode)
2076 {
2077     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "%s(r, page=%d, mode=%d)\n", __func__, page, mode ));
2078 
2079     if (PVTS(r, page)->rvideo != mode)
2080     {
2081 	PVTS(r, page)->rvideo = mode;
2082 
2083 	SWAP_IT( PVTS(r, page)->p_fg, PVTS(r, page)->p_bg, unsigned long );
2084 #ifdef XFT_SUPPORT
2085 	if( ISSET_OPTION( r, Opt_xft ) )
2086 	    SWAP_IT( PVTS(r, page)->p_xftfg, PVTS(r, page)->p_xftbg, XftColor );
2087 #endif
2088 	if( r->TermWin.fade )
2089 	{
2090 	    SWAP_IT( PVTS(r, page)->p_fgfade, PVTS(r, page)->p_bgfade,
2091 		    unsigned long );
2092 #ifdef XFT_SUPPORT
2093 	    if( ISSET_OPTION( r, Opt_xft ) )
2094 		SWAP_IT( PVTS(r, page)->p_xftfgfade, PVTS(r, page)->p_xftbgfade,
2095 			XftColor );
2096 #endif
2097 	}
2098 
2099 	if( page == ATAB(r) )
2100 	{
2101 	    /* Background colors need to be forcibly reset */
2102 	    r->fgbg_tabnum = -1;
2103 	    rxvt_set_vt_colors( r, ATAB(r) );
2104 	}
2105 
2106 	rxvt_scr_clear( r, page );
2107 	rxvt_scr_touch( r, page, True );
2108     }
2109 }
2110 
2111 /* ------------------------------------------------------------------------- */
2112 /*
2113  * Report current cursor position
2114  * XTERM_SEQ: Report position: ESC [ 6 n
2115  */
2116 /* EXTPROTO */
2117 void
rxvt_scr_report_position(rxvt_t * r,int page)2118 rxvt_scr_report_position(rxvt_t* r, int page)
2119 {
2120     rxvt_tt_printf(r, page, "\033[%d;%dR", CURROW + 1, CURCOL + 1);
2121 }
2122 
2123 /* ------------------------------------------------------------------------- *
2124  *				  FONTS					*
2125  * ------------------------------------------------------------------------- */
2126 
2127 /*
2128  * Set font style
2129  */
2130 /* INTPROTO */
2131 void
rxvt_set_font_style(rxvt_t * r,int page)2132 rxvt_set_font_style(rxvt_t *r, int page)
2133 {
2134     PVTS(r, page)->rstyle &= ~RS_fontMask;
2135     switch (PVTS(r, page)->charsets[PSCR(r, page).charset])
2136     {
2137 	case '0':	    /* DEC Special Character & Line Drawing Set */
2138 	    PVTS(r, page)->rstyle |= RS_acsFont;
2139 	    break;
2140 	case 'A':	    /* United Kingdom (UK) */
2141 	    PVTS(r, page)->rstyle |= RS_ukFont;
2142 	    break;
2143 	case 'B':	    /* United States (USASCII) */
2144 	    break;
2145 	case '<':	    /* Multinational character set */
2146 	    break;
2147 	case '5':	    /* Finnish character set */
2148 	    break;
2149 	case 'C':	    /* Finnish character set */
2150 	    break;
2151 	case 'K':	    /* German character set */
2152 	    break;
2153     }
2154 }
2155 
2156 /* ------------------------------------------------------------------------- */
2157 /*
2158  * Choose a font
2159  * XTERM_SEQ: Invoke G0 character set: CTRL-O
2160  * XTERM_SEQ: Invoke G1 character set: CTRL-N
2161  * XTERM_SEQ: Invoke G2 character set: ESC N
2162  * XTERM_SEQ: Invoke G3 character set: ESC O
2163  */
2164 /* EXTPROTO */
2165 void
rxvt_scr_charset_choose(rxvt_t * r,int page,int set)2166 rxvt_scr_charset_choose(rxvt_t* r, int page, int set)
2167 {
2168     PSCR(r, page).charset = set;
2169     rxvt_set_font_style(r, page);
2170 }
2171 
2172 /* ------------------------------------------------------------------------- */
2173 /*
2174  * Set a font
2175  * XTERM_SEQ: Set G0 character set: ESC ( <C>
2176  * XTERM_SEQ: Set G1 character set: ESC ) <C>
2177  * XTERM_SEQ: Set G2 character set: ESC * <C>
2178  * XTERM_SEQ: Set G3 character set: ESC + <C>
2179  * See set_font_style for possible values for <C>
2180  */
2181 /* EXTPROTO */
2182 void
rxvt_scr_charset_set(rxvt_t * r,int page,int set,unsigned int ch)2183 rxvt_scr_charset_set(rxvt_t* r, int page, int set, unsigned int ch)
2184 {
2185 #ifdef MULTICHAR_SET
2186     PVTS(r, page)->multi_byte = !!(set < 0);
2187     set = abs(set);
2188 #endif
2189     PVTS(r, page)->charsets[set] = (unsigned char)ch;
2190     rxvt_set_font_style(r, page);
2191 }
2192 
2193 
2194 /* ------------------------------------------------------------------------- *
2195  *			MAJOR SCREEN MANIPULATION			  *
2196  * ------------------------------------------------------------------------- */
2197 
2198 /*
2199  * Refresh an area
2200  */
2201 enum
2202 {
2203     PART_BEG = 0,
2204     PART_END,
2205     RC_COUNT
2206 };
2207 
2208 /* EXTPROTO */
2209 void
rxvt_scr_expose(rxvt_t * r,int page,int x,int y,int width,int height,Bool refresh)2210 rxvt_scr_expose(rxvt_t* r, int page,
2211 	int x, int y, int width, int height,
2212 	Bool refresh)
2213 {
2214     int		 i;
2215     row_col_t	   rc[RC_COUNT];
2216 
2217     if (PVTS(r, page)->drawn_text == NULL)  /* sanity check */
2218 	return;
2219 
2220     x = max(x, (int)r->TermWin.int_bwidth);
2221     x = min(x, (int)r->szHint.width);
2222     y = max(y, (int)r->TermWin.int_bwidth);
2223     y = min(y, (int)r->szHint.height);
2224 
2225     /* round down */
2226     rc[PART_BEG].col = Pixel2Col(x);
2227     rc[PART_BEG].row = Pixel2Row(y);
2228     /* round up */
2229     rc[PART_END].col = Pixel2Width(x + width + r->TermWin.fwidth - 1);
2230     rc[PART_END].row = Pixel2Row(y + height + r->TermWin.fheight - 1);
2231 
2232     /* sanity checks */
2233     for (i = PART_BEG; i < RC_COUNT; i++)
2234     {
2235 	MIN_IT(rc[i].col, r->TermWin.ncol - 1);
2236 	MIN_IT(rc[i].row, r->TermWin.nrow - 1);
2237     }
2238 
2239     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_expose %d (x:%d, y:%d, w:%d, h:%d) area (c:%d,r:%d)-(c:%d,r:%d)\n", page, x, y, width, height, rc[PART_BEG].col, rc[PART_BEG].row, rc[PART_END].col, rc[PART_END].row));
2240 
2241 	{
2242 		register int	j = rc[PART_BEG].col;
2243 		register int	k = rc[PART_END].col - rc[PART_BEG].col + 1;
2244 
2245 		for (i = rc[PART_BEG].row; i <= rc[PART_END].row; i++)
2246 		{
2247 			rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, " memset drawn_text[%d][%d], len=%d\n", i, j, k));
2248 			MEMSET(&(PVTS(r, page)->drawn_text[i][j]), 0, k);
2249 		}
2250 	 }
2251 
2252     if (refresh)
2253     {
2254 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "Forcing immediate screen refresh"));
2255 	rxvt_scr_refresh(r, page, SLOW_REFRESH | REFRESH_BOUNDS);
2256     }
2257 }
2258 
2259 
2260 /* ------------------------------------------------------------------------- */
2261 /*
2262  * Refresh the entire screen
2263  */
2264 /* EXTPROTO */
2265 void
rxvt_scr_touch(rxvt_t * r,int page,Bool refresh)2266 rxvt_scr_touch(rxvt_t* r, int page, Bool refresh)
2267 {
2268     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_touch\n"));
2269     rxvt_scr_expose(r, page, 0, 0, VT_WIDTH(r), VT_HEIGHT(r), refresh);
2270 }
2271 
2272 /* ------------------------------------------------------------------------- */
2273 /*
2274  * Move the display so that the line represented by scrollbar value Y is at
2275  * the top of the screen
2276  */
2277 /* EXTPROTO */
2278 int
rxvt_scr_move_to(rxvt_t * r,int page,int y,int len)2279 rxvt_scr_move_to(rxvt_t* r, int page, int y, int len)
2280 {
2281     long	    p = 0;
2282     uint16_t	    oldviewstart;
2283 
2284     oldviewstart = VSTART;
2285     if (y < len)
2286     {
2287 	p = (r->TermWin.nrow + PVTS(r, page)->nscrolled) * (len - y) / len;
2288 	p -= (long)(r->TermWin.nrow - 1);
2289 	p = max(p, 0);
2290     }
2291     VSTART = (uint16_t)min(p, PVTS(r, page)->nscrolled);
2292     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_move_to %d (%d, %d) view_start:%d\n", page, y, len, VSTART));
2293 
2294     return rxvt_scr_change_view(r, page, oldviewstart);
2295 }
2296 
2297 /* ------------------------------------------------------------------------- */
2298 /*
2299  * Page the screen up/down nlines
2300  * direction should be UP or DN
2301  */
2302 /* EXTPROTO */
2303 int
rxvt_scr_page(rxvt_t * r,int page,enum page_dirn direction,int nlines)2304 rxvt_scr_page(rxvt_t* r, int page, enum page_dirn direction, int nlines)
2305 {
2306     int		n;
2307     uint16_t	oldviewstart;
2308 
2309     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_page %d (%s, %d) view_start:%d\n", page, ((direction == UP) ? "UP" : "DN"), nlines, VSTART));
2310 
2311     oldviewstart = VSTART;
2312     if (direction == UP)
2313     {
2314 	n = VSTART + nlines;
2315 	VSTART = min(n, PVTS(r, page)->nscrolled);
2316     }
2317     else
2318     {
2319 	n = VSTART - nlines;
2320 	VSTART = max(n, 0);
2321     }
2322     return rxvt_scr_change_view(r, page, oldviewstart);
2323 }
2324 
2325 
2326 /* INTPROTO */
2327 int
rxvt_scr_change_view(rxvt_t * r,int page,uint16_t oldviewstart)2328 rxvt_scr_change_view(rxvt_t* r, int page, uint16_t oldviewstart)
2329 {
2330     if (VSTART != oldviewstart)
2331     {
2332 	PVTS(r, page)->want_refresh = 1;
2333 	PVTS(r, page)->num_scr -= (VSTART - oldviewstart);
2334     }
2335     return (int)(VSTART - oldviewstart);
2336 }
2337 
2338 
2339 /* ------------------------------------------------------------------------- */
2340 /* EXTPROTO */
2341 void
rxvt_scr_bell(rxvt_t * r,int page)2342 rxvt_scr_bell(rxvt_t *r, int page)
2343 {
2344 #ifndef NO_BELL
2345 
2346 #if defined(THROTTLE_BELL_MSEC) && THROTTLE_BELL_MSEC > 0
2347     /* Maximal number of bell per pre-defined time interval */
2348     static int		    bellcount	= 0;
2349     static struct timeval   lastBell	= {0, 0};
2350     struct timeval	    tvnow	= {0, 0};
2351     long		    tminterval;
2352 
2353 #ifdef HAVE_NANOSLEEP
2354     struct timespec	    rqt;
2355 
2356     rqt.tv_sec = r->TermWin.vBellDuration / 1000000000ul;
2357     rqt.tv_nsec = r->TermWin.vBellDuration % 1000000000ul;
2358 #endif
2359 
2360     if (gettimeofday (&tvnow, NULL) >= 0)
2361     {
2362 	if (0 == lastBell.tv_sec && 0 == lastBell.tv_usec)
2363 	    /* first time bell, try avoid integer overflow */
2364 	    tminterval = 0;
2365 
2366 	else
2367 	    tminterval = (tvnow.tv_sec - lastBell.tv_sec) * 1000 +
2368 			(tvnow.tv_usec - lastBell.tv_usec) / 1000;
2369 
2370 	lastBell = tvnow;
2371 	if (tminterval > THROTTLE_BELL_MSEC)
2372 	    bellcount = 1;
2373 
2374 	else if (bellcount ++ >= THROTTLE_BELL_COUNT)
2375 	    return;
2376     }
2377 #endif	/* THROTTLE_BELL_MSEC && THROTTLE_BELL_MSEC > 0 */
2378 
2379 # ifndef NO_MAPALERT
2380 #  ifdef MAPALERT_OPTION
2381     if (ISSET_OPTION(r, Opt_mapAlert))
2382 #  endif
2383 	XMapWindow(r->Xdisplay, r->TermWin.parent);
2384 # endif
2385     if(
2386 	    ISSET_OPTION(r, Opt_visualBell) ||
2387 	    (
2388 	     ISSET_OPTION( r, Opt_currentTabVBell ) && APAGE(r) == page
2389 	     && r->TermWin.focus
2390 	    )
2391       )
2392     {
2393 	/*
2394 	 * Visual bells don't need to be rung on windows which are not visible.
2395 	 */
2396 	if( APAGE(r) != page  || r->h->refresh_type == NO_REFRESH )
2397 	    return;
2398 
2399 #if defined(TRANSPARENT) || defined(BACKGROUND_IMAGE)
2400 	/*
2401 	 * Reverse video bell doesn't look so good with transparency or a
2402 	 * background pixmap. Flash screen ourselves.
2403 	 */
2404 	if (
2405 # ifdef TRANSPARENT
2406 	      r->h->am_transparent || r->h->am_pixmap_trans
2407 #  ifdef BACKGROUND_IMAGE
2408 	      ||
2409 #  endif
2410 # endif
2411 # ifdef BACKGROUND_IMAGE
2412 	      IS_PIXMAP(PVTS(r, page)->pixmap)
2413 # endif
2414 	   )
2415 	{
2416 	    XGCValues values;
2417 
2418 	    XGetGCValues( r->Xdisplay, r->TermWin.gc,
2419 		    GCForeground | GCFillStyle, &values);
2420 
2421 	    XSetForeground( r->Xdisplay, r->TermWin.gc,
2422 		    r->pixColors[Color_fg] );
2423 	    XSetFillStyle( r->Xdisplay, r->TermWin.gc, FillSolid);
2424 
2425 	    XFillRectangle( r->Xdisplay, PVTS(r, page)->vt, r->TermWin.gc,
2426 		    Row2Pixel(0), Col2Pixel(0),
2427 		    Width2Pixel( r->TermWin.ncol),
2428 		    Height2Pixel( r->TermWin.nrow) );
2429 
2430 	    XChangeGC( r->Xdisplay, r->TermWin.gc,
2431 		    GCForeground | GCFillStyle, &values);
2432 
2433 	    XSync( r->Xdisplay, False);
2434 
2435 #ifdef HAVE_NANOSLEEP
2436 	    if( r->TermWin.vBellDuration )
2437 		nanosleep(&rqt, NULL);
2438 #endif
2439 
2440 	    XClearArea( r->Xdisplay, PVTS(r, page)->vt, 0, 0, 0, 0, True);
2441 	}
2442 	else
2443 #endif /* TRANSPARENT || BACKGROUND_IMAGE */
2444 	{
2445 	    /* refresh also done */
2446 	    rxvt_scr_rvideo_mode(r, page, !PVTS(r, page)->rvideo);
2447 
2448 #ifdef HAVE_NANOSLEEP
2449 	    rxvt_scr_refresh( r, page,  r->h->refresh_type );
2450 	    XSync( r->Xdisplay, False );
2451 	    if( r->TermWin.vBellDuration )
2452 		nanosleep(&rqt, NULL);
2453 #endif
2454 	    rxvt_scr_rvideo_mode(r, page, !PVTS(r, page)->rvideo);
2455 	}
2456     }
2457 
2458     else if( r->h->rs[Rs_bellCommand] && *r->h->rs[Rs_bellCommand] )
2459 	rxvt_async_exec( r, r->h->rs[Rs_bellCommand] );
2460 
2461     else
2462 	XBell(r->Xdisplay, 0);
2463 #endif /* NO_BELL */
2464 }
2465 
2466 /* ------------------------------------------------------------------------- */
2467 
2468 #ifdef PRINTPIPE
2469 /*
2470  * Generate escape sequences (not including the "\e[0") to reproduce screen
2471  * rendition attributes for the foreground / background color. If "fg" is true,
2472  * then sequences for the foreground color are generated, otherwise sequences
2473  * for setting the background color are generated.
2474  *
2475  * Returns a pointer to the character after the escape sequence written. If no
2476  * further attributes are to be added, strip the trailing ";".
2477  */
2478 /* INTPROTO */
2479 char *
escSetColor(char * s,int color,int fg)2480 escSetColor( char *s, int color, int fg)
2481 {
2482     if( color >= minCOLOR && color < minCOLOR + 8 )
2483 	s += sprintf( s, "%c%d;", fg ? '3' : '4', color - minCOLOR );
2484 #ifndef NO_BRIGHTCOLOR
2485     else if( color >= minBrightCOLOR && color <= maxBrightCOLOR )
2486 	s += sprintf( s, "%s%d;", fg ? "9" : "10", color - minBrightCOLOR );
2487 #endif
2488 #ifdef TTY_256COLOR
2489     else if( color >= min256COLOR && color <= max256COLOR )
2490 	s += sprintf( s, "%c8;5;%d;", fg ? '3' : '4',
2491 				color - min256COLOR + 16 );
2492 #endif
2493     else
2494 	assert(0);
2495 
2496     return s;
2497 }
2498 #endif
2499 
2500 /*
2501  * Print the screen into the printer pipe. If fullhist != 0, then the entire
2502  * scroll back buffer is also dumped.
2503  */
2504 /* EXTPROTO */
2505 void
rxvt_scr_printscreen(rxvt_t * r,int page,int fullhist,int pretty,int linecont,const char * pipeName)2506 rxvt_scr_printscreen(rxvt_t* r, int page, int fullhist, int pretty,
2507 	int linecont, const char *pipeName )
2508 {
2509 #ifdef PRINTPIPE
2510     int		row, col, nrows, row_offset;
2511     text_t	*txt;
2512     rend_t	*rnd;
2513 
2514     FILE*	fd;
2515 
2516 
2517     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_printscreen( r, %d, %d, %d, %s )\n", page, fullhist, pretty, pipeName ));
2518 
2519     if ( ( fd = rxvt_popen_printer( r, pipeName ) ) == NULL )
2520 	return;
2521 
2522     nrows	= r->TermWin.nrow;
2523     row_offset	= SVLINES;
2524     if (!fullhist)
2525 	row_offset -= VSTART;
2526     else
2527     {
2528 	nrows += PVTS(r, page)->nscrolled;
2529 	row_offset -= PVTS(r, page)->nscrolled;
2530     }
2531 
2532     for( row=0; row < nrows && !ferror( fd ); row++ )
2533     {
2534 	int lineEnd;
2535 
2536 	txt = PSCR( r, page ).text[ row + row_offset ];
2537 	rnd = PSCR( r, page ).rend[ row + row_offset ];
2538 
2539 	if( linecont && PSCR(r, page).tlen[row+row_offset] == -1 )
2540 	    /* Line continues over. Don't trim trailing spaces */
2541 	    lineEnd = r->TermWin.ncol - 1;
2542 	else
2543 	{
2544 	    /* Trim trailing spaces */
2545 	    for(
2546 		 lineEnd = r->TermWin.ncol - 1;
2547 		 lineEnd >= 0 && isspace( txt[lineEnd] );
2548 		 lineEnd--
2549 	       )
2550 	    ;
2551 	}
2552 
2553 	if( pretty )
2554 	{
2555 	    /* Print colors as escape sequences */
2556 
2557 	    for( col=0; col <= lineEnd; )
2558 	    {
2559 		char	escsq[32];  /* Buffer to hold the escape sequence. 29
2560 				       bytes are enough. */
2561 		char	*s, *t;
2562 		int	start = col;
2563 		int	color;
2564 
2565 		rend_t	rend = rnd[start];
2566 
2567 		/* Get longest string with constant rendition attrs */
2568 
2569 		do
2570 		  {
2571 		    col++;
2572 		  }
2573 		while( col <= lineEnd && rnd[col] == rend );
2574 
2575 
2576 		t = s = escsq + sprintf( escsq, "\e[" );
2577 
2578 		if( rend & RS_Bold )	    s += sprintf( s, "1;" );
2579 		if( rend & RS_Uline )	    s += sprintf( s, "4;" );
2580 		if( rend & RS_Blink )	    s += sprintf( s, "5;" );
2581 		if( rend & RS_RVid )	    s += sprintf( s, "7;" );
2582 
2583 		color = GET_BASEFG( rend );
2584 		if( color != Color_fg )
2585 		    s = escSetColor( s, color, 1 );
2586 
2587 		color = GET_BASEBG( rend );
2588 		if( color != Color_bg )
2589 		    s = escSetColor( s, color, 0 );
2590 
2591 		if( s != t )
2592 		{
2593 		    /*
2594 		     * Some esc seq has been set. Null terminate and Replace
2595 		     * trailing ';' with 'm'
2596 		     */
2597 		    *(s--)  = '\0';
2598 		    *s	    = 'm';
2599 
2600 		    fprintf( fd, "%s%.*s\e[0m", escsq, col-start, txt+start );
2601 		}
2602 
2603 		else
2604 		    fprintf( fd, "%.*s", col - start, txt + start );
2605 
2606 	    }
2607 	} /* if( pretty ) */
2608 
2609 	else
2610 	    /* Vanilla text */
2611 	    fprintf( fd, "%.*s", (lineEnd + 1), txt );
2612 
2613 	if( !linecont || PSCR(r, page).tlen[row+row_offset] != -1 )
2614 	    fputc( '\n', fd );
2615 
2616     } /* for( row ... ) */
2617 
2618     rxvt_pclose_printer(fd);
2619 #endif
2620 }
2621 
2622 
2623 #ifdef TEXT_SHADOW
2624 /*
2625  * If refreshRegion is not None, then we should intersect our clipping with it.
2626  */
2627 /* INTPROTO */
2628 # ifdef XFT_SUPPORT
2629 void
rxvt_set_clipping(rxvt_t * r,XftDraw * xftdraw,GC gc,Region refreshRegion,int x,int y,unsigned width,unsigned height,int * offx,int * offy)2630 rxvt_set_clipping (rxvt_t* r, XftDraw *xftdraw, GC gc, Region refreshRegion,
2631 	int x, int y, unsigned width, unsigned height,
2632 	int* offx, int* offy)
2633 # else
2634 void
2635 rxvt_set_clipping (rxvt_t* r, __attribute__((unused)) void *xftdraw,
2636 	GC gc, Region refreshRegion,
2637 	int x, int y, unsigned width, unsigned height,
2638 	int* offx, int* offy)
2639 # endif
2640 {
2641     /*
2642      * mfont is a flag whether the output string is multi-byte. if so, we need
2643      * to extend its length twice assuming string is 2-byte string
2644      */
2645     XRectangle	rect;
2646     Region	region;
2647 
2648     /* Sanity check */
2649     assert (offx);
2650     assert (offy);
2651 
2652     if (SHADOW_NONE == r->TermWin.shadow_mode)
2653     {
2654 	*offx = *offy = 0;
2655 	return;	/* shortcut */
2656     }
2657 
2658     switch (r->TermWin.shadow_mode)
2659     {
2660 	case SHADOW_TOP:
2661 	    *offx = 0;
2662 	    *offy = -1;
2663 	    break;
2664 	case SHADOW_BOTTOM:
2665 	    *offx = 0;
2666 	    *offy = 1;
2667 	    break;
2668 	case SHADOW_LEFT:
2669 	    *offx = -1;
2670 	    *offy = 0;
2671 	    break;
2672 	case SHADOW_RIGHT:
2673 	    *offx = 1;
2674 	    *offy = 0;
2675 	    break;
2676 	case SHADOW_TOPLEFT:
2677 	    *offx = -1;
2678 	    *offy = -1;
2679 	    break;
2680 	case SHADOW_TOPRIGHT:
2681 	    *offx = 1;
2682 	    *offy = -1;
2683 	    break;
2684 	case SHADOW_BOTLEFT:
2685 	    *offx = -1;
2686 	    *offy = 1;
2687 	    break;
2688 	case SHADOW_BOTRIGHT:
2689 	    *offx = 1;
2690 	    *offy = 1;
2691 	    break;
2692 	default:
2693 	    assert (0);
2694 	    break;
2695     }
2696 
2697 #if 0
2698     rect.width	= Width2Pixel(1) * len * (mfont+1);
2699     rect.height	= Height2Pixel(1) * 1;
2700 
2701     rect.x += x;
2702     rect.y += (y - rect.height);
2703 #endif
2704 
2705     /*
2706      * Left / right dropped pixels are cleaned up by pixel dropping avoidance
2707      * under Xft, so we can add that to our offset.
2708      */
2709     rect.x	=
2710 #ifdef XFT_SUPPORT
2711 		    (ISSET_OPTION(r, Opt_xft) && xftdraw) ? x + *offx :
2712 #endif
2713 		    x;
2714     rect.y	= y;
2715     rect.width	= width;
2716     rect.height	= height;
2717 
2718     region = XCreateRegion();
2719     XUnionRectWithRegion( &rect, region, region);
2720     if (IS_REGION(refreshRegion))
2721 	XIntersectRegion( region, refreshRegion, region);
2722 
2723     XSetRegion( r->Xdisplay, gc, region);
2724     /*
2725      * XSetClipRectangles (r->Xdisplay, gc, x, y-hy, &rectangle, 1, Unsorted);
2726      */
2727 #ifdef XFT_SUPPORT
2728     if( ISSET_OPTION(r, Opt_xft) && xftdraw )
2729 	XftDrawSetClip( xftdraw, region);
2730 #endif
2731 
2732     XDestroyRegion( region );
2733 }
2734 
2735 
2736 /* INTPROTO */
2737 void
2738 # ifdef XFT_SUPPORT
rxvt_free_clipping(rxvt_t * r,XftDraw * xftdraw,GC gc,Region refreshRegion)2739 rxvt_free_clipping (rxvt_t* r, XftDraw* xftdraw, GC gc, Region refreshRegion)
2740 # else
2741 rxvt_free_clipping (rxvt_t* r, void* xftdraw, GC gc, Region refreshRegion)
2742 # endif
2743 {
2744 
2745     if (SHADOW_NONE == r->TermWin.shadow_mode)
2746 	return;	/* shortcut */
2747 
2748     if (IS_REGION(refreshRegion))
2749 	XSetRegion( r->Xdisplay, gc, refreshRegion );
2750     else
2751 	XSetClipMask( r->Xdisplay, gc, None);
2752 
2753 # ifdef XFT_SUPPORT
2754     if( ISSET_OPTION(r, Opt_xft) && xftdraw)
2755 	XftDrawSetClip( xftdraw, refreshRegion );
2756 # endif	/* XFT_SUPPORT */
2757 }
2758 #endif	/* TEXT_SHADOW */
2759 
2760 
2761 void static inline
rxvt_clear_area(rxvt_t * r,int page,int x,int y,unsigned int w,unsigned int h)2762 rxvt_clear_area (rxvt_t* r, int page, int x, int y, unsigned int w, unsigned int h)
2763 {
2764     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "clear area (%d, %d, %d, %d)\n", x,y,w,h));
2765 
2766     XClearArea (r->Xdisplay, drawBuffer, x, y, w, h, False);
2767 }
2768 
2769 
2770 void static inline
rxvt_fill_rectangle(rxvt_t * r,int page,int x,int y,unsigned int w,unsigned int h)2771 rxvt_fill_rectangle (rxvt_t* r, int page, int x, int y, unsigned int w, unsigned int h)
2772 {
2773     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "fill rectangle (%d, %d, %d, %d)\n", x,y,w,h));
2774     XFillRectangle (r->Xdisplay, drawBuffer, r->TermWin.gc, x, y, w, h);
2775 }
2776 
2777 
2778 /* ------------------------------------------------------------------------- */
2779 /*
2780  * Refresh the screen
2781  * PVTS(r, page)->drawn_text/PVTS(r, page)->drawn_rend contain the screen information before the update.
2782  * PSCR(r, page).text/PSCR(r, page).rend contain what the screen will change to.
2783  */
2784 
2785 
2786 #define X11_DRAW_STRING_8	    (1)
2787 #define X11_DRAW_STRING_16	    (2)
2788 #define X11_DRAW_IMAGE_STRING_8	    (3)
2789 #define X11_DRAW_IMAGE_STRING_16    (4)
2790 #define XFT_DRAW_STRING_8	    (5)
2791 #define XFT_DRAW_STRING_16	    (6)
2792 #define XFT_DRAW_STRING_32	    (7)
2793 #define XFT_DRAW_STRING_UTF8	    (8)
2794 #define XFT_DRAW_IMAGE_STRING_8	    (9)
2795 #define XFT_DRAW_IMAGE_STRING_16    (10)
2796 #define XFT_DRAW_IMAGE_STRING_32    (11)
2797 #define XFT_DRAW_IMAGE_STRING_UTF8  (12)
2798 
2799 #ifdef XFT_SUPPORT
2800 #define XFTDRAW_STRING(xdraw, color, font, x, y, str, len)		    \
2801     ( ( rend & RS_acsFont) ?						    \
2802       (xftDrawACSString( r->Xdisplay, d, gc,				    \
2803 			 xftdraw_string,				    \
2804 			 (xdraw), (color), (font), (x), (y),		    \
2805 			 (unsigned char*) (str), (len))) :		    \
2806       (xftdraw_string( (xdraw), (color), (font), (x), (y), (str), (len))))
2807 /*
2808  * len: number of characters to draw. for UTF-8 string, it is the
2809  *      number of characters * 2 of the original 16-bits string
2810  *
2811  * pfont: Weather to use xftpfn (if defined) or not.
2812  *
2813  * refreshRegion: If set, then any changes made to clipping are undone by
2814  * resetting them to this. (Pass None for no region).
2815  */
2816 /* EXTPROTO */
2817 void
rxvt_draw_string_xft(rxvt_t * r,Drawable d,GC gc,Region refreshRegion,rend_t rend,int pfont,XftDraw * win,XftColor * fore,int x,int y,char * str,int len,void (* xftdraw_string)())2818 rxvt_draw_string_xft (rxvt_t* r, Drawable d, GC gc, Region refreshRegion,
2819 	rend_t rend, int pfont,
2820 	XftDraw* win, XftColor* fore, int x, int y, char* str, int len,
2821 	void (*xftdraw_string)())
2822 {
2823     XftFont *font;
2824 
2825     /*
2826      * If "multichar" stuff is needed in tab titles etc, then xftpfont /
2827      * xftPfont must be multichar capable. If that's not an option, then set
2828      * xftpfont to NULL, and the correct multichar font will be used.
2829      */
2830     if( pfont && r->TermWin.xftpfont )
2831     {
2832 	font = ( pfont == USE_BOLD_PFONT) ?
2833 	    r->TermWin.xftPfont : r->TermWin.xftpfont;
2834     }
2835 #ifdef MULTICHAR_SET
2836     else if( xftdraw_string == XftDrawStringUtf8 )
2837 	font = r->TermWin.xftmfont;
2838 #endif
2839     else font = r->TermWin.xftfont;
2840 
2841     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "Draw: 0x%8x %p: '%.40s'\n", rend, font, str ));
2842 
2843 #ifdef MULTICHAR_SET
2844     if( xftdraw_string == XftDrawStringUtf8 )
2845 	len = STRLEN( str);
2846 #endif
2847 
2848 # ifdef TEXT_SHADOW
2849     if (r->h->rs[Rs_textShadow] && SHADOW_NONE != r->TermWin.shadow_mode)
2850     {
2851 	/*
2852 	 * Get the bounding box of the rectangle we would draw, and clip to it.
2853 	 */
2854 	void	    (*xftTextExtents)() = NULL; /* Suppress compile warning */
2855 	XGlyphInfo  extents;
2856 
2857 	int	sx, sy;	/* Shadow offsets */
2858 
2859 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "handling text shadow for %s (%d)\n", str, len));
2860 
2861 	if( xftdraw_string == XftDrawString8 )
2862 	    xftTextExtents = XftTextExtents8;
2863 	else if( xftdraw_string == XftDrawString16)
2864 	    xftTextExtents = XftTextExtents16;
2865 	else if( xftdraw_string == XftDrawString32)
2866 	    xftTextExtents = XftTextExtents32;
2867 	else if( xftdraw_string == XftDrawStringUtf8)
2868 	    xftTextExtents = XftTextExtentsUtf8;
2869 	else
2870 	    assert(0); /* Shouldn't happen */
2871 
2872 	xftTextExtents( r->Xdisplay, font, str, len, &extents);
2873 
2874 	/*
2875 	 * We should ignore extents.height. The height of the drawn text might
2876 	 * be much smaller than the height of the font (which is really what
2877 	 * we've reserved space for.
2878 	 */
2879 	rxvt_set_clipping( r, win, gc, refreshRegion,
2880 		x, y - font->ascent, extents.width - extents.x, font->height,
2881 		&sx, &sy);
2882 
2883 	XFTDRAW_STRING (win, &(r->TermWin.xftshadow),
2884 	    font, x+sx, y+sy, str, len);
2885 	/*
2886 	 * We need to free clipping area, otherwise text on screen may be
2887 	 * clipped unexpectedly. Is there a better way to unset it, say,
2888 	 * XUnsetClipRectangles?
2889 	 */
2890 	rxvt_free_clipping (r, win, gc, refreshRegion);
2891     }
2892 # endif	/* TEXT_SHADOW */
2893 
2894     XFTDRAW_STRING (win, fore, font, x, y, str, len);
2895 }
2896 #undef XFTDRAW_STRING
2897 #endif	/* XFT_SUPPORT */
2898 
2899 
2900 /*
2901  *  len : number of characters in the string
2902  */
2903 /* EXTPROTO */
2904 void
rxvt_draw_string_x11(rxvt_t * r,Window win,GC gc,Region refreshRegion,int x,int y,char * str,int len,int (* draw_string)())2905 rxvt_draw_string_x11 (rxvt_t* r, Window win, GC gc, Region refreshRegion,
2906 	int x, int y, char* str, int len, int (*draw_string)())
2907 {
2908 # ifdef TEXT_SHADOW
2909     while (r->h->rs[Rs_textShadow] && SHADOW_NONE != r->TermWin.shadow_mode)
2910     {
2911 	int	sx, sy;
2912 	XGCValues   gcvalue;
2913 
2914 	int	    (*xtextextents)();
2915 	int	    unused_dir, ascent, descent;
2916 	XCharStruct charstruct;
2917 	GContext    gid = XGContextFromGC( gc );
2918 	XFontStruct *font = XQueryFont( r->Xdisplay, gid);
2919 
2920 	if( font == NULL ) break;
2921 	rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "handling text shadow for %s (%d)\n", str, len));
2922 
2923 	/*
2924 	 * Save the old GC values foreground.
2925 	 */
2926 	XGetGCValues (r->Xdisplay, gc,
2927 		GCForeground | GCBackground | GCFillStyle, &gcvalue);
2928 
2929 	/*
2930 	 * Get the bounding box of the rectangle we would draw, and clip to it.
2931 	 */
2932 	if( draw_string == XDrawImageString || draw_string == XDrawString)
2933 	    xtextextents = XTextExtents;
2934 	else if ( draw_string == XDrawImageString16 ||
2935 		draw_string == XDrawString16)
2936 	    xtextextents = XTextExtents16;
2937 	else
2938 		assert(0); /* Shouldn't happen */
2939 
2940 	xtextextents( font, str, len,
2941 		&unused_dir, &ascent, &descent, &charstruct);
2942 
2943 	/*
2944 	 * If we're using XDrawImageString, then when we draw the actual text,
2945 	 * the shadow will be erased. Clear the rectangle ourselves, and change
2946 	 * to XDrawString.
2947 	 */
2948 	if( draw_string == XDrawImageString ||
2949 		draw_string == XDrawImageString16)
2950 	{
2951 	    XSetForeground( r->Xdisplay, gc, gcvalue.background);
2952 	    XSetFillStyle( r->Xdisplay, gc, FillSolid);
2953 	    XFillRectangle( r->Xdisplay, win, gc,
2954 		    x, y - font->ascent,
2955 		    charstruct.width, font->ascent + font->descent);
2956 
2957 	    if( draw_string == XDrawImageString )
2958 		draw_string = XDrawString;
2959 	    else draw_string = XDrawString16;
2960 	}
2961 
2962 
2963 	/*
2964 	 * Restrict output to the above bounding box.
2965 	 */
2966 	rxvt_set_clipping( r, NULL, gc, refreshRegion,
2967 		x, y - font->ascent,
2968 		charstruct.width, font->ascent + font->descent,
2969 		&sx, &sy);
2970 
2971 	/*
2972 	 * Draw the shadow at the appropriate offset.
2973 	 */
2974 	XSetForeground (r->Xdisplay, gc, r->TermWin.shadow);
2975 	draw_string (r->Xdisplay, win, gc, x+sx, y+sy, str, len);
2976 
2977 	/*
2978 	 * Restore old GC values.
2979 	 */
2980 	XChangeGC( r->Xdisplay, gc,
2981 		GCForeground | GCBackground | GCFillStyle,
2982 		&gcvalue);
2983 
2984 	/*
2985 	 * Unclip drawing for remaining drawing.
2986 	 */
2987 	rxvt_free_clipping (r, NULL, gc, refreshRegion);
2988 
2989 	XFreeFontInfo( NULL, font, 1);
2990 	break;
2991     }
2992 # endif	/* TEXT_SHADOW */
2993 
2994     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "output entire string: %s\n", str));
2995     draw_string (r->Xdisplay, win, gc, x, y, str, len);
2996 }
2997 
2998 
2999 /*
3000 ** Draw the string:
3001 **    x, y:  top left corner of the string
3002 **    str :  actual string to draw
3003 **    len :  actual number of characters in the string. It is NOT
3004 **           the byte length!
3005 **    drawfunc: function to draw string
3006 **    fore, back: color index to r->pixColors array. It is only
3007 **           used by the XFT drawing, not X11 drawing
3008 **    rend:  rendition value (text attributes). Only used for the RS_acsFont
3009 **	     attribute with Xft for correct drawing of ACS graphics characters.
3010 */
3011 /* INTPROTO */
3012 void
rxvt_scr_draw_string(rxvt_t * r,int page,int x,int y,char * str,int len,int drawfunc,uint16_t fore,uint16_t back,rend_t rend,Region refreshRegion)3013 rxvt_scr_draw_string (rxvt_t* r, int page,
3014 	int x, int y, char* str, int len, int drawfunc,
3015 	uint16_t fore, uint16_t back,
3016 	__attribute__((unused)) rend_t rend, Region refreshRegion)
3017 {
3018 #ifdef XFT_SUPPORT
3019     int	    fillback = 0;
3020     int	    adjust;
3021     void    (*xftdraw_string) () = NULL;
3022 
3023     switch (drawfunc)
3024     {
3025 	case	XFT_DRAW_IMAGE_STRING_8:
3026 	    fillback = 1;
3027 	case	XFT_DRAW_STRING_8:
3028 	    xftdraw_string = XftDrawString8; break;
3029 
3030 	case	XFT_DRAW_IMAGE_STRING_16:
3031 	    fillback = 1;
3032 	case	XFT_DRAW_STRING_16:
3033 	    xftdraw_string = XftDrawString16; break;
3034     }
3035 
3036     /*
3037      * adjust is a variable that records whether each character of the string is
3038      * 8 bits or 16 bits
3039      */
3040     adjust = (XftDrawString8 == xftdraw_string) ? 0 : 1;
3041 
3042     if (ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt && xftdraw_string)
3043     {
3044 	register int	loop;	    /* loop iteration number */
3045 	register int	loopitem;   /* each iteration increasing # */
3046 	register int	i;
3047 	/*
3048 	** xft_draw_string_xft should call these two parameters
3049 	*/
3050 	register char*	pstr;	    /* string to print */
3051 	register int	plen;	    /* string length */
3052 	char*		newstr;
3053 #ifdef MULTICHAR_SET
3054 #  ifdef HAVE_ICONV_H
3055 	char		pbuf[1024]; /* buffer to save UTF-8 string */
3056 #  endif
3057 #endif
3058 
3059 
3060 	/*
3061 	 * Xft does not support XDrawImageString, so we need to clear the
3062 	 * background of text by ourselves.
3063 	 */
3064 	if (fillback)
3065 	    XftDrawRect( PVTS(r, page)->xftvt, &(r->xftColors[back]),
3066 		    x, y, Width2Pixel(len * (1 + adjust)), Height2Pixel(1));
3067 
3068 	/* We use TermWin.xftfont->ascent here */
3069 	y += r->TermWin.xftfont->ascent;
3070 
3071 	/*
3072 	 * Xft does not support XftDrawString16, so we need to convert the
3073 	 * string to UTF-8. Here we reencode the string before conversion
3074 	 */
3075 # ifdef MULTICHAR_SET
3076 #  ifdef HAVE_ICONV_H
3077 	if (adjust && (iconv_t) -1 != r->TermWin.xfticonv)
3078 	{
3079 	    register int    j, newlen = (len << 1);
3080 	    switch (r->encoding_method)
3081 	    {
3082 		case ENC_EUCJ:
3083 		case ENC_GB:
3084 		    for (j = 0; j < newlen; j ++)
3085 			str[j] |= 0x80;
3086 		    break;
3087 		case ENC_GBK:	/* need to do nothing */
3088 		case ENC_BIG5:	/* need to do nothing */
3089 		default:
3090 		    break;
3091 	    }
3092 	    /* we will use utf8 routine to draw string */
3093 	    xftdraw_string = XftDrawStringUtf8;
3094 	}
3095 #  endif
3096 # endif	/* MULTICHAR_SET */
3097 
3098 
3099 	/*
3100 	** If the font is monospace, we print the entire string once,
3101 	** otherwise, print the characters one by one
3102 	*/
3103 	if (r->TermWin.xftmono)
3104 	{
3105 	    /* print string once for mono font */
3106 	    loop = 1;
3107 	    loopitem = len;
3108 	    /*
3109 	    ** If XftDrawString8 == xftdraw_string
3110 	    **     string length == character number
3111 	    ** If XftDrawString16 == xftdraw_string
3112 	    **     string length == 2 * character number, but we do
3113 	    ** not need to multiply loopitem by 2 because  the
3114 	    ** XftDrawString16 takes character numbers.
3115 	    **
3116 	    ** If XftDrawStringUtf8 == xftdraw_string
3117 	    **     string length == 2 * character number, but we need
3118 	    ** to multiply loopitem by 2 because iconv need string
3119 	    ** length as parameter, not character number.
3120 	    */
3121 	    if (XftDrawStringUtf8 == xftdraw_string)
3122 		loopitem <<= 1;
3123 	    rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "output entire mono string\n"));
3124 	}
3125 	/*
3126 	** Non monospace font, but still we can improve the performance
3127 	** by print it once under certain conditions
3128 	*/
3129 # ifdef MULTICHAR_SET
3130 	else
3131 	if (
3132 	      NOTSET_OPTION(r, Opt2_xftSlowOutput)
3133 	      && (XftDrawStringUtf8 == xftdraw_string)
3134 	      && (
3135 		  r->TermWin.xftmfont->max_advance_width ==
3136 		  	(r->TermWin.fwidth << 1)
3137 		 )
3138 	   )
3139 	{
3140 	    /* print string once for multichar string */
3141 	    loop = 1;
3142 	    /*
3143 	    ** If XftDrawStringUtf8 == xftdraw_string
3144 	    **     string length == 2 * character number, but we need
3145 	    ** to multiply loopitem by 2 because iconv need string
3146 	    ** length as parameter, not character number.
3147 	    */
3148 	    loopitem = (len << 1);
3149 	    rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "output entire UTF-8 string\n"));
3150 	}
3151 	else
3152 	if (
3153 	      NOTSET_OPTION(r, Opt2_xftSlowOutput)
3154 	      && (XftDrawString16 == xftdraw_string)
3155 	      && (
3156 		  r->TermWin.xftmfont->max_advance_width ==
3157 		  	(r->TermWin.fwidth << 1)
3158 		 )
3159 	   )
3160 	{
3161 	    /* print string once for 16-bits string */
3162 	    loop = 1;
3163 	    loopitem = len;
3164 	    rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "output entire 16-bits string\n"));
3165 	}
3166 # endif	/* MULTICHAR_SET */
3167 	else
3168 	if (
3169 	      r->TermWin.xftfnmono && (XftDrawString8 == xftdraw_string)
3170 	      && (r->TermWin.xftfont->max_advance_width == r->TermWin.fwidth)
3171 	   )
3172 	{
3173 	    /* print string once for 8-bits string */
3174 	    loop = 1;
3175 	    loopitem = len;
3176 	    rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "output entire 8-bits string\n"));
3177 	}
3178 	else
3179 	{
3180 	    /* print string one by one character */
3181 	    loop = len;
3182 	    loopitem = 1 + adjust;
3183 	    rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "output characters one by one\n"));
3184 	}
3185 
3186 
3187 	newstr = str;	/* string beginning in each iteration */
3188 	for (i = 0; i < loop; i ++)
3189 	{
3190 # ifdef MULTICHAR_SET
3191 #  ifdef HAVE_ICONV_H
3192 	    if (XftDrawStringUtf8 == xftdraw_string)
3193 	    {
3194 		/* We should convert the string to UTF-8 */
3195 		char*	buf = pbuf;		/* always init it */
3196 		int	buflen = sizeof(pbuf)-1;/* always init it */
3197 		int	newlen = loopitem;	/* always init it */
3198 		char*	oldstr = newstr;
3199 		iconv (r->TermWin.xfticonv, (char**)(&newstr),
3200 		    (size_t*) &newlen, &buf, (size_t*) &buflen);
3201 		*buf = (char) 0;    /* set end of string */
3202 		pstr = pbuf;
3203 		/*
3204 		 * we should use the length of original string, not UTF-8 string
3205 		 * here!!!
3206 		 */
3207 		plen = loopitem;
3208 		/*
3209 		 * reset newstr to old position, we will increase it later
3210 		 */
3211 		newstr = oldstr;
3212 	    }
3213 	    else
3214 #  endif
3215 # endif	/* MULTICHAR_SET */
3216 	    {
3217 		/* We do not need to convert the string to UTF-8 */
3218 		pstr = newstr;
3219 		plen = loopitem;
3220 	    }
3221 
3222 	    rxvt_draw_string_xft(r, PVTS( r, page)->vt, r->TermWin.gc,
3223 		    refreshRegion, rend, NO_PFONT,
3224 		    PVTS(r, page)->xftvt, &(r->xftColors[fore]),
3225 		    x, y, pstr, plen, xftdraw_string);
3226 
3227 	    x += Width2Pixel (loopitem);
3228 	    newstr += loopitem;	/* next string to display */
3229 	}
3230     }
3231     else
3232 #endif	/* XFT_SUPPORT */
3233     {
3234 	int	(*draw_string) ();
3235 	switch (drawfunc)
3236 	{
3237 	    case    X11_DRAW_STRING_8:
3238 		draw_string = XDrawString; break;
3239 	    case    X11_DRAW_STRING_16:
3240 		draw_string = XDrawString16; break;
3241 	    case    X11_DRAW_IMAGE_STRING_8:
3242 		draw_string = XDrawImageString; break;
3243 	    case    X11_DRAW_IMAGE_STRING_16:
3244 		draw_string = XDrawImageString16; break;
3245 
3246 	    case    XFT_DRAW_STRING_8:	    /* fall back to X11 */
3247 		draw_string = XDrawString; break;
3248 	    case    XFT_DRAW_STRING_16:	    /* fall back to X11 */
3249 	    case    XFT_DRAW_STRING_32:	    /* fall back to X11 */
3250 	    case    XFT_DRAW_STRING_UTF8:   /* fall back to X11 */
3251 		draw_string = XDrawString16; break;
3252 
3253 	    case    XFT_DRAW_IMAGE_STRING_8:	/* fall back to X11 */
3254 		draw_string = XDrawImageString; break;
3255 	    case    XFT_DRAW_IMAGE_STRING_16:	/* fall back to X11 */
3256 	    case    XFT_DRAW_IMAGE_STRING_32:	/* fall back to X11 */
3257 	    case    XFT_DRAW_IMAGE_STRING_UTF8:	/* fall back to X11 */
3258 		draw_string = XDrawImageString16; break;
3259 
3260 	    default:
3261 		draw_string = NULL; break;
3262 	}
3263 
3264 	/* We use TermWin.font->ascent here */
3265 	y += r->TermWin.font->ascent;
3266 
3267 	/* Now draw the string */
3268 	if (draw_string)
3269 	    rxvt_draw_string_x11 (r, PVTS(r, page)->vt, r->TermWin.gc,
3270 		    refreshRegion, x, y, str, len, draw_string);
3271     }
3272 }
3273 
3274 
3275 /*
3276  * 2006-08-19 gi1242: Don't display blinking text with the bold attribute. This
3277  * causes problems all over the code: When we unset the bold attribute, thinking
3278  * "we've taken care of it", the blink attribute might cause us to do over
3279  * striking. Plus it causes trouble in the pixel dropping avoidance stuff.
3280  */
3281 #define MONO_BOLD(x)							    \
3282     (ISSET_OPTION(r, Opt2_veryBold) ?					    \
3283      ((x) & RS_Bold) :							    \
3284      (((x) & (RS_Bold | RS_fgMask)) == (RS_Bold | Color_fg))		    \
3285     )
3286 #define MONO_BOLD_FG(x, fg)						    \
3287     (ISSET_OPTION(r, Opt2_veryBold) ?					    \
3288      ((x) & RS_Bold) :							    \
3289      (((x) & RS_Bold) && (fg) == Color_fg)				    \
3290     )
3291 
3292 
3293 #define FONT_WIDTH(X, Y)						    \
3294     (X)->per_char[(Y) - (X)->min_char_or_byte2].width
3295 #define FONT_RBEAR(X, Y)						    \
3296     (X)->per_char[(Y) - (X)->min_char_or_byte2].rbearing
3297 #define FONT_LBEAR(X, Y)						    \
3298     (X)->per_char[(Y) - (X)->min_char_or_byte2].lbearing
3299 #define IS_FONT_CHAR(X, Y)						    \
3300     ((Y) >= (X)->min_char_or_byte2 && (Y) <= (X)->max_char_or_byte2)
3301 
3302 /*
3303  * This function needs to be optimized and sped up more
3304  *
3305  * 2006-02-11 gi1242: Added a CLIPPED_REFRESH option. If set, only clipped areas
3306  * are redrawn. This adds a TREMENDOUS speedup when dragging some window over
3307  * mrxvt's window. (Not to mention got rid of that annoying flicker).
3308  */
3309 /* EXTPROTO */
3310 void
rxvt_scr_refresh(rxvt_t * r,int page,unsigned char refresh_type)3311 rxvt_scr_refresh(rxvt_t* r, int page, unsigned char refresh_type)
3312 {
3313     unsigned char
3314 		clearfirst, /* first character writes before cell */
3315 		clearlast,  /* last character writes beyond cell */
3316 		must_clear, /* use drawfunc not image_drawfunc */
3317 		already_cleared=0, /* Use XClearArea or no-op */
3318 #ifndef NO_BOLDFONT
3319 		usingBoldFt, /* we've changed font to bold font */
3320 		loadedBoldFt,	/* If a bold font is loaded */
3321 #endif
3322 		rvid,	    /* reverse video this position */
3323 		wbyte,	    /* we're in multibyte */
3324 		showcursor; /* show the cursor */
3325 
3326 
3327     signed char	morecur = 0;/* */
3328 #ifdef TTY_256COLOR
3329     uint16_t	fore, back; /* desired foreground/background */
3330 #else
3331     unsigned char
3332 		fore, back; /* desired foreground/background */
3333 #endif
3334     int16_t	col, row,   /* column/row we're processing */
3335 		ocrow,	    /* old cursor row */
3336 		len, wlen;  /* text length screen/buffer */
3337     int		i,	    /* tmp */
3338 		row_offset; /* basic offset in screen structure */
3339 #ifndef NO_CURSORCOLOR
3340     rend_t	cc1 = 0;    /* store colours at cursor position(s) */
3341 # ifdef MULTICHAR_SET
3342     rend_t	cc2 = 0;    /* store colours at cursor position(s) */
3343 			    /* 2007-07-30 gi1242: NULL assignment to suppress
3344 			     * compile warning. */
3345 # endif
3346 #endif
3347     XGCValues	gcvalue;    /* Graphics Context values */
3348     XFontStruct*    wf;	    /* font structure */
3349     rend_t*	drp;	    /* drawn_rend pointer */
3350     rend_t*	srp;	    /* screen-rend-pointer */
3351     text_t*	dtp;	    /* drawn-text pointer */
3352     text_t*	stp;	    /* screen-text-pointer */
3353     char*	buffer;	    /* local copy of r->h->buffer */
3354     /*
3355     int		(*drawfunc) () = XDrawString;
3356     int		(*image_drawfunc) () = XDrawImageString;
3357     */
3358     int		drawfunc, image_drawfunc;
3359     struct rxvt_hidden *h = r->h;
3360 
3361     if( !(refresh_type & CLIPPED_REFRESH) )
3362 	PVTS( r, page)->scrolled_lines = 0;
3363 
3364     if (refresh_type == NO_REFRESH || !PVTS(r, page)->mapped)
3365     {
3366 	rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "Skipping refresh (%d, %d)\n", refresh_type, PVTS(r, page)->mapped));
3367 
3368 	return;
3369     }
3370 
3371     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_refresh %d ()\n", page));
3372 
3373     /*
3374     ** A: set up vars
3375     */
3376 #ifdef XFT_SUPPORT
3377     if (ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt)
3378     {
3379 	drawfunc = XFT_DRAW_STRING_8;
3380 	image_drawfunc = XFT_DRAW_IMAGE_STRING_8;
3381     }
3382     else
3383 #endif
3384     {
3385 	drawfunc = X11_DRAW_STRING_8;
3386 	image_drawfunc = X11_DRAW_IMAGE_STRING_8;
3387     }
3388 
3389     clearfirst = clearlast = must_clear = wbyte = 0;
3390 #ifndef NO_BOLDFONT
3391     usingBoldFt = 0;
3392 
3393     /* Determine if we have a bold font loaded or not */
3394     if(
3395 # ifdef XFT_SUPPORT
3396         ISSET_OPTION( r, Opt_xft )		    ?
3397 	    NOT_NULL( r->TermWin.xftbfont )	    :
3398 	    NOT_NULL( r->TermWin.bfont )
3399 # else
3400         NOT_NULL( r->TermWin.bfont )
3401 # endif
3402       )
3403 	loadedBoldFt = 1;
3404     else
3405 	loadedBoldFt = 0;
3406 #endif /* NO_BOLDFONT */
3407 
3408     if (h->currmaxcol < r->TermWin.ncol)
3409     {
3410 	h->currmaxcol = r->TermWin.ncol;
3411 	h->buffer = rxvt_realloc(h->buffer, sizeof(char) * (h->currmaxcol + 1));
3412     }
3413     buffer = h->buffer;
3414 
3415     row_offset = SVLINES - VSTART;
3416 #ifdef XFT_SUPPORT
3417     if (!(ISSET_OPTION(r, Opt_xft) && r->TermWin.xftfont))
3418 #endif
3419     {
3420 	/* always go back to the base font - it's much safer */
3421 	XSetFont(r->Xdisplay, r->TermWin.gc, r->TermWin.font->fid);
3422 	wf = r->TermWin.font;
3423     }
3424 
3425     if ((refresh_type & REFRESH_BOUNDS))
3426     {
3427 	clearfirst = clearlast = 1;
3428 	h->refresh_type &= ~REFRESH_BOUNDS;
3429     }
3430 #ifdef BACKGROUND_IMAGE
3431     must_clear |= IS_PIXMAP(PVTS(r, page)->bg.pixmap);
3432 #endif
3433 #ifdef TRANSPARENT
3434     must_clear |= ( h->am_transparent || h->am_pixmap_trans );
3435 #endif
3436     /* is there an old outline cursor on screen? */
3437     ocrow = h->oldcursor.row;
3438 
3439     /*
3440      * set base colours to avoid check in "single glyph writing" below
3441      */
3442     gcvalue.foreground = r->pixColors[Color_fg];
3443     gcvalue.background = r->pixColors[Color_bg];
3444 
3445     /*
3446      * Set clippings on our XftDrawables and GC's to make sure we don't waste
3447      * time drawing pixels outside this clipping. (This probably happened
3448      * because of an expose event).
3449      */
3450     if( (refresh_type & CLIPPED_REFRESH) && IS_REGION(h->refreshRegion))
3451     {
3452 	rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "Doing clipped refresh (Region %p)\n", h->refreshRegion));
3453 
3454 	/*
3455 	 * We must wait till refresh is complete before destroying the
3456 	 * region because the clipping is reset when text shadow is used.
3457 	 */
3458 	XSetRegion( r->Xdisplay, r->TermWin.gc, h->refreshRegion);
3459 #ifdef XFT_SUPPORT
3460 	if( ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt )
3461 	    XftDrawSetClip( PVTS(r, page)->xftvt, h->refreshRegion);
3462 #endif
3463 	/* Remember we don't need to call XClearArea on exposed regions */
3464 	if( must_clear ) already_cleared = 1;
3465     }
3466 
3467     /*
3468     ** B: reverse any characters which are selected
3469     */
3470     rxvt_scr_reverse_selection(r, page);
3471 
3472 
3473     /*
3474     ** C: set the cursor character(s)
3475     */
3476     {
3477 	unsigned char	setoldcursor;
3478 	rend_t		ccol1,	/* Cursor colour */
3479 			ccol2;	/* Cursor colour2 */
3480 
3481 	showcursor = (PSCR(r, page).flags & Screen_VisibleCursor);
3482 #ifdef CURSOR_BLINK
3483 	if (h->hidden_cursor)
3484 	    showcursor = 0;
3485 #endif
3486 	if (showcursor && r->TermWin.focus)
3487 	{
3488 	    int	    currow = CURROW + SVLINES;
3489 	    srp = &(PSCR(r, page).rend[currow][CURCOL]);
3490 
3491 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "Setting solid cursor\n"));
3492 
3493 	    *srp ^= RS_RVid;
3494 
3495 #ifndef NO_CURSORCOLOR
3496 	    cc1 = *srp & (RS_fgMask | RS_bgMask);
3497 	    if (XDEPTH > 2 && ISSET_PIXCOLOR(h, Color_cursor))
3498 		ccol1 = Color_cursor;
3499 	    else
3500 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
3501 		ccol1 = GET_FGCOLOR(
3502 			    PVTS(r, page)->drawn_rend[CURROW][CURCOL]);
3503 		/*
3504 		ccol1 = GET_FGCOLOR(PVTS(r, page)->rstyle);
3505 		*/
3506 #else
3507 		ccol1 = Color_fg;
3508 #endif
3509 	    if(
3510 		 XDEPTH > 2
3511 		 && ISSET_PIXCOLOR( h, Color_cursor )
3512 		 && ISSET_PIXCOLOR( h, Color_cursor2 )
3513 	      )
3514 		ccol2 = Color_cursor2;
3515 	    else
3516 	    {
3517 #ifdef CURSOR_COLOR_IS_RENDITION_COLOR
3518 # ifdef SIMULATE_LINUX_CONSOLE_CURSOR_COLOR
3519 		ccol2 = GET_FGCOLOR(PVTS(r, page)->drawn_rend[CURROW][CURCOL]);
3520 # else
3521 		ccol2 = GET_BGCOLOR(PVTS(r, page)->drawn_rend[CURROW][CURCOL]);
3522 # endif	/* SIMULATE_LINUX_CONSOLE_CURSOR_COLOR */
3523 		/*
3524 		ccol2 = GET_BGCOLOR(PVTS(r, page)->rstyle);
3525 		*/
3526 #else
3527 		ccol2 = Color_bg;
3528 #endif
3529 	    }
3530 
3531 	    *srp = SET_FGCOLOR(*srp, ccol1);
3532 	    *srp = SET_BGCOLOR(*srp, ccol2);
3533 #endif /* NO_CURSORCOLOR */
3534 
3535 #ifdef MULTICHAR_SET
3536 	    if (IS_MULTI1(*srp))
3537 	    {
3538 		if (CURCOL < r->TermWin.ncol - 2 && IS_MULTI2(*++srp))
3539 		    morecur = 1;
3540 	    }
3541 	    else if (IS_MULTI2(*srp))
3542 	    {
3543 		if (CURCOL > 0 && IS_MULTI1(*--srp))
3544 		    morecur = -1;
3545 	    }
3546 	    if (morecur)
3547 	    {
3548 		*srp ^= RS_RVid;
3549 # ifndef NO_CURSORCOLOR
3550 		cc2 = *srp & (RS_fgMask | RS_bgMask);
3551 		*srp = SET_FGCOLOR(*srp, ccol1);
3552 		*srp = SET_BGCOLOR(*srp, ccol2);
3553 # endif
3554 	    }
3555 #endif
3556 	}
3557 
3558 	/* make sure no outline cursor is left around */
3559 	setoldcursor = 0;
3560 	if (ocrow != -1)
3561 	{
3562 	    if (CURROW + VSTART != ocrow || CURCOL != h->oldcursor.col)
3563 	    {
3564 		if (
3565 		      ocrow < r->TermWin.nrow
3566 		      && h->oldcursor.col < r->TermWin.ncol
3567 		   )
3568 		{
3569 		    PVTS(r, page)->drawn_rend[ocrow][h->oldcursor.col] ^=
3570 			(RS_RVid | RS_Uline);
3571 #ifdef MULTICHAR_SET
3572 		    if (h->oldcursormulti)
3573 		    {
3574 			col = h->oldcursor.col + h->oldcursormulti;
3575 			if (col < r->TermWin.ncol)
3576 			    PVTS(r, page)->drawn_rend[ocrow][col] ^=
3577 				(RS_RVid | RS_Uline);
3578 		    }
3579 #endif
3580 		}
3581 		if (r->TermWin.focus || !showcursor)
3582 		    h->oldcursor.row = -1;
3583 		else
3584 		    setoldcursor = 1;
3585 	    }
3586 	}
3587 	else if (!r->TermWin.focus)
3588 	    setoldcursor = 1;
3589 
3590 	if (setoldcursor)
3591 	{
3592 	    if (CURROW + VSTART >= r->TermWin.nrow)
3593 		h->oldcursor.row = -1;
3594 	    else
3595 	    {
3596 		h->oldcursor.row = CURROW + VSTART;
3597 		h->oldcursor.col = CURCOL;
3598 #ifdef MULTICHAR_SET
3599 		h->oldcursormulti = morecur;
3600 #endif
3601 	    }
3602 	}
3603     }
3604     /* End of C */
3605 
3606 
3607 #ifndef NO_SLOW_LINK_SUPPORT
3608     /*
3609     ** D: CopyArea pass - very useful for slower links
3610     **	This has been deliberately kept simple.
3611     */
3612     i = PVTS(r, page)->num_scr;
3613     if (refresh_type == FAST_REFRESH &&
3614 	    h->num_scr_allow &&
3615 	    i &&
3616 	    abs(i) < r->TermWin.nrow &&
3617 	    !must_clear)
3618     {
3619 	int16_t	     nits;
3620 	int	     j;
3621 	rend_t	     *drp2;
3622 	text_t	     *dtp2;
3623 
3624 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "Trying slowlink copyarea pass\n"));
3625 
3626 	j = r->TermWin.nrow;
3627 	wlen = len = -1;
3628 	row = i > 0 ? 0 : j - 1;
3629 	for (; j-- >= 0; row += (i > 0 ? 1 : -1))
3630 	{
3631 	    if (row + i >= 0 && row + i < r->TermWin.nrow && row + i != ocrow)
3632 	    {
3633 		stp = PSCR(r, page).text[row + row_offset];
3634 		srp = PSCR(r, page).rend[row + row_offset];
3635 		dtp = PVTS(r, page)->drawn_text[row];
3636 		dtp2 = PVTS(r, page)->drawn_text[row + i];
3637 		drp = PVTS(r, page)->drawn_rend[row];
3638 		drp2 = PVTS(r, page)->drawn_rend[row + i];
3639 		for (nits = 0, col = r->TermWin.ncol; col--; )
3640 		    if (stp[col] != dtp2[col] ||
3641 			srp[col] != drp2[col])
3642 			nits--;
3643 		    else if (stp[col] != dtp[col] ||
3644 			srp[col] != drp[col])
3645 			nits++;
3646 		if (nits > 8)	    /* XXX: arbitrary choice */
3647 		{
3648 		    for (col = r->TermWin.ncol; col--; )
3649 		    {
3650 			*dtp++ = *dtp2++;
3651 			*drp++ = *drp2++;
3652 		    }
3653 		    if (len == -1)
3654 		    len = row;
3655 		    wlen = row;
3656 		    continue;
3657 		}
3658 	    }
3659 
3660 	    if (len != -1)
3661 	    {
3662 		/* also comes here at end if needed because of >= above */
3663 		if (wlen < len)
3664 		    SWAP_IT(wlen, len, int);
3665 
3666 		rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_scr_refresh %d (): " "XCopyArea: %d -> %d (height: %d)\n", page, len + i, len, wlen - len + 1));
3667 		XCopyArea(r->Xdisplay, PVTS(r, page)->vt,
3668 		    PVTS(r, page)->vt, r->TermWin.gc,
3669 		    0, Row2Pixel(len + i),
3670 		    TWIN_WIDTH(r),
3671 		    (unsigned int)Height2Pixel(wlen-len+1),
3672 		    0, Row2Pixel(len));
3673 		len = -1;
3674 	    }
3675 	}
3676     }	/* End of D */
3677 #endif	/* !NO_SLOW_LINK_SUPPORT */
3678 
3679 
3680     /*
3681     ** E: main pass across every character
3682     */
3683     for (row = 0; row < r->TermWin.nrow; row++)
3684     {
3685 	unsigned char	clear_next = 0;
3686 	int		j,
3687 			/* x offset for start of drawing (font) */
3688 			xpixel,
3689 			/* y offset for top of drawing */
3690 			ypixelc;
3691 	unsigned long	gcmask;	 /* Graphics Context mask */
3692 
3693 
3694 	stp = PSCR(r, page).text[row + row_offset];
3695 	srp = PSCR(r, page).rend[row + row_offset];
3696 	dtp = PVTS(r, page)->drawn_text[row];
3697 	drp = PVTS(r, page)->drawn_rend[row];
3698 
3699 
3700 #ifndef NO_PIXEL_DROPPING_AVOIDANCE
3701 	/*
3702 	 * E1: Pixel dropping avoidance.  Do this before the main refresh on the
3703 	 * line. Require a refresh where pixels may have been dropped into our
3704 	 * area by a neighbour character which has now changed
3705 	 *
3706 	 * TODO: This could be integrated into E2 but might be too messy
3707 	 */
3708 	for (col = 0; col < r->TermWin.ncol; col++)
3709 	{
3710 	    unsigned char   is_font_char, is_same_char;
3711 	    text_t	    t;
3712 
3713 	    t = dtp[col];
3714 	    is_same_char = (t == stp[col] && drp[col] == srp[col]);
3715 	    if (!clear_next &&
3716 		(is_same_char || t == 0 || t == ' '))
3717 		/* screen cleared elsewhere */
3718 		continue;
3719 
3720 	    if (clear_next)
3721 	    {
3722 		/* previous char caused change here */
3723 		clear_next = 0;
3724 		dtp[col] = 0;
3725 
3726 		/* don't cascade into next char */
3727 		if (is_same_char)
3728 		    continue;
3729 	    }
3730 
3731 #ifdef XFT_SUPPORT
3732 	    if( ISSET_OPTION(r, Opt_xft) )
3733 	    {
3734 		/*
3735 		 * Only text drawn by over striking needs to be watched for
3736 		 * "dropped pixels".
3737 		 *
3738 		 * XXX This does not take into account Color_BD
3739 		 */
3740 		if( !loadedBoldFt && ( drp[col] & RS_Bold ) )
3741 		{
3742 		    if( col == r->TermWin.ncol - 1 ) clearlast = 1;
3743 		    else clear_next = 1;
3744 		}
3745 # ifdef TEXT_SHADOW
3746 		/*
3747 		 * Xft with shadow drops pixels into the next / prev char.
3748 		 */
3749 		else if (
3750 			  h->rs[Rs_textShadow]
3751 			  && r->TermWin.shadow_mode != SHADOW_NONE
3752 			)
3753 		{
3754 		    switch( r->TermWin.shadow_mode )
3755 		    {
3756 			case SHADOW_TOPRIGHT:
3757 			case SHADOW_RIGHT:
3758 			case SHADOW_BOTRIGHT:
3759 			    /* Clear next char */
3760 			    if( col == r->TermWin.ncol - 1) clearlast = 1;
3761 			    else clear_next = 1;
3762 			    break;
3763 
3764 			case SHADOW_TOPLEFT:
3765 			case SHADOW_LEFT:
3766 			case SHADOW_BOTLEFT:
3767 			    /* Clear prev char */
3768 			    if( col == 0 ) clearfirst = 1;
3769 			    else dtp[col-1] = 0;
3770 			    break;
3771 
3772 			default:
3773 			    /* Nothing to be done here */
3774 			    break;
3775 		    }
3776 		}
3777 # endif	/* TEXT_SHADOW */
3778 	    }
3779 	    else
3780 #endif
3781 	    {
3782 		j = MONO_BOLD(drp[col]) ? 1 : 0;
3783 # ifndef NO_BOLDFONT
3784 		wf = (j && r->TermWin.bfont) ?
3785 			    r->TermWin.bfont : r->TermWin.font;
3786 # endif
3787 
3788 		/*
3789 		** TODO: consider if anything special needs to happen
3790 		** with:
3791 		** #if defined(MULTICHAR_SET) &&
3792 		** !defined(NO_BOLDOVERSTRIKE_MULTI)
3793 		*/
3794 		is_font_char = (wf->per_char && IS_FONT_CHAR(wf, t)) ? 1:0;
3795 		if (!is_font_char || FONT_LBEAR(wf, t) < 0)
3796 		{
3797 		    if (col == 0)
3798 			clearfirst = 1;
3799 		    else
3800 			dtp[col - 1] = 0;
3801 		}
3802 		if (!is_font_char ||
3803 			(FONT_WIDTH(wf, t) < (FONT_RBEAR(wf, t) + j)))
3804 		{
3805 		    if (col == r->TermWin.ncol - 1)
3806 			clearlast = 1;
3807 		    else
3808 			clear_next = 1;
3809 		}
3810 	    }
3811 	}
3812 #endif	/* NO_PIXEL_DROPPING_AVOIDANCE */
3813 	/* End of E1 */
3814 
3815 
3816 	/*
3817 	** E2: OK, now the real pass
3818 	*/
3819 	ypixelc = (int)Row2Pixel(row);
3820 
3821 	for (col = 0; col < r->TermWin.ncol; col++)
3822 	{
3823 			    /* current font size != base font size */
3824 	    unsigned char   fontdiff,
3825 			    /* proportional font used */
3826 			    fprop;
3827 			    /* rendition value */
3828 	    rend_t	    rend;
3829 
3830 	    /* screen rendition (target rendtion) */
3831 	    rend = srp[col];
3832 
3833 	    /*
3834 	     * compare new text with old - if exactly the same then continue
3835 	     */
3836 	    if (
3837 		 /* Must match characters to skip. */
3838 		 stp[col] == dtp[col] &&
3839 		 /* Either rendition the same or   */
3840 		 (
3841 		   rend == drp[col] ||
3842 		   /* space w/ no background change  */
3843 		   (
3844 		     stp[col] == ' ' &&
3845 		     	 GET_BGATTR(rend) == GET_BGATTR(drp[col])
3846 		   )
3847 		 )
3848 	       )    /* if */
3849 	    {
3850 		if (!IS_MULTI1(rend))
3851 		    continue;
3852 #ifdef MULTICHAR_SET
3853 		else
3854 		{
3855 		    /* first byte is Kanji so compare second bytes */
3856 		    if (stp[col + 1] == dtp[col + 1])
3857 		    {
3858 			/* assume no corrupt characters on the screen */
3859 			col++;
3860 			continue;
3861 		    }
3862 		}
3863 #endif
3864 	    }
3865 	    /* redraw one or more characters */
3866 
3867 	    fontdiff = 0;
3868 	    len = 0;
3869 	    buffer[len++] = dtp[col] = stp[col];
3870 	    drp[col] = rend;
3871 	    xpixel = Col2Pixel(col);
3872 
3873 	    /*
3874 	     * Find out the longest string we can write out at once
3875 	     */
3876 #ifndef NO_BOLDFONT
3877 	    if (MONO_BOLD(rend) && r->TermWin.bfont != NULL)
3878 		fprop = (r->TermWin.propfont & PROPFONT_BOLD);
3879 	    else
3880 #endif
3881 		fprop = (r->TermWin.propfont & PROPFONT_NORMAL);
3882 
3883 #ifdef MULTICHAR_SET
3884 	    if (
3885 		  IS_MULTI1(rend) && col < r->TermWin.ncol - 1
3886 		  && IS_MULTI2(srp[col + 1])
3887 	       )
3888 	    {
3889 		if (!wbyte && r->TermWin.mfont)
3890 		{
3891 		    wbyte = 1;
3892 		    XSetFont(r->Xdisplay, r->TermWin.gc,
3893 			 r->TermWin.mfont->fid);
3894 		    fontdiff = (r->TermWin.propfont & PROPFONT_MULTI);
3895 #ifdef XFT_SUPPORT
3896 		    if ( ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt )
3897 		    {
3898 			drawfunc = XFT_DRAW_STRING_16;
3899 			image_drawfunc = XFT_DRAW_IMAGE_STRING_16;
3900 		    }
3901 		    else
3902 #endif
3903 		    {
3904 			drawfunc = X11_DRAW_STRING_16;
3905 			image_drawfunc = X11_DRAW_IMAGE_STRING_16;
3906 		    }
3907 		}
3908 
3909 		if (r->TermWin.mfont == NULL)
3910 		{
3911 		    buffer[0] = buffer[1] = ' ';
3912 		    len = 2;
3913 		    col++;
3914 		}
3915 		else
3916 		{
3917 		    /* double stepping - we're in multibyte font mode */
3918 		    for (; ++col < r->TermWin.ncol;)
3919 		    {
3920 			/* XXX: could check sanity on 2nd byte */
3921 			dtp[col] = stp[col];
3922 			drp[col] = srp[col];
3923 			buffer[len++] = stp[col];
3924 			col++;
3925 			/* proportional multibyte font mode */
3926 			if (fprop)
3927 			    break;
3928 			if ((col == r->TermWin.ncol) ||
3929 			    (srp[col] != rend))
3930 			    break;
3931 			if ((stp[col] == dtp[col]) &&
3932 			    (srp[col] == drp[col]) &&
3933 			    (stp[col + 1] == dtp[col + 1]))
3934 			    break;
3935 			if (len == h->currmaxcol)
3936 			    break;
3937 			dtp[col] = stp[col];
3938 			drp[col] = srp[col];
3939 			buffer[len++] = stp[col];
3940 		    }
3941 		    col--;
3942 		}
3943 
3944 		if (buffer[0] & 0x80)
3945 		    (h->multichar_decode)( (unsigned char*) buffer, len);
3946 		wlen = len / 2;
3947 	    }
3948 	    else
3949 	    {
3950 		if (rend & RS_multi1)
3951 		{
3952 		    /* corrupt character - you're outta there */
3953 		    rend &= ~RS_multiMask;
3954 		    drp[col] = rend; /* TODO check: may also want */
3955 		    dtp[col] = ' '; /* to poke into stp/srp   */
3956 		    buffer[0] = ' ';
3957 		}
3958 		if (wbyte)
3959 		{
3960 		    wbyte = 0;
3961 #ifdef XFT_SUPPORT
3962 		    if (!(ISSET_OPTION(r, Opt_xft) && r->TermWin.xftfont))
3963 #endif
3964 		    XSetFont(r->Xdisplay, r->TermWin.gc,
3965 			r->TermWin.font->fid);
3966 #ifdef XFT_SUPPORT
3967 		    if ( ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt )
3968 		    {
3969 			drawfunc = XFT_DRAW_STRING_8;
3970 			image_drawfunc = XFT_DRAW_IMAGE_STRING_8;
3971 		    }
3972 		    else
3973 #endif
3974 		    {
3975 			drawfunc = X11_DRAW_STRING_8;
3976 			image_drawfunc = X11_DRAW_IMAGE_STRING_8;
3977 		    }
3978 		}   /* if (wbyte) */
3979 #else
3980 	    { /* add } for correct % bouncing */
3981 #endif
3982 		if (!fprop)
3983 		{
3984 		    int echars;
3985 
3986 		    /* single stepping - `normal' mode */
3987 		    for (i = 0, echars=0; ++col < r->TermWin.ncol - 1;)
3988 		    {
3989 			/*
3990 			 * Instead of simply getting the longest string that
3991 			 * needs to be refreshed, we do some caching.
3992 			 *
3993 			 * i is the number of trailing chars that we read (in an
3994 			 * attempt to cache) that DO NOT need to be refreshed.
3995 			 * These had better be dumped.
3996 			 *
3997 			 * echars are the number of extra chars we drew that did
3998 			 * not need to be drawn. When echars get's too high,
3999 			 * then we should break out.
4000 			 */
4001 			if (rend != srp[col])
4002 			    /* Different attributes. */
4003 			    break;
4004 			buffer[len++] = stp[col];
4005 
4006 			if ( (stp[col] != dtp[col]) || (srp[col] != drp[col]) )
4007 			{
4008 			    /* This position needed to be refreshed anyway */
4009 			    /* if (must_clear && (i++ > (len / 2))) break; */
4010 
4011 			    dtp[col] = stp[col];
4012 			    drp[col] = srp[col];
4013 			    i = 0; /* Set trailing chars to 0 */
4014 			}
4015 			else /* if (must_clear ||
4016 				(stp[col] != ' ' && ++i > 32)) */
4017 			{
4018 			    /*
4019 			     * This position did not require a refresh. Let's do
4020 			     * some caching.
4021 			     */
4022 			    i++;
4023 			    /*
4024 			     * 25% (arbitarily choosen) of our drawn string can
4025 			     * be extra chars.
4026 			     */
4027 			    if( ++echars > (len >> 2) ) break;
4028 			}
4029 
4030 		    }	/* for */
4031 		    col--;	/* went one too far.  move back */
4032 		    len -= i;	/* dump any matching trailing chars */
4033 
4034 		    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "Drawing %d(%d) chars: %.*s\n", len, echars-i, (len > 55) ? 55 : len, buffer));
4035 		} /* if (!fprop) */
4036 		wlen = len;
4037 	    }
4038 	    buffer[len] = '\0';
4039 
4040 	    /*
4041 	     * Determine the attributes for the string
4042 	     */
4043 	    fore = GET_FGCOLOR(rend);
4044 	    back = GET_BGCOLOR(rend);
4045 	    rend = GET_ATTR(rend);
4046 
4047 	    switch (rend & RS_fontMask)
4048 	    {
4049 		case RS_acsFont:
4050 		    for (i = 0; i < len; i++)
4051 			/*
4052 			 * Xterm leaves 0x5f (_) unchanged in graphics mode.
4053 			 */
4054 			if (buffer[i] > 0x5f && buffer[i] < 0x7f)
4055 			    buffer[i] -= 0x5f;
4056 		    break;
4057 		case RS_ukFont:
4058 		    for (i = 0; i < len; i++)
4059 			if (buffer[i] == '#')
4060 			    buffer[i] = 0x1e;	/* pound sign */
4061 		    break;
4062 	    }
4063 
4064 #ifndef NO_BOLD_UNDERLINE_REVERSE
4065 	    /*
4066 	     * Bold / underline fonts. We use Color_BD / Color_UL only if we're
4067 	     * displaying the font with normal colors.
4068 	     */
4069 	    if( fore == Color_fg && back == Color_bg )
4070 	    {
4071 		/*
4072 		 * TODO: Should probably add a Color_BDUL here.
4073 		 */
4074 		if ( rend & RS_Bold )
4075 		{
4076 		    if (
4077 			  XDEPTH > 2 && ISSET_PIXCOLOR(h, Color_BD)
4078 			  && r->pixColors[fore] != r->pixColors[Color_BD]
4079 			  && r->pixColors[back] != r->pixColors[Color_BD]
4080 		       )
4081 		    {
4082 
4083 			fore = Color_BD;
4084 			/*
4085 			 * 2006-03-27 gi1242: Ignore veryBold when Color_BD is
4086 			 * set. veryBold will only be used when displaying
4087 			 * colored / blinking text.
4088 			 */
4089 #if 0
4090 			if (NOTSET_OPTION(r, Opt2_veryBold))
4091 #endif
4092 			rend &= ~RS_Bold;   /* we've taken care of it */
4093 		    }
4094 		}
4095 		else if ( rend & RS_Uline )
4096 		{
4097 		    if (
4098 			  XDEPTH > 2 && ISSET_PIXCOLOR(h, Color_UL)
4099 			  && r->pixColors[fore] != r->pixColors[Color_UL]
4100 			  && r->pixColors[back] != r->pixColors[Color_UL]
4101 		       )
4102 		    {
4103 			fore = Color_UL;
4104 			rend &= ~RS_Uline;  /* we've taken care of it */
4105 		    }
4106 		}
4107 	    }
4108 #endif
4109 	    rvid = (rend & RS_RVid) ? 1 : 0;
4110 #ifdef OPTION_HC
4111 	    /*
4112 	     * Use Color_HC for anything blinking. TODO: Add a seperate
4113 	     * attribute for the XSelection, so that the user can distinguish
4114 	     * blinking text from the selection. (XTerm does this)
4115 	     */
4116 	    if ( (rend & RS_Blink))
4117 	    {
4118 		if (
4119 		      XDEPTH > 2 && ISSET_PIXCOLOR(h, Color_HC)
4120 		      && r->pixColors[fore] != r->pixColors[Color_HC]
4121 		      && r->pixColors[back] != r->pixColors[Color_HC]
4122 # ifndef NO_CURSORCOLOR
4123 			/* Don't do this for the cursor */
4124 		      && (
4125 			    !ISSET_PIXCOLOR(h, Color_cursor)
4126 			    || !r->TermWin.focus || !showcursor
4127 			    || CURROW != row || CURCOL != col
4128 			 )
4129 # endif
4130 		   )
4131 		{
4132 		    if( rvid) rvid = 0;
4133 		    back = Color_HC;
4134 		}
4135 		else
4136 		    rvid = 1;	/* fall back */
4137 	    }
4138 #endif
4139 
4140 	    /*
4141 	     * Reverse Video. If defined, Color_RV for background and leave
4142 	     * foreground untouched. Done last so that RV-BD text will have
4143 	     * Color_BD background if set (like in XTerm).
4144 	     */
4145 	    if( rvid )
4146 	    {
4147 #ifndef NO_BOLD_UNDERLINE_REVERSE
4148 		if (
4149 		      XDEPTH > 2 && ISSET_PIXCOLOR(h, Color_RV)
4150 		      && r->pixColors[fore] != r->pixColors[Color_RV]
4151 		      && r->pixColors[back] != r->pixColors[Color_RV]
4152 # ifndef NO_CURSORCOLOR
4153 		      /* Don't do this for the cursor */
4154 		      && (
4155 			    !ISSET_PIXCOLOR(h, Color_cursor)
4156 			    || !r->TermWin.focus || !showcursor
4157 			    || CURROW != row || CURCOL != col
4158 			 )
4159 # endif
4160 		   )
4161 		    back = Color_RV;
4162 		else
4163 #endif
4164 		{
4165 #ifdef TTY_256COLOR
4166 		    SWAP_IT(fore, back, uint16_t);
4167 #else
4168 		    SWAP_IT(fore, back, unsigned char);
4169 #endif
4170 		}
4171 	    }
4172 
4173 #ifndef NO_BRIGHTCOLOR
4174 	    /* Use bright colors for bold primary colors */
4175 	    if( (rend & RS_Bold) && NOTSET_OPTION( r, Opt2_boldColors ) )
4176 	    {
4177 		if( fore >= minCOLOR && fore < minBrightCOLOR )
4178 		{
4179 		    fore += minBrightCOLOR - minCOLOR;
4180 		    if( NOTSET_OPTION( r, Opt_veryBright ) )
4181 			rend &= ~RS_Bold;
4182 		}
4183 #if defined(TTY_256COLOR) && defined(BOLD_BRIGHTENS_256_COLORS)
4184 		/* If fore is in the 6x6x6 color cube, try and brighten it */
4185 		else if(
4186 			 fore >= min256COLOR			    &&
4187 			 fore <= min256COLOR + 4*36 + 4*6 + 4	    &&
4188 			 (fore - min256COLOR) % 6 < 5		    &&
4189 			 ((fore - min256COLOR)/6) % 6 < 5
4190 		       )
4191 		{
4192 		    fore += 36 + 6 + 1;
4193 
4194 		    if( NOTSET_OPTION( r, Opt_veryBright ) )
4195 			rend &= ~RS_Bold;
4196 		}
4197 
4198 		/* Brighten up colors in the grey-scale ramp. */
4199 		else if(
4200 			fore >= min256COLOR + 6*6*6		    &&
4201 			fore <= max256COLOR - 3
4202 		       )
4203 		{
4204 		    if( fore == max256COLOR -3 )
4205 			fore = min256COLOR + 6*6*6 - 1;
4206 		    else
4207 			fore += 4;
4208 
4209 		    if( NOTSET_OPTION( r, Opt_veryBright ) )
4210 			rend &= ~RS_Bold;
4211 		}
4212 #endif /*TTY_256COLOR && BOLD_BRIGHTENS_256_COLORS*/
4213 	    }
4214 #endif /*NO_BRIGHTCOLOR*/
4215 
4216 
4217 	    /*
4218 	     * fore and back should now have the correct colors.
4219 	     */
4220 	    gcmask = 0;
4221 	    if (back != Color_bg)
4222 	    {
4223 		gcvalue.background = r->pixColors[back];
4224 		gcmask = GCBackground;
4225 	    }
4226 	    if (fore != Color_fg)
4227 	    {
4228 		gcvalue.foreground = r->pixColors[fore];
4229 		gcmask |= GCForeground;
4230 	    }
4231 #ifndef NO_BOLD_UNDERLINE_REVERSE
4232 	    else if (rend & RS_Bold && ISSET_PIXCOLOR( h, Color_BD) )
4233 	    {
4234 # ifdef XFT_SUPPORT
4235 		/*
4236 		 * XFT won't use the colors from the GC, so we need to set
4237 		 * fore.
4238 		 */
4239 		if( ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt )
4240 		    fore = Color_BD;
4241 		else
4242 # endif
4243 		{
4244 		    gcvalue.foreground = r->pixColors[Color_BD];
4245 		    gcmask |= GCForeground;
4246 		}
4247 
4248 		/*
4249 		 * If veryBold is not set, then don't render colored text in
4250 		 * bold.
4251 		 */
4252 		if (NOTSET_OPTION(r, Opt2_veryBold))
4253 		    rend &= ~RS_Bold;
4254 	    }
4255 	    else if (rend & RS_Uline && ISSET_PIXCOLOR( h, Color_UL) )
4256 	    {
4257 # ifdef XFT_SUPPORT
4258 		if( ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt )
4259 		    fore = Color_UL;
4260 		else
4261 # endif
4262 		{
4263 		    gcvalue.foreground = r->pixColors[Color_UL];
4264 		    gcmask |= GCForeground;
4265 		}
4266 		rend &= ~RS_Uline;  /* we've taken care of it */
4267 	    }
4268 #endif
4269 
4270 
4271 	    if (gcmask)
4272 		XChangeGC(r->Xdisplay, r->TermWin.gc, gcmask, &gcvalue);
4273 #ifndef NO_BOLDFONT
4274 	    /*
4275 	     * Switch to the bold font if we are rendering bold text.
4276 	     *
4277 	     * NOTE: We only deal with bold fonts for non-multichar text.
4278 	     * Multichar bold text will have to be done by over striking (or
4279 	     * some other shmuck must code it) -- gi1242 2006-08-19.
4280 	     */
4281 	    if ( MONO_BOLD_FG(rend, fore) && !wbyte )
4282 	    {
4283 		if( usingBoldFt )
4284 		    rend &= ~RS_Bold;	/* We've taken care of it */
4285 
4286 		else if( loadedBoldFt )
4287 		{
4288 		    usingBoldFt = 1;
4289 
4290 # ifdef XFT_SUPPORT
4291 		    if( ISSET_OPTION(r, Opt_xft) )
4292 		    {
4293 			SWAP_IT( r->TermWin.xftfont, r->TermWin.xftbfont,
4294 				XftFont*);
4295 		    }
4296 		    else
4297 # endif
4298 		    {
4299 			XSetFont(r->Xdisplay, r->TermWin.gc,
4300 				r->TermWin.bfont->fid);
4301 		    }
4302 
4303 		    fontdiff = (r->TermWin.propfont & PROPFONT_BOLD);
4304 		    rend &= ~RS_Bold;   /* we've taken care of it */
4305 		}
4306 	    }
4307 
4308 	    /*
4309 	     * If we are using the bold font, but don't want to render bold
4310 	     * text, then we should restore the original font.
4311 	     */
4312 	    else if( usingBoldFt && !MONO_BOLD_FG( rend, fore ) )
4313 	    {
4314 		usingBoldFt = 0;
4315 
4316 		/*
4317 		 * If we're not showing a multi byte char, then we reset
4318 		 * fontdiff to 0. If we're showing a multi byte char, then font
4319 		 * diff will have been set elsewhere, and we should not reset
4320 		 * it.
4321 		 */
4322 		if( !wbyte )
4323 		    fontdiff    = 0;
4324 
4325 # ifdef XFT_SUPPORT
4326 		if( ISSET_OPTION(r, Opt_xft) )
4327 		{
4328 		    SWAP_IT( r->TermWin.xftfont, r->TermWin.xftbfont, XftFont*);
4329 		}
4330 		else
4331 # endif
4332 		{
4333 		    XSetFont(r->Xdisplay, r->TermWin.gc, r->TermWin.font->fid);
4334 		}
4335 	    }
4336 #endif
4337 
4338 	    /*
4339 	     * Actually do the drawing of the string here
4340 	     */
4341 	    if (back == Color_bg && must_clear)
4342 	    {
4343 		CLEAR_CHARS( r, page, already_cleared,
4344 			xpixel, ypixelc, len);
4345 		for (i = 0; i < len; i++)
4346 		    /* don't draw empty strings */
4347 		    if (buffer[i] != ' ')
4348 		    {
4349 			rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "CL Drawing '%.60s' (%d)\n", buffer, len));
4350 
4351 			rxvt_scr_draw_string (r, page, xpixel, ypixelc,
4352 				buffer, wlen, drawfunc,
4353 				fore, back, rend,
4354 				((refresh_type & CLIPPED_REFRESH) ?
4355 					r->h->refreshRegion : None ));
4356 			break;
4357 		    }
4358 	    }
4359 	    else if (fprop || fontdiff)
4360 	    {
4361 		/* single glyph writing */
4362 		unsigned long   pixel;
4363 
4364 		pixel = gcvalue.foreground;
4365 		gcvalue.foreground = gcvalue.background;
4366 		XChangeGC(r->Xdisplay, r->TermWin.gc, GCForeground, &gcvalue);
4367 		rxvt_fill_rectangle (r, page, xpixel, ypixelc,
4368 		    (unsigned int) Width2Pixel(len),
4369 		    (unsigned int) (Height2Pixel(1)
4370 		    /* - r->TermWin.lineSpace */));
4371 		gcvalue.foreground = pixel;
4372 		XChangeGC(r->Xdisplay, r->TermWin.gc, GCForeground, &gcvalue);
4373 
4374 		rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "PF Drawing '%.60s' (%d)\n", buffer, len));
4375 		rxvt_scr_draw_string (r, page,
4376 			xpixel, ypixelc, buffer, wlen, drawfunc,
4377 			fore, back, rend,
4378 			((refresh_type & CLIPPED_REFRESH) ?
4379 				r->h->refreshRegion : None ));
4380 	    }
4381 	    else
4382 	    {
4383 		rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "NC Drawing '%.60s' (%d)\n", buffer, len));
4384 		rxvt_scr_draw_string (r, page,
4385 			xpixel, ypixelc, buffer, wlen, image_drawfunc,
4386 			fore, back, rend,
4387 			((refresh_type & CLIPPED_REFRESH) ?
4388 				r->h->refreshRegion : None ));
4389 	    }
4390 
4391 #ifndef NO_BOLDOVERSTRIKE
4392 # ifdef NO_BOLDOVERSTRIKE_MULTI
4393 	    if (!wbyte)
4394 # endif
4395 	    if (MONO_BOLD_FG(rend, fore))
4396 	    {
4397 		/*
4398 		 * If we still need to draw a bold chars, then all else has
4399 		 * failed. Fall back to overstriking.
4400 		 */
4401 		rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "Overstriking %s\n", buffer ));
4402 		rxvt_scr_draw_string (r, page,
4403 			xpixel + 1, ypixelc, buffer, wlen, drawfunc,
4404 			fore, back, rend,
4405 			((refresh_type & CLIPPED_REFRESH) ?
4406 				r->h->refreshRegion : None ));
4407 	    }
4408 #endif
4409 	    if (rend & RS_Uline)
4410 	    {
4411 #ifdef XFT_SUPPORT
4412 		if (ISSET_OPTION(r, Opt_xft) && PVTS(r, page)->xftvt)
4413 		{
4414 		    if (r->TermWin.xftfont->descent > 1)
4415 			XDrawLine(r->Xdisplay, drawBuffer,
4416 			    r->TermWin.gc,
4417 			    xpixel,
4418 			    ypixelc + r->TermWin.xftfont->ascent + 1,
4419 			    xpixel + Width2Pixel(len) - 1,
4420 			    ypixelc + r->TermWin.xftfont->ascent + 1);
4421 		}
4422 		else
4423 #endif
4424 		if (r->TermWin.font->descent > 1)
4425 		    XDrawLine(r->Xdisplay, drawBuffer, r->TermWin.gc,
4426 			xpixel,
4427 			ypixelc + r->TermWin.font->ascent + 1,
4428 			xpixel + Width2Pixel(len) - 1,
4429 			ypixelc + r->TermWin.font->ascent + 1);
4430 	    }
4431 
4432 	    if (gcmask)	    /* restore normal colours */
4433 	    {
4434 		gcvalue.foreground = r->pixColors[Color_fg];
4435 		gcvalue.background = r->pixColors[Color_bg];
4436 		XChangeGC(r->Xdisplay, r->TermWin.gc, gcmask, &gcvalue);
4437 	    }
4438 	} /* for (col....) */
4439 
4440 	/* End of E2 */
4441     } /* for (row....) */
4442 
4443     /*
4444      * If we've completed our refresh, and are using the bold font, we need to
4445      * reset it. Only needed when using XFT.
4446      */
4447     if( usingBoldFt )
4448     {
4449 	usingBoldFt = 0;
4450 
4451 #ifdef XFT_SUPPORT
4452 	if( ISSET_OPTION(r, Opt_xft) )
4453 	{
4454 	    SWAP_IT( r->TermWin.xftfont, r->TermWin.xftbfont, XftFont*);
4455 	}
4456 	else
4457 # endif
4458 	{
4459 	    XSetFont(r->Xdisplay, r->TermWin.gc, r->TermWin.font->fid);
4460 	}
4461     }
4462     /* End of E */
4463 
4464 
4465     /*
4466     ** G: cleanup cursor and display outline cursor in necessary
4467     */
4468     if (showcursor)
4469     {
4470 	if (r->TermWin.focus)
4471 	{
4472 	    int	    currow = CURROW + SVLINES;
4473 	    srp = &(PSCR(r, page).rend[currow][CURCOL]);
4474 	    *srp ^= RS_RVid;
4475 
4476 #ifndef NO_CURSORCOLOR
4477 	    *srp = (*srp & ~(RS_fgMask | RS_bgMask)) | cc1;
4478 #endif
4479 #ifdef MULTICHAR_SET
4480 	    if (morecur)
4481 	    {
4482 		assert (0 == morecur || -1 == morecur || 1 == morecur);
4483 		srp += morecur;
4484 		*srp ^= RS_RVid;
4485 # ifndef NO_CURSORCOLOR
4486 		*srp = (*srp & ~(RS_fgMask | RS_bgMask)) | cc2;
4487 # endif
4488 	    }
4489 #endif
4490 	}
4491 	else if (h->oldcursor.row >= 0)
4492 	{
4493 #ifndef NO_CURSORCOLOR
4494 	    unsigned long   gcmask; /* Graphics Context mask */
4495 
4496 	    gcmask = 0;
4497 	    if (XDEPTH > 2 && ISSET_PIXCOLOR(h, Color_cursor))
4498 	    {
4499 		gcvalue.foreground = r->pixColors[Color_cursor];
4500 		gcmask = GCForeground;
4501 		XChangeGC(r->Xdisplay, r->TermWin.gc, gcmask, &gcvalue);
4502 		    gcvalue.foreground = r->pixColors[Color_fg];
4503 	    }
4504 #endif
4505 
4506 	    XDrawRectangle(r->Xdisplay, drawBuffer, r->TermWin.gc,
4507 		Col2Pixel(h->oldcursor.col + morecur),
4508 		Row2Pixel(h->oldcursor.row),
4509 		(unsigned int)(Width2Pixel(1 + (morecur?1:0)) - 1),
4510 		(unsigned int)(Height2Pixel(1)
4511 		/* - r->TermWin.lineSpace*/ - 1));
4512 
4513 #ifndef NO_CURSORCOLOR
4514 	    if (gcmask)	    /* restore normal colours */
4515 		XChangeGC(r->Xdisplay, r->TermWin.gc, gcmask, &gcvalue);
4516 #endif
4517 	}
4518     }
4519     /* End of G */
4520 
4521 
4522     /*
4523     ** H: cleanup selection
4524     */
4525     rxvt_scr_reverse_selection(r, page);
4526 
4527 
4528     /*
4529     ** I: other general cleanup
4530     */
4531     /*
4532     ** clear the whole screen height, note that width == 0 is treated
4533     ** specially by XClearArea
4534     */
4535     if (clearfirst && r->TermWin.int_bwidth)
4536 	rxvt_clear_area (r, page, 0, 0,
4537 	    (unsigned int)r->TermWin.int_bwidth, VT_HEIGHT(r));
4538     /*
4539     ** clear the whole screen height, note that width == 0 is treated
4540     ** specially by XClearArea
4541     */
4542     if (clearlast && r->TermWin.int_bwidth)
4543 	rxvt_clear_area (r, page,
4544 	    TWIN_WIDTH(r) + r->TermWin.int_bwidth, 0,
4545 	    (unsigned int)r->TermWin.int_bwidth, VT_HEIGHT(r));
4546 
4547     if (refresh_type & SMOOTH_REFRESH)
4548 	XSync(r->Xdisplay, False);
4549 
4550     if( (refresh_type & CLIPPED_REFRESH) && IS_REGION(h->refreshRegion))
4551     {
4552 	/*
4553 	 * A clipped refresh is complete. Don't restrict future refreshes.
4554 	 */
4555 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "Completed clipped refresh\n"));
4556 
4557 	/*
4558 	 * XSetRegion( r->Xdisplay, r->TermWin.gc, None) causes a segfault.
4559 	 * Probably because GC's dont' accept the None region as gracefully.
4560 	 */
4561 	XSetClipMask( r->Xdisplay, r->TermWin.gc, None);
4562 #ifdef XFT_SUPPORT
4563 	if( ISSET_OPTION(r, Opt_xft) && PVTS( r, page)->xftvt)
4564 	    XftDrawSetClip( PVTS( r, page)->xftvt, None);
4565 #endif
4566     }
4567     else
4568 	/* If we performed an unclipped refresh, then the screen is current */
4569 	PVTS(r, page)->want_refresh = 0;
4570 
4571     h->refresh_type &= ~CLIPPED_REFRESH;
4572     h->want_clip_refresh = 0; /* clipping is current (regardless of wether we
4573 				 performed a clipped refresh or not. */
4574 
4575     /* Clipping regions will now carry stale information. */
4576     if (IS_REGION(h->refreshRegion))
4577     {
4578 	XDestroyRegion( h->refreshRegion );
4579 	UNSET_REGION(h->refreshRegion);
4580     }
4581 
4582     PVTS(r, page)->num_scr = 0;
4583     h->num_scr_allow = 1;
4584 }
4585 
4586 
4587 #undef X11_DRAW_STRING_8
4588 #undef X11_DRAW_STRING_16
4589 #undef X11_DRAW_IMAGE_STRING_8
4590 #undef X11_DRAW_IMAGE_STRING_16
4591 #undef XFT_DRAW_STRING_8
4592 #undef XFT_DRAW_STRING_16
4593 #undef XFT_DRAW_STRING_32
4594 #undef XFT_DRAW_STRING_UTF8
4595 
4596 /* ------------------------------------------------------------------------- */
4597 
4598 
4599 /* EXTPROTO */
4600 void
4601 rxvt_scr_clear(rxvt_t* r, int page)
4602 {
4603     if (!PVTS(r, page)->mapped)
4604 	return;
4605 
4606     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_scr_clear()\n"));
4607 
4608     r->h->num_scr_allow = 0;
4609     PVTS(r, page)->want_refresh = 1;
4610 #ifdef TRANSPARENT
4611     if( ISSET_OPTION(r, Opt_transparent) )
4612     {
4613 	if (IS_WIN(r->TermWin.parent))
4614 	    XClearWindow(r->Xdisplay, r->TermWin.parent);
4615     }
4616 #endif
4617     XClearWindow(r->Xdisplay, PVTS(r, page)->vt);
4618 }
4619 
4620 /* ------------------------------------------------------------------------- */
4621 /* INTPROTO */
4622 void
4623 rxvt_scr_reverse_selection(rxvt_t* r, int page)
4624 {
4625     int		 i, col, row, end_row;
4626     rend_t	 *srp;
4627 
4628     if (
4629 	  SEL(r).op && SEL(r).vt == page &&
4630 	  PVTS(r, page)->current_screen == SEL(r).screen
4631        )
4632     {
4633 	end_row = SVLINES - VSTART;
4634 
4635 	i = SEL(r).beg.row + SVLINES;
4636 	row = SEL(r).end.row + SVLINES;
4637 
4638 	if (i >= end_row)
4639 	    col = SEL(r).beg.col;
4640 	else
4641 	{
4642 	    col = 0;
4643 	    i = end_row;
4644 	}
4645 
4646 	end_row += r->TermWin.nrow;
4647 	for (; i < row && i < end_row; i++, col = 0)
4648 	    for (srp = PSCR(r, page).rend[i]; col < r->TermWin.ncol; col++)
4649 #ifndef OPTION_HC
4650 		srp[col] ^= RS_RVid;
4651 #else
4652 		srp[col] ^= RS_Blink;
4653 #endif
4654 	if (i == row && i < end_row)
4655 	    for (srp = PSCR(r, page).rend[i]; col < SEL(r).end.col; col++)
4656 #ifndef OPTION_HC
4657 		srp[col] ^= RS_RVid;
4658 #else
4659 		srp[col] ^= RS_Blink;
4660 #endif
4661     }
4662 }
4663 
4664 /* ------------------------------------------------------------------------- */
4665 /*
4666  * Dump the whole scrollback and screen to the passed filedescriptor.  The
4667  * invoking routine must close the fd.
4668  */
4669 #if 0
4670 /* EXTPROTO */
4671 void
4672 rxvt_scr_dump(rxvt_t* r, int fd)
4673 {
4674     int		 row, wrote;
4675     unsigned int    width, towrite;
4676     char	    r1[] = "\n";
4677 
4678     for (
4679 	  row = SVLINES - PVTS(r, page)->nscrolled;
4680 	  row < SVLINES + r->TermWin.nrow - 1;
4681 	  row++
4682 	)
4683     {
4684     width = PSCR(r, page).tlen[row] >= 0 ? PSCR(r, page).tlen[row]
4685 		     : r->TermWin.ncol;
4686     for (towrite = width; towrite; towrite -= wrote)
4687     {
4688 	wrote = write(fd, &(PSCR(r, page).text[row][width - towrite]),
4689 	      towrite);
4690 	if (wrote < 0)
4691 	return;	    /* XXX: death, no report */
4692     }
4693     if (PSCR(r, page).tlen[row] >= 0)
4694 	if (write(fd, r1, 1) <= 0)
4695 	return;	/* XXX: death, no report */
4696     }
4697 }
4698 #endif
4699 
4700 /* ------------------------------------------------------------------------- *
4701  *			   CHARACTER SELECTION				 *
4702  * ------------------------------------------------------------------------- */
4703 
4704 /*
4705  * -PVTS(r, page)->nscrolled <= (selection row) <= r->TermWin.nrow - 1
4706  */
4707 /* EXTPROTO */
4708 void
4709 rxvt_selection_check(rxvt_t* r, int page, int check_more)
4710 {
4711     row_col_t	   pos;
4712 
4713     if (!SEL(r).op ||
4714 	SEL(r).vt != page ||
4715 	SEL(r).screen != PVTS(r, page)->current_screen)
4716 	return;
4717 
4718     pos.row = pos.col = 0;
4719     if (
4720 	    (SEL(r).beg.row < -(int32_t)PVTS(r, page)->nscrolled) ||
4721 	    (SEL(r).beg.row >= r->TermWin.nrow) ||
4722 	    (SEL(r).mark.row < -(int32_t)PVTS(r, page)->nscrolled) ||
4723 	    (SEL(r).mark.row >= r->TermWin.nrow) ||
4724 	    (SEL(r).end.row < -(int32_t)PVTS(r, page)->nscrolled) ||
4725 	    (SEL(r).end.row >= r->TermWin.nrow) ||
4726 	    ( check_more == 1 &&
4727 	      PVTS(r, page)->current_screen == SEL(r).screen &&
4728 	      !RC_BEFORE(PSCR(r, page).cur, SEL(r).beg) &&
4729 	      RC_BEFORE(PSCR(r, page).cur, SEL(r).end)) ||
4730 	    ( check_more == 2 &&
4731 	      RC_BEFORE(SEL(r).beg, pos) &&
4732 	      RC_AFTER(SEL(r).end, pos)) ||
4733 	    ( check_more == 3 &&
4734 	      RC_AFTER(SEL(r).end, pos)) ||
4735 	    ( check_more == 4	/* screen width change */ &&
4736 	      ( SEL(r).beg.row != SEL(r).end.row ||
4737 		SEL(r).end.col > r->TermWin.ncol))
4738        )
4739     {
4740 	CLEAR_SELECTION(r);
4741     }
4742 }
4743 
4744 /* ------------------------------------------------------------------------- */
4745 /*
4746  * Paste a selection direct to the command fd
4747  */
4748 /* INTPROTO */
4749 void
4750 rxvt_paste_str(rxvt_t* r, int page,
4751 	const unsigned char *data, unsigned int nitems)
4752 {
4753     unsigned int    i, j, n;
4754     unsigned char  *ds = rxvt_malloc(PROP_SIZE);
4755 
4756     /*
4757      * Convert normal newline chars into common keyboard Return key sequence
4758      */
4759     for (i = 0; i < nitems; i += PROP_SIZE)
4760     {
4761 	n = min(nitems - i, PROP_SIZE);
4762 	MEMCPY(ds, data + i, n);
4763 	for (j = 0; j < n; j++)
4764 	    if (ds[j] == '\n')
4765 		ds[j] = '\r';
4766 	rxvt_tt_write(r, page, ds, (int)n);
4767     }
4768     rxvt_free(ds);
4769 }
4770 
4771 
4772 /* ------------------------------------------------------------------------- */
4773 /*
4774  * Respond to a notification that a primary selection has been sent
4775  * EXT: SelectionNotify
4776  */
4777 /* EXTPROTO */
4778 int
4779 rxvt_selection_paste(rxvt_t* r, Window win, Atom prop, Bool delete_prop)
4780 {
4781     long	    nread = 0;
4782     unsigned long   bytes_after;
4783     XTextProperty   ct;
4784 #ifdef MULTICHAR_SET
4785     int		    dummy_count;
4786     char**	    cl;
4787 #endif
4788 
4789     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_paste (%08lx, %lu, %d), wait=%2x\n", win, (unsigned long)prop, (int)delete_prop, r->h->selection_wait));
4790 
4791     if (NOT_ATOM(prop))	    /* check for failed XConvertSelection */
4792     {
4793 #ifdef MULTICHAR_SET
4794 	if ((r->h->selection_type & Sel_CompoundText))
4795 	{
4796 	    int	    selnum = r->h->selection_type & Sel_whereMask;
4797 
4798 	    r->h->selection_type = 0;
4799 	    if (selnum != Sel_direct)
4800 		rxvt_selection_request_other(r, ATAB(r), XA_STRING, selnum);
4801 	}
4802 #endif
4803 	return 0;
4804     }
4805 
4806     for (;;)
4807     {
4808 	if(
4809 	     XGetWindowProperty( r->Xdisplay, win, prop, (long) (nread/4),
4810 		    (long) (PROP_SIZE / 4), delete_prop, AnyPropertyType,
4811 		    &ct.encoding, &ct.format, &ct.nitems, &bytes_after,
4812 		    &ct.value)
4813 		!= Success
4814 	  )
4815 	    break;
4816 	if( ct.encoding == None )
4817 	{
4818 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_paste: property didn't exist!\n"));
4819 	    break;
4820 	}
4821 
4822 	if (ct.value == NULL)
4823 	{
4824 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_paste: property shooting blanks!\n"));
4825 	    continue;
4826 	}
4827 
4828 	if (ct.nitems == 0)
4829 	{
4830 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_paste: property empty - also INCR end\n"));
4831 
4832 	    if( r->h->selection_wait == Sel_normal && nread == 0 )
4833 	    {
4834 		/*
4835 		 * pass through again trying CUT_BUFFER0 if we've come from
4836 		 * XConvertSelection() but nothing was presented
4837 		 */
4838 		rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_request: pasting CUT_BUFFER0\n"));
4839 		rxvt_selection_paste(r, XROOT, XA_CUT_BUFFER0, False);
4840 	    }
4841 	    nread = -1;	    /* discount any previous stuff */
4842 	    break;
4843 	}
4844 
4845 	nread += ct.nitems;
4846 #ifdef MULTICHAR_SET
4847 	if (
4848 	      XmbTextPropertyToTextList(r->Xdisplay, &ct, &cl, &dummy_count)
4849 	      	    == Success
4850 	      && cl
4851 	   )
4852 	{
4853 	    rxvt_paste_str(r, ATAB(r), (const unsigned char*) cl[0],
4854 		    STRLEN(cl[0]));
4855 	    XFreeStringList(cl);
4856 	}
4857 	else
4858 #endif
4859 	    rxvt_paste_str(r, ATAB(r), ct.value, (unsigned int) ct.nitems);
4860 
4861 	if( bytes_after == 0 )
4862 	    break;
4863 
4864 	XFree(ct.value);
4865 	ct.value = 0;
4866     }
4867 
4868     if (ct.value)
4869 	XFree(ct.value);
4870 
4871     if (r->h->selection_wait == Sel_normal)
4872 	r->h->selection_wait = Sel_none;
4873 
4874     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_paste: bytes written: %ld\n", nread));
4875     return (int)nread;
4876 }
4877 
4878 
4879 /*
4880  * INCR support originally provided by Paul Sheer <psheer@obsidian.co.za>
4881  */
4882 /* EXTPROTO */
4883 void
4884 rxvt_selection_property(rxvt_t* r, Window win, Atom prop)
4885 {
4886     int		 reget_time = 0;
4887 
4888     if (NOT_ATOM(prop))
4889 	return;
4890 
4891     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_property(%08lx, %lu)\n", win, (unsigned long)prop));
4892     if (r->h->selection_wait == Sel_normal)
4893     {
4894 	int	     a, afmt;
4895 	Atom		atype;
4896 	unsigned long   bytes_after, nitems;
4897 	unsigned char  *s = NULL;
4898 
4899 	a = XGetWindowProperty(r->Xdisplay, win, prop, 0L, 1L, False,
4900 		   r->h->xa[XA_INCR], &atype, &afmt, &nitems,
4901 		   &bytes_after, &s);
4902 	if (s)
4903 	    XFree(s);
4904 	if (a != Success)
4905 	    return;
4906 #ifndef OS_CYGWIN
4907 	if (atype == r->h->xa[XA_INCR])	    /* start an INCR transfer */
4908 	{
4909 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_property: INCR: starting transfer\n"));
4910 	    XDeleteProperty(r->Xdisplay, win, prop);
4911 	    XFlush(r->Xdisplay);
4912 	    reget_time = 1;
4913 	    r->h->selection_wait = Sel_incr;
4914 	}
4915 #endif
4916     }
4917     else if (r->h->selection_wait == Sel_incr)
4918     {
4919 	reget_time = 1;
4920 	if (rxvt_selection_paste(r, win, prop, True) == -1)
4921 	{
4922 	    rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_property: INCR: clean end\n"));
4923 	    r->h->selection_wait = Sel_none;
4924 	    r->h->timeout[TIMEOUT_INCR].tv_sec = 0; /* turn off timer */
4925 	}
4926     }
4927     if (reget_time)	/* received more data so reget time */
4928     {
4929 	gettimeofday( &(r->h->timeout[TIMEOUT_INCR]), NULL);
4930 	/* ten seconds wait */
4931 	r->h->timeout[TIMEOUT_INCR].tv_sec += 10;
4932     }
4933 }
4934 
4935 
4936 /* ------------------------------------------------------------------------- */
4937 /*
4938  * Request the content of a selection buffer:
4939  *
4940  * EXT: button 2 release
4941  */
4942 /* EXTPROTO */
4943 void
4944 rxvt_selection_request_by_sel(rxvt_t* r, int page, Time tm, int x, int y,int sel)
4945 {
4946     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_request %d (%lu, %d, %d)\n", page, tm, x, y ));
4947 
4948     if (x < 0 || x >= VT_WIDTH(r) || y < 0 || y >= VT_HEIGHT(r))
4949 	return;		/* outside window */
4950 
4951 	r->h->selection_request_time = tm;
4952 	r->h->selection_wait = Sel_normal;
4953 
4954 #ifdef MULTICHAR_SET
4955    r->h->selection_type = Sel_CompoundText;
4956 #else
4957    r->h->selection_type = 0;
4958 #endif
4959    rxvt_selection_request_other(r, page,
4960 #ifdef MULTICHAR_SET
4961 	 r->h->xa[XA_COMPOUND_TEXT],
4962 #else
4963 	 XA_STRING,
4964 #endif
4965 		 sel);
4966 }
4967 
4968 
4969 /* ------------------------------------------------------------------------- */
4970 /*
4971  * Request the current selection:
4972  * Order: > internal selection if available
4973  *	> PRIMARY, SECONDARY, CLIPBOARD if ownership is claimed (+)
4974  *	> CUT_BUFFER0
4975  * (+) if ownership is claimed but property is empty, rxvt_selection_paste()
4976  *   will auto fallback to CUT_BUFFER0
4977  * EXT: button 2 release
4978  */
4979 /* EXTPROTO */
4980 void
4981 rxvt_selection_request(rxvt_t* r, int page, Time tm, int x, int y)
4982 {
4983     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_request %d (%lu, %d, %d)\n", page, tm, x, y ));
4984 
4985     if (x < 0 || x >= VT_WIDTH(r) || y < 0 || y >= VT_HEIGHT(r))
4986 	return;		/* outside window */
4987 
4988     if( SEL(r).text != NULL )	    /* internal selection */
4989     {
4990 	rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_request %d: pasting internal\n", page ));
4991 	rxvt_paste_str( r, page, SEL(r).text, SEL(r).len );
4992 	return;
4993     }
4994     else
4995     {
4996 	int	     i;
4997 
4998 	r->h->selection_request_time = tm;
4999 	r->h->selection_wait = Sel_normal;
5000 
5001 	for (i = Sel_Primary; i <= Sel_Clipboard; i++)
5002 	{
5003 #ifdef MULTICHAR_SET
5004 	    r->h->selection_type = Sel_CompoundText;
5005 #else
5006 	    r->h->selection_type = 0;
5007 #endif
5008 	    if (rxvt_selection_request_other(r, page,
5009 #ifdef MULTICHAR_SET
5010 		 r->h->xa[XA_COMPOUND_TEXT],
5011 #else
5012 		 XA_STRING,
5013 #endif
5014 		 i))
5015 	    return;
5016 	}
5017     }
5018 
5019     /* don't loop in rxvt_selection_paste() */
5020     r->h->selection_wait = Sel_none;
5021     rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_request %d: pasting CUT_BUFFER0\n", page ));
5022     rxvt_selection_paste(r, XROOT, XA_CUT_BUFFER0, False);
5023 }
5024 
5025 
5026 /* INTPROTO */
5027 int
5028 rxvt_selection_request_other(rxvt_t* r, int page, Atom target, int selnum)
5029 {
5030     Atom	    sel;
5031 #ifdef DEBUG
5032     char	   *debug_xa_names[] = { "PRIMARY", "SECONDARY", "CLIPBOARD" };
5033 #endif
5034 
5035     r->h->selection_type |= selnum;
5036     if (selnum == Sel_Primary)
5037 	sel = XA_PRIMARY;
5038     else if (selnum == Sel_Secondary)
5039 	sel = XA_SECONDARY;
5040     else
5041 	sel = r->h->xa[XA_CLIPBOARD];
5042     if (XGetSelectionOwner(r->Xdisplay, sel) != None)
5043     {
5044 	rxvt_dbgmsg(( DBG_DEBUG, DBG_SCREEN, "rxvt_selection_request_other %d: "
5045 		    "pasting %s\n", page, debug_xa_names[selnum] ));
5046 
5047 	XConvertSelection(r->Xdisplay, sel, target,
5048 	    r->h->xa[XA_VT_SELECTION], PVTS(r, page)->vt,
5049 	    r->h->selection_request_time);
5050 	return 1;
5051     }
5052     return 0;
5053 }
5054 
5055 
5056 /* ------------------------------------------------------------------------- */
5057 /*
5058  * Paste the content of the file specified by filename to the
5059  * currently active tab
5060  * EXT: button 2 release
5061  */
5062 /* EXTPROTO */
5063 void
5064 rxvt_paste_file(rxvt_t* r, int page, Time tm, int x, int y, char* filename)
5065 {
5066     rxvt_dbgmsg(( DBG_DEBUG, DBG_SCREEN,
5067 		"rxvt_paste_file %d (%lu, %d, %d) %s\n", page, tm, x, y,
5068 		filename ));
5069 
5070     if (x < 0 || x >= VT_WIDTH(r) || y < 0 || y >= VT_HEIGHT(r))
5071 	return;		/* outside window */
5072 
5073     char buffer[BUFSIZ];
5074     char TAINTED * str;
5075     FILE * fdpaste;
5076 
5077 #ifdef HAVE_WORDEXP_H
5078     wordexp_t p;
5079     int wordexp_result;
5080 
5081     /* perform a shell-like expansion of the provided filename */
5082     wordexp_result = wordexp(filename, &p, 0);
5083     if( wordexp_result == 0 && p.we_wordc == 1 )
5084 	filename = *p.we_wordv;
5085     else
5086     {
5087 	rxvt_msg( DBG_ERROR, DBG_SCREEN,
5088 		    "Error expanding %s, or possibly ambiguous expansion\n",
5089 		    filename );
5090 	rxvt_msg( DBG_INFO, DBG_SCREEN, "wordexp_result=%i\n", wordexp_result );
5091     }
5092 #endif
5093 
5094     if (NOT_NULL(fdpaste = fopen( filename , "r")))
5095     {
5096 	while (NOT_NULL(str = fgets(buffer, sizeof(buffer), fdpaste)))
5097 	    rxvt_paste_str( r, page, (const unsigned char*) str , STRLEN(str));
5098 
5099 	fclose(fdpaste);
5100     }
5101     else
5102     {
5103 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN,
5104 		    "rxvt_paste_file : unable to open file '%s'\n", filename));
5105     }
5106 
5107 #ifdef HAVE_WORDEXP_H
5108     wordfree(&p);
5109 #endif
5110     return;
5111 }
5112 
5113 
5114 
5115 /* ------------------------------------------------------------------------- */
5116 /*
5117  * Clear all selected text
5118  * EXT: SelectionClear
5119  */
5120 /* EXTPROTO */
5121 void
5122 rxvt_process_selectionclear(rxvt_t* r, int page)
5123 {
5124     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_process_selectionclear %d ()\n", page));
5125 
5126     PVTS(r, page)->want_refresh = 1;
5127     if (SEL(r).text)
5128 	rxvt_free(SEL(r).text);
5129     SEL(r).text = NULL;
5130     SEL(r).len = 0;
5131     CLEAR_SELECTION(r);
5132     SEL(r).vt = -1;
5133 
5134     SEL(r).op = SELECTION_CLEAR;
5135     SEL(r).screen = PRIMARY;
5136     SEL(r).clicks = 0;
5137 }
5138 
5139 
5140 /* ------------------------------------------------------------------------- */
5141 /*
5142  * Copy a selection into the cut buffer
5143  * EXT: button 1 or 3 release
5144  */
5145 /* EXTPROTO */
5146 void
5147 rxvt_selection_make(rxvt_t* r, int page, Time tm)
5148 {
5149     int		    i, col, end_col, row, end_row;
5150     unsigned char*  new_selection_text;
5151     unsigned char*  str;
5152     text_t*	    t;
5153 #ifdef MULTICHAR_SET
5154     rend_t*	    tr;
5155 #endif
5156 #ifdef ACS_ASCII
5157     rend_t*	    re;
5158 #endif
5159 
5160     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_make %d (): sel.op=%d, sel.clicks=%d\n", page, SEL(r).op, SEL(r).clicks));
5161     switch (SEL(r).op)
5162     {
5163 	case SELECTION_CONT:
5164 	    break;
5165 	case SELECTION_INIT:
5166 	    CLEAR_SELECTION(r);
5167 	    /* FALLTHROUGH */
5168 	case SELECTION_BEGIN:
5169 	    SEL(r).op = SELECTION_DONE;
5170 	    /* FALLTHROUGH */
5171 	default:
5172 	    return;
5173     }
5174     SEL(r).op = SELECTION_DONE;
5175     SEL(r).vt = page;	/* update selection vt */
5176 
5177     if (SEL(r).clicks == 4)
5178 	return;		/* nothing selected, go away */
5179 
5180     assert ((SEL(r).end.row - SEL(r).beg.row + 1) > 0);
5181     assert ((r->TermWin.ncol + 1) > 0);
5182     i = (SEL(r).end.row - SEL(r).beg.row + 1)
5183 	* (r->TermWin.ncol + 1) + 1;
5184     /* possible integer overflow? */
5185     assert (i > 0);
5186     str = rxvt_malloc(i * sizeof(char));
5187 
5188     new_selection_text = (unsigned char *)str;
5189 
5190     col = SEL(r).beg.col;
5191     MAX_IT(col, 0);
5192     row = SEL(r).beg.row + SVLINES;
5193     end_row = SEL(r).end.row + SVLINES;
5194 
5195     /*
5196     ** A: rows before end row
5197     */
5198     for (; row < end_row; row++, col = 0)
5199     {
5200 	t = &(PSCR(r, page).text[row][col]);
5201 #ifdef MULTICHAR_SET
5202 	tr = &(PSCR(r, page).rend[row][col]);
5203 #endif	/* MULTICHAR_SET */
5204 #ifdef ACS_ASCII
5205 	re = &(PSCR(r, page).rend[row][col]);
5206 #endif
5207 	if ((end_col = PSCR(r, page).tlen[row]) == -1)
5208 	    end_col = r->TermWin.ncol;
5209 
5210 
5211 	/*
5212 	** Looks like a completely mess. Think about the logic here
5213 	** carefully. ;-)
5214 	** Patch source:
5215 	** http://gentoo.nedlinux.nl/distfiles/rxvt-2.7.10-rk.patch
5216 	*/
5217 	for (; col < end_col; col++, str++, t++)
5218 	{
5219 #ifdef MULTICHAR_SET
5220 	    if (
5221 		  (ENC_EUCJ == r->encoding_method) && (*t & 0x80)
5222 		  && !(*tr & RS_multiMask)
5223 	       )
5224 	    {
5225 		*str++ = 0x8E;
5226 	    }
5227 	    tr ++;
5228 #endif	/* MULTICHAR_SET */
5229 #ifdef ACS_ASCII
5230 	    if ((*re++ & RS_acsFont) && *t >= 0x60 && *t < 0x80)
5231 		*str = r->h->rs[Rs_acs_chars][(*t) - 0x60];
5232 	    else
5233 #endif	/* ACS_ASCII */
5234 	    *str = *t;
5235 	}
5236 
5237 
5238 	if (PSCR(r, page).tlen[row] != -1)
5239 	{
5240 #ifdef DONT_SELECT_TRAILING_SPACES
5241 	    STRIP_TRAILING_SPACE(str, new_selection_text);
5242 #endif
5243 	    *str++ = '\n';
5244 	}
5245     }
5246 
5247     /*
5248     ** B: end row
5249     */
5250     t = &(PSCR(r, page).text[row][col]);
5251 #ifdef MULTICHAR_SET
5252     tr = &(PSCR(r, page).rend[row][col]);
5253 #endif	/* MULTICHAR_SET */
5254 #ifdef ACS_ASCII
5255     re = &(PSCR(r, page).rend[row][col]);
5256 #endif
5257     end_col = PSCR(r, page).tlen[row];
5258     if (end_col == -1 || SEL(r).end.col <= end_col)
5259 	end_col = SEL(r).end.col;
5260     MIN_IT(end_col, r->TermWin.ncol);	/* CHANGE */
5261 
5262 
5263     /*
5264     ** Looks like a completely mess. Think about the logic here
5265     ** carefully. ;-)
5266     ** Patch source:
5267     ** http://gentoo.nedlinux.nl/distfiles/rxvt-2.7.10-rk.patch
5268     */
5269     for (; col < end_col; col++, str++, t++)
5270     {
5271 #ifdef MULTICHAR_SET
5272 	if (
5273 	      (ENC_EUCJ == r->encoding_method) && (*t & 0x80)
5274 	      && !(*tr & RS_multiMask)
5275 	   )
5276 	{
5277 	    *str++ = 0x8E;
5278 	}
5279 	tr ++;
5280 #endif	/* MULTICHAR_SET */
5281 #ifdef ACS_ASCII
5282 	if ((*re++ & RS_acsFont) && *t >= 0x60 && *t < 0x80)
5283 	    *str = r->h->rs[Rs_acs_chars][(*t) - 0x60];
5284 	else
5285 #endif	/* ACS_ASCII */
5286 	*str = *t;
5287     }
5288 
5289 #ifdef DONT_SELECT_TRAILING_SPACES
5290     STRIP_TRAILING_SPACE(str, new_selection_text);
5291 #endif
5292 
5293 
5294 #ifndef NO_OLD_SELECTION
5295     if (r->selection_style == OLD_SELECT)
5296 	if (end_col == r->TermWin.ncol)
5297 	{
5298 	    *str++ = '\n';
5299 	}
5300 #endif
5301 #ifndef NO_NEW_SELECTION
5302     if (r->selection_style != OLD_SELECT)
5303 	if (end_col != SEL(r).end.col)
5304 	{
5305 	    *str++ = '\n';
5306 	}
5307 #endif
5308     *str = '\0';
5309     if ((i = STRLEN((char *)new_selection_text)) == 0)
5310     {
5311 	rxvt_free(new_selection_text);
5312 	return;
5313     }
5314     SEL(r).len = i;
5315     if (SEL(r).text)
5316 	rxvt_free(SEL(r).text);
5317     SEL(r).text = new_selection_text;
5318 
5319     XSetSelectionOwner(r->Xdisplay, XA_PRIMARY, PVTS(r, page)->vt, tm);
5320     if (XGetSelectionOwner(r->Xdisplay, XA_PRIMARY) != PVTS(r, page)->vt)
5321 	rxvt_msg (DBG_ERROR, DBG_SCREEN, "can't get primary selection");
5322     XChangeProperty(r->Xdisplay, XROOT, XA_CUT_BUFFER0, XA_STRING, 8,
5323 	PropModeReplace, SEL(r).text, (int)SEL(r).len);
5324     r->h->selection_time = tm;
5325     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_make %d (): sel.len=%d\n", page, SEL(r).len));
5326     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "sel.text=%s\n", SEL(r).text));
5327 }
5328 
5329 
5330 /* ------------------------------------------------------------------------- */
5331 /*
5332  * Mark or select text based upon number of clicks: 1, 2, or 3
5333  * EXT: button 1 press
5334  */
5335 /* EXTPROTO */
5336 void
5337 rxvt_selection_click(rxvt_t* r, int page, int clicks, int x, int y)
5338 {
5339     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_click %d (%d, %d, %d)\n", page, clicks, x, y));
5340 
5341     SEL(r).vt = page;
5342     clicks = ((clicks - 1) % 3) + 1;
5343     SEL(r).clicks = clicks; /* save clicks so extend will work */
5344 
5345     rxvt_selection_start_colrow(r, page, Pixel2Col(x), Pixel2Row(y));
5346 
5347     if (clicks == 2 || clicks == 3)
5348 	rxvt_selection_extend_colrow(r, page, SEL(r).mark.col,
5349 	    SEL(r).mark.row + VSTART,
5350 	    0,	/* button 3  */
5351 	    1,	/* button press */
5352 	    0);	/* click change */
5353 }
5354 
5355 
5356 /* ------------------------------------------------------------------------- */
5357 /*
5358  * Mark a selection at the specified col/row
5359  */
5360 /* INTPROTO */
5361 void
5362 rxvt_selection_start_colrow(rxvt_t* r, int page, int col, int row)
5363 {
5364     PVTS(r, page)->want_refresh = 1;
5365     SEL(r).mark.col = col;
5366     SEL(r).mark.row = row - VSTART;
5367     MAX_IT(SEL(r).mark.row, -(int32_t)PVTS(r, page)->nscrolled);
5368     MIN_IT(SEL(r).mark.row, (int32_t)r->TermWin.nrow - 1);
5369     MAX_IT(SEL(r).mark.col, 0);
5370     MIN_IT(SEL(r).mark.col, (int32_t)r->TermWin.ncol - 1);
5371 
5372     if (SEL(r).op)
5373     {
5374 	/* clear the old selection */
5375 	SEL(r).beg.row = SEL(r).end.row = SEL(r).mark.row;
5376 	SEL(r).beg.col = SEL(r).end.col = SEL(r).mark.col;
5377     }
5378     SEL(r).op = SELECTION_INIT;
5379     SEL(r).screen = PVTS(r, page)->current_screen;
5380     r->selection.vt = page;
5381 }
5382 
5383 
5384 /* ------------------------------------------------------------------------- */
5385 /*
5386  * Word select: select text for 2 clicks
5387  * We now only find out the boundary in one direction
5388  */
5389 
5390 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
5391 #define DELIMIT_TEXT(x) \
5392     (((x) == ' ' || (x) == '\t') ? 2 : (STRCHR(r->h->rs[Rs_cutchars], (x)) != NULL))
5393 #ifdef MULTICHAR_SET
5394 # define DELIMIT_REND(x)    (((x) & RS_multiMask) ? 1 : 0)
5395 #else
5396 # define DELIMIT_REND(x)    1
5397 #endif
5398 
5399 /* INTPROTO */
5400 void
5401 rxvt_selection_delimit_word(rxvt_t* r, int page, enum page_dirn dirn, const row_col_t *mark, row_col_t *ret)
5402 {
5403     int		 col, row, dirnadd, tcol, trow, w1, w2;
5404     row_col_t	   bound;
5405     text_t	 *stp;
5406     rend_t	 *srp;
5407 
5408 
5409     r->selection.vt = page; /* update selection vt */
5410 
5411     if (dirn == UP)
5412     {
5413 	bound.row = SVLINES - PVTS(r, page)->nscrolled - 1;
5414 	bound.col = 0;
5415 	dirnadd = -1;
5416     }
5417     else
5418     {
5419 	bound.row = SVLINES + r->TermWin.nrow;
5420 	bound.col = r->TermWin.ncol - 1;
5421 	dirnadd = 1;
5422     }
5423     row = mark->row + SVLINES;
5424     col = mark->col;
5425     MAX_IT(col, 0);
5426 /* find the edge of a word */
5427     stp = &(PSCR(r, page).text[row][col]);
5428     w1 = DELIMIT_TEXT(*stp);
5429 
5430     if (r->selection_style != NEW_SELECT)
5431     {
5432 	if (w1 == 1)
5433 	{
5434 	    stp += dirnadd;
5435 	    if (DELIMIT_TEXT(*stp) == 1)
5436 		goto Old_Word_Selection_You_Die;
5437 	    col += dirnadd;
5438 	}
5439 	w1 = 0;
5440     }
5441     srp = (&PSCR(r, page).rend[row][col]);
5442     w2 = DELIMIT_REND(*srp);
5443 
5444     for (;;)
5445     {
5446 	for (; col != bound.col; col += dirnadd)
5447 	{
5448 	    stp += dirnadd;
5449 	    if (DELIMIT_TEXT(*stp) != w1)
5450 		break;
5451 	    srp += dirnadd;
5452 	    if (DELIMIT_REND(*srp) != w2)
5453 		break;
5454 	}
5455 	if ((col == bound.col) && (row != bound.row))
5456 	{
5457 	    if (PSCR(r, page).tlen[(row - (dirn == UP ? 1 : 0))] == -1)
5458 	    {
5459 		trow = row + dirnadd;
5460 		tcol = dirn == UP ? r->TermWin.ncol - 1 : 0;
5461 		if (PSCR(r, page).text[trow] == NULL)
5462 		    break;
5463 		stp = &(PSCR(r, page).text[trow][tcol]);
5464 		srp = &(PSCR(r, page).rend[trow][tcol]);
5465 		if (DELIMIT_TEXT(*stp) != w1 ||
5466 		    DELIMIT_REND(*srp) != w2)
5467 		    break;
5468 		row = trow;
5469 		col = tcol;
5470 		continue;
5471 	    }
5472 	}
5473 	break;
5474     }
5475 
5476 Old_Word_Selection_You_Die:
5477     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_delimit_word %d (%s,...) @ (r:%3d, c:%3d) has boundary (r:%3d, c:%3d)\n", page, (dirn == UP ? "up    " : "down"), mark->row, mark->col, row - SVLINES, col));
5478 
5479     if (dirn == DN)
5480 	col++;		/* put us on one past the end */
5481 
5482 /* Poke the values back in */
5483     ret->row = row - SVLINES;
5484     ret->col = col;
5485 }
5486 
5487 
5488 /* ------------------------------------------------------------------------- */
5489 /*
5490  * Extend the selection to the specified x/y pixel location
5491  * EXT: button 3 press; button 1 or 3 drag
5492  * flag == 0 ==> button 1
5493  * flag == 1 ==> button 3 press
5494  * flag == 2 ==> button 3 motion
5495  */
5496 /* EXTPROTO */
5497 void
5498 rxvt_selection_extend(rxvt_t* r, int page, int x, int y, int flag)
5499 {
5500     int		 col, row;
5501 
5502     col = Pixel2Col(x);
5503     row = Pixel2Row(y);
5504     MAX_IT(row, 0);
5505     MIN_IT(row, (int)r->TermWin.nrow - 1);
5506     MAX_IT(col, 0);
5507     MIN_IT(col, (int)r->TermWin.ncol);
5508 
5509 #ifndef NO_NEW_SELECTION
5510 /*
5511  * If we're selecting characters (single click) then we must check first
5512  * if we are at the same place as the original mark.  If we are then
5513  * select nothing.  Otherwise, if we're to the right of the mark, you have to
5514  * be _past_ a character for it to be selected.
5515  */
5516     if (r->selection_style != OLD_SELECT)
5517     {
5518 	if (
5519 	      ((SEL(r).clicks % 3) == 1) && !flag
5520 	      && (
5521 		    col == SEL(r).mark.col
5522 		    && (row == SEL(r).mark.row + VSTART)
5523 		 )
5524 	   )
5525 	{
5526 	    /* select nothing */
5527 	    SEL(r).beg.row = SEL(r).end.row = 0;
5528 	    SEL(r).beg.col = SEL(r).end.col = 0;
5529 	    SEL(r).clicks = 4;
5530 	    PVTS(r, page)->want_refresh = 1;
5531 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_SCREEN, "rxvt_selection_extend %d () sel.clicks = 4\n", page));
5532 	    return;
5533 	}
5534     }
5535 #endif
5536     if (SEL(r).clicks == 4)
5537 	SEL(r).clicks = 1;
5538     rxvt_selection_extend_colrow(r, page, col, row, !!flag,
5539 	/* ? button 3	  */
5540 	 flag == 1 ? 1 : 0, /* ? button press  */
5541 	 0);	/* no click change */
5542 }
5543 
5544 
5545 #ifdef MULTICHAR_SET
5546 /* INTPROTO */
5547 void
5548 rxvt_selection_adjust_kanji(rxvt_t* r, int page)
5549 {
5550     int		 c1, r1;
5551 
5552     if (SEL(r).beg.col > 0)
5553     {
5554 	r1 = SEL(r).beg.row + SVLINES;
5555 	c1 = SEL(r).beg.col;
5556 	if (IS_MULTI2(PSCR(r, page).rend[r1][c1]) &&
5557 	    IS_MULTI1(PSCR(r, page).rend[r1][c1 - 1]))
5558 	    SEL(r).beg.col--;
5559     }
5560     if (SEL(r).end.col < r->TermWin.ncol)
5561     {
5562 	r1 = SEL(r).end.row + SVLINES;
5563 	c1 = SEL(r).end.col;
5564 	if (IS_MULTI1(PSCR(r, page).rend[r1][c1 - 1]) &&
5565 	    IS_MULTI2(PSCR(r, page).rend[r1][c1]))
5566 	    SEL(r).end.col++;
5567     }
5568 }
5569 #endif		    /* MULTICHAR_SET */
5570 
5571 
5572 /* ------------------------------------------------------------------------- */
5573 /*
5574  * Extend the selection to the specified col/row
5575  */
5576 /* INTPROTO */
5577 void
5578 rxvt_selection_extend_colrow(rxvt_t* r, int page, int32_t col, int32_t row, int button3, int buttonpress, int clickchange)
5579 {
5580     unsigned int    ncol = r->TermWin.ncol;
5581     row_col_t	    pos;
5582 #ifndef NO_NEW_SELECTION
5583     int		 end_col;
5584     enum {
5585 	LEFT, RIGHT
5586     } closeto = RIGHT;
5587 #endif
5588 
5589 
5590     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_extend_colrow %d (c:%d, r:%d, %d, %d) clicks:%d, op:%d\n", page, col, row, button3, buttonpress, SEL(r).clicks, SEL(r).op));
5591     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_extend_colrow %d () ENT  b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)\n", page, SEL(r).beg.row, SEL(r).beg.col, SEL(r).mark.row, SEL(r).mark.col, SEL(r).end.row, SEL(r).end.col));
5592 
5593     PVTS(r, page)->want_refresh = 1;
5594     switch (SEL(r).op)
5595     {
5596 	case SELECTION_INIT:
5597 	    CLEAR_SELECTION(r);
5598 	    SEL(r).op = SELECTION_BEGIN;
5599 	    /* FALLTHROUGH */
5600 	case SELECTION_BEGIN:
5601 	    if (row != SEL(r).mark.row ||
5602 		col != SEL(r).mark.col ||
5603 		(!button3 && buttonpress))
5604 		SEL(r).op = SELECTION_CONT;
5605 	    break;
5606 	case SELECTION_DONE:
5607 	    SEL(r).op = SELECTION_CONT;
5608 	    /* FALLTHROUGH */
5609 	case SELECTION_CONT:
5610 	    break;
5611 	case SELECTION_CLEAR:
5612 	    rxvt_selection_start_colrow(r, page, col, row);
5613 	    /* FALLTHROUGH */
5614 	default:
5615 	    return;
5616     }
5617 
5618     if (
5619 	  SEL(r).beg.col == SEL(r).end.col
5620 	  && SEL(r).beg.col != SEL(r).mark.col
5621 	  && SEL(r).beg.row == SEL(r).end.row
5622 	  && SEL(r).beg.row != SEL(r).mark.row
5623        )
5624     {
5625 	SEL(r).beg.col = SEL(r).end.col = SEL(r).mark.col;
5626 	SEL(r).beg.row = SEL(r).end.row = SEL(r).mark.row;
5627 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN,
5628 	    "rxvt_selection_extend_colrow %d () "
5629 	    "ENT2 b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)\n",
5630 	    page, SEL(r).beg.row, SEL(r).beg.col, SEL(r).mark.row,
5631 	    SEL(r).mark.col, SEL(r).end.row, SEL(r).end.col));
5632     }
5633 
5634     pos.col = col;
5635     pos.row = row;
5636 
5637     pos.row -= VSTART;	/* adjust for scroll */
5638 
5639 
5640 #ifndef NO_OLD_SELECTION
5641     /*
5642     ** This mimics some of the selection behaviour of version 2.20
5643     ** and before.
5644     ** There are no ``selection modes'', button3 is always character
5645     ** extension.
5646     ** Note: button3 drag is always available, c.f. v2.20
5647     ** Selection always terminates (left or right as appropriate) at
5648     ** the mark.
5649     */
5650     if (r->selection_style == OLD_SELECT)
5651     {
5652 	if (SEL(r).clicks == 1 || button3)
5653 	{
5654 	    if (r->h->hate_those_clicks)
5655 	    {
5656 		r->h->hate_those_clicks = 0;
5657 		if (SEL(r).clicks == 1)
5658 		{
5659 		    SEL(r).beg.row = SEL(r).mark.row;
5660 		    SEL(r).beg.col = SEL(r).mark.col;
5661 		}
5662 		else
5663 		{
5664 		    SEL(r).mark.row = SEL(r).beg.row;
5665 		    SEL(r).mark.col = SEL(r).beg.col;
5666 		}
5667 	    }
5668 
5669 	    if (RC_BEFORE(pos, SEL(r).mark))
5670 	    {
5671 		SEL(r).end.row = SEL(r).mark.row;
5672 		SEL(r).end.col = SEL(r).mark.col + 1;
5673 		SEL(r).beg.row = pos.row;
5674 		SEL(r).beg.col = pos.col;
5675 	    }
5676 	    else
5677 	    {
5678 		SEL(r).beg.row = SEL(r).mark.row;
5679 		SEL(r).beg.col = SEL(r).mark.col;
5680 		SEL(r).end.row = pos.row;
5681 		SEL(r).end.col = pos.col + 1;
5682 	    }
5683 # ifdef MULTICHAR_SET
5684 	    rxvt_selection_adjust_kanji(r, page);
5685 # endif		    /* MULTICHAR_SET */
5686 	}
5687 	else if (SEL(r).clicks == 2)
5688 	{
5689 	    rxvt_selection_delimit_word(r, page, UP, &(SEL(r).mark),
5690 		&(SEL(r).beg));
5691 	    rxvt_selection_delimit_word(r, page, DN, &(SEL(r).mark),
5692 		&(SEL(r).end));
5693 	    r->h->hate_those_clicks = 1;
5694 	}
5695 	else if (SEL(r).clicks == 3)
5696 	{
5697 	    SEL(r).beg.row = SEL(r).end.row = SEL(r).mark.row;
5698 	    SEL(r).beg.col = 0;
5699 	    SEL(r).end.col = ncol;
5700 	    r->h->hate_those_clicks = 1;
5701 	}
5702 
5703 	rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_extend_colrow %d () EXIT b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)\n", page, SEL(r).beg.row, SEL(r).beg.col, SEL(r).mark.row, SEL(r).mark.col, SEL(r).end.row, SEL(r).end.col));
5704 	return;
5705     }
5706 #endif		    /* ! NO_OLD_SELECTION */
5707 
5708 
5709 #ifndef NO_NEW_SELECTION
5710     /* selection_style must not be OLD_SELECT to get here */
5711     /*
5712     ** This is mainly xterm style selection with a couple of
5713     ** differences, mainly in the way button3 drag extension
5714     ** works.
5715     ** We're either doing: button1 drag; button3 press; or
5716     ** button3 drag
5717     **  a) button1 drag : select around a midpoint/word/line -
5718     ** that point/word/line is always at the left/right edge
5719     ** of the SEL(r).
5720     **  b) button3 press: extend/contract character/word/line
5721     ** at whichever edge of the selection we are closest to.
5722     **  c) button3 drag : extend/contract character/word/line
5723     ** - we select around a point/word/line which is either
5724     ** the start or end of the selection and it was decided
5725     ** by whichever point/word/line was `fixed' at the time
5726     ** of the most recent button3 press
5727     */
5728     if (button3 && buttonpress)	    /* button3 press */
5729     {
5730 	/* first determine which edge of the selection we are
5731 	** closest to
5732 	*/
5733 	if (RC_BEFORE(pos, SEL(r).beg) ||
5734 	    (!RC_AFTER(pos, SEL(r).end) &&
5735 	     (((pos.col - SEL(r).beg.col) +
5736 	       ((pos.row - SEL(r).beg.row) * ncol)) <
5737 	      ((SEL(r).end.col - pos.col) +
5738 	       ((SEL(r).end.row - pos.row) * ncol)))))
5739 	     closeto = LEFT;
5740 
5741 	if (closeto == LEFT)
5742 	{
5743 	    SEL(r).beg.row = pos.row;
5744 	    SEL(r).beg.col = pos.col;
5745 	    SEL(r).mark.row = SEL(r).end.row;
5746 	    SEL(r).mark.col = SEL(r).end.col
5747 		    - (SEL(r).clicks == 2);
5748 	}
5749 	else
5750 	{
5751 	    SEL(r).end.row = pos.row;
5752 	    SEL(r).end.col = pos.col;
5753 	    SEL(r).mark.row = SEL(r).beg.row;
5754 	    SEL(r).mark.col = SEL(r).beg.col;
5755 	}
5756     }
5757     else	    /* button1 drag or button3 drag */
5758     {
5759 	if (RC_AFTER(SEL(r).mark, pos))
5760 	{
5761 	    if ((SEL(r).mark.row == SEL(r).end.row) &&
5762 		(SEL(r).mark.col == SEL(r).end.col) &&
5763 		clickchange && SEL(r).clicks == 2)
5764 		SEL(r).mark.col--;
5765 	    SEL(r).beg.row = pos.row;
5766 	    SEL(r).beg.col = pos.col;
5767 	    SEL(r).end.row = SEL(r).mark.row;
5768 	    SEL(r).end.col = SEL(r).mark.col
5769 	       + (SEL(r).clicks == 2);
5770 	}
5771 	else
5772 	{
5773 	    SEL(r).beg.row = SEL(r).mark.row;
5774 		SEL(r).beg.col = SEL(r).mark.col;
5775 		SEL(r).end.row = pos.row;
5776 		SEL(r).end.col = pos.col;
5777 	}
5778     }
5779 
5780 
5781     if (SEL(r).clicks == 1)
5782     {
5783 	end_col = PSCR(r, page).tlen[SEL(r).beg.row + SVLINES];
5784 	if (end_col != -1 && SEL(r).beg.col > end_col)
5785 	{
5786 #if 1
5787 	    SEL(r).beg.col = ncol;
5788 #else
5789 	    if (SEL(r).beg.row != SEL(r).end.row)
5790 		SEL(r).beg.col = ncol;
5791 	    else
5792 		SEL(r).beg.col = SEL(r).mark.col;
5793 #endif
5794 	}
5795 	end_col = PSCR(r, page).tlen[SEL(r).end.row +
5796 		    SVLINES];
5797 	if (end_col != -1 && SEL(r).end.col > end_col)
5798 	    SEL(r).end.col = ncol;
5799 
5800 # ifdef MULTICHAR_SET
5801 	rxvt_selection_adjust_kanji(r, page);
5802 # endif		    /* MULTICHAR_SET */
5803     }
5804     else if (SEL(r).clicks == 2)
5805     {
5806 	if (RC_AFTER(SEL(r).end, SEL(r).beg))
5807 	    SEL(r).end.col--;
5808 	rxvt_selection_delimit_word(r, page, UP, &(SEL(r).beg),
5809 	    &(SEL(r).beg));
5810 	rxvt_selection_delimit_word(r, page, DN, &(SEL(r).end),
5811 	    &(SEL(r).end));
5812     }
5813     else if (SEL(r).clicks == 3)
5814     {
5815 #ifndef NO_FRILLS
5816 	if (ISSET_OPTION(r, Opt_tripleclickwords))
5817 	{
5818 	    int		 end_row;
5819 
5820 	    rxvt_selection_delimit_word(r, page, UP, &(SEL(r).beg),
5821 		&(SEL(r).beg));
5822 	    end_row = PSCR(r, page).tlen[SEL(r).mark.row +
5823 			SVLINES];
5824 	    for (
5825 		  end_row = SEL(r).mark.row;
5826 		  end_row < r->TermWin.nrow;
5827 		  end_row++
5828 		)
5829 	    {
5830 		end_col = PSCR(r, page).tlen[end_row + SVLINES];
5831 		if (end_col != -1)
5832 		{
5833 		    SEL(r).end.row = end_row;
5834 		    SEL(r).end.col = end_col;
5835 		    rxvt_selection_trim(r, page);
5836 		    break;
5837 		}
5838 	    }	/* for */
5839 	}
5840 	else
5841 #endif
5842 	{
5843 	    if (RC_AFTER(SEL(r).mark, SEL(r).beg))
5844 		SEL(r).mark.col++;
5845 	    SEL(r).beg.col = 0;
5846 	    SEL(r).end.col = ncol;
5847 	}
5848     }	/* if (ISSET_OPTION(r, Opt_tripleclickwords)) */
5849 
5850     if (button3 && buttonpress)
5851     {
5852 	/* mark may need to be changed */
5853 	if (closeto == LEFT)
5854 	{
5855 	    SEL(r).mark.row = SEL(r).end.row;
5856 	    SEL(r).mark.col = SEL(r).end.col - (SEL(r).clicks == 2);
5857 	}
5858 	else
5859 	{
5860 	    SEL(r).mark.row = SEL(r).beg.row;
5861 	    SEL(r).mark.col = SEL(r).beg.col;
5862 	}
5863     }
5864 
5865     rxvt_dbgmsg ((DBG_VERBOSE, DBG_SCREEN, "rxvt_selection_extend_colrow %d () EXIT b:(r:%d,c:%d) m:(r:%d,c:%d), e:(r:%d,c:%d)\n", page, SEL(r).beg.row, SEL(r).beg.col, SEL(r).mark.row, SEL(r).mark.col, SEL(r).end.row, SEL(r).end.col));
5866 
5867 #endif		    /* ! NO_NEW_SELECTION */
5868 }
5869 
5870 
5871 #ifndef NO_FRILLS
5872 /* INTPROTO */
5873 void
5874 rxvt_selection_trim(rxvt_t* r, int page)
5875 {
5876     int32_t	 end_col, end_row;
5877     text_t	 *stp;
5878 
5879     end_col = SEL(r).end.col;
5880     end_row = SEL(r).end.row;
5881     for ( ; end_row >= SEL(r).beg.row; )
5882     {
5883 	stp = PSCR(r, page).text[end_row + SVLINES];
5884 	while (--end_col >= 0)
5885 	{
5886 	    if (stp[end_col] != ' ' && stp[end_col] != '\t')
5887 		break;
5888 	}
5889 	if (end_col >= 0 || PSCR(r, page).tlen[end_row - 1 + SVLINES] != -1)
5890 	{
5891 	    SEL(r).end.col = end_col + 1;
5892 	    SEL(r).end.row = end_row;
5893 	    break;
5894 	}
5895 	end_row--;
5896 	end_col = r->TermWin.ncol;
5897     }
5898 
5899     if (SEL(r).mark.row > SEL(r).end.row)
5900     {
5901 	SEL(r).mark.row = SEL(r).end.row;
5902 	SEL(r).mark.col = SEL(r).end.col;
5903     }
5904     else if (SEL(r).mark.row == SEL(r).end.row &&
5905 	SEL(r).mark.col > SEL(r).end.col)
5906 	SEL(r).mark.col = SEL(r).end.col;
5907 }
5908 #endif
5909 
5910 
5911 /* ------------------------------------------------------------------------- */
5912 /*
5913  * Double click on button 3 when already selected
5914  * EXT: button 3 double click
5915  */
5916 /* EXTPROTO */
5917 void
5918 rxvt_selection_rotate(rxvt_t* r, int page, int x, int y)
5919 {
5920     SEL(r).clicks = SEL(r).clicks % 3 + 1;
5921 
5922     rxvt_selection_extend_colrow (r, page, Pixel2Col(x),
5923 	Pixel2Row(y), 1, 0, 1);
5924 }
5925 
5926 
5927 
5928 /* ------------------------------------------------------------------------- */
5929 /*
5930  * Respond to a request for our current selection
5931  * EXT: SelectionRequest
5932  */
5933 /* EXTPROTO */
5934 void
5935 rxvt_process_selectionrequest (rxvt_t* r, int page, const XSelectionRequestEvent *rq)
5936 {
5937     XSelectionEvent ev;
5938 #ifdef USE_XIM
5939     Atom	  target_list[4];
5940 #else
5941     Atom	  target_list[3];
5942 #endif
5943     Atom	    target;
5944     XTextProperty   ct;
5945     XICCEncodingStyle style;
5946     char	   *cl[2], dummy[1];
5947 
5948     ev.type = SelectionNotify;
5949     ev.property = None;
5950     ev.display = rq->display;
5951     ev.requestor = rq->requestor;
5952     ev.selection = rq->selection;
5953     ev.target = rq->target;
5954     ev.time = rq->time;
5955 
5956     if (rq->target == r->h->xa[XA_TARGETS])
5957     {
5958 	target_list[0] = r->h->xa[XA_TARGETS];
5959 	target_list[1] = XA_STRING;
5960 	target_list[2] = r->h->xa[XA_TEXT];
5961 #ifdef USE_XIM
5962 	target_list[3] = r->h->xa[XA_COMPOUND_TEXT];
5963 #endif
5964 	XChangeProperty(r->Xdisplay, rq->requestor, rq->property,
5965 	    XA_ATOM, 32, PropModeReplace,
5966 	    (unsigned char *)target_list,
5967 	    (sizeof(target_list) / sizeof(target_list[0])));
5968 	ev.property = rq->property;
5969     }
5970     else if (rq->target == r->h->xa[XA_MULTIPLE])
5971     {
5972 	/* TODO: Handle MULTIPLE */
5973     }
5974     else if (rq->target == r->h->xa[XA_TIMESTAMP] && SEL(r).text)
5975     {
5976 	XChangeProperty(r->Xdisplay, rq->requestor, rq->property,
5977 	    XA_INTEGER,
5978 	    sizeof(Time) > 4 ? 32 : (8 * sizeof(Time)),
5979 	    PropModeReplace, (unsigned char*)&r->h->selection_time,
5980 	    sizeof(Time) > 4 ? sizeof(Time)/4 : 1);
5981 	ev.property = rq->property;
5982     }
5983     else if (
5984 	      rq->target == XA_STRING
5985 	      || rq->target == r->h->xa[XA_COMPOUND_TEXT]
5986 	      || rq->target == r->h->xa[XA_TEXT]
5987 	    )
5988     {
5989 #ifdef USE_XIM
5990 	short	       freect = 0;
5991 #endif
5992 	int	     selectlen;
5993 
5994 #ifdef USE_XIM
5995 	if (rq->target != XA_STRING)
5996 	{
5997 	    target = r->h->xa[XA_COMPOUND_TEXT];
5998 	    style = (rq->target == r->h->xa[XA_COMPOUND_TEXT])
5999 		? XCompoundTextStyle : XStdICCTextStyle;
6000 	} else
6001 #endif
6002 	{
6003 	    target = XA_STRING;
6004 	    style = XStringStyle;
6005 	}
6006 	if (SEL(r).text)
6007 	{
6008 	    cl[0] = (char *)SEL(r).text;
6009 	    selectlen = SEL(r).len;
6010 	}
6011 	else
6012 	{
6013 	    cl[0] = dummy;
6014 	    *dummy = '\0';
6015 	    selectlen = 0;
6016 	}
6017 #ifdef USE_XIM
6018 	if (XmbTextListToTextProperty(r->Xdisplay, cl, 1, style, &ct) == Success)	/* if we failed to convert then send it raw */
6019 	    freect = 1;
6020 	else
6021 #endif
6022 	{
6023 	    ct.value = (unsigned char *)cl[0];
6024 	    ct.nitems = selectlen;
6025 	}
6026 	XChangeProperty(r->Xdisplay, rq->requestor, rq->property,
6027 	    target, 8, PropModeReplace,
6028 	    ct.value, (int)ct.nitems);
6029 	ev.property = rq->property;
6030 #ifdef USE_XIM
6031 	if (freect)
6032 	    XFree(ct.value);
6033 #endif
6034     }
6035     XSendEvent(r->Xdisplay, rq->requestor, False, 0L, (XEvent *)&ev);
6036 }
6037 
6038 /* ------------------------------------------------------------------------- *
6039  *			      MOUSE ROUTINES				   *
6040  * ------------------------------------------------------------------------- */
6041 
6042 /*
6043  * return col/row values corresponding to x/y pixel values
6044  */
6045 /* EXTPROTO */
6046 void
6047 rxvt_pixel_position(rxvt_t* r, int *x, int *y)
6048 {
6049     *x = Pixel2Col(*x);
6050 /* MAX_IT(*x, 0); MIN_IT(*x, (int)r->TermWin.ncol - 1); */
6051     *y = Pixel2Row(*y);
6052 /* MAX_IT(*y, 0); MIN_IT(*y, (int)r->TermWin.nrow - 1); */
6053 }
6054 
6055 /*----------------------- end-of-file (C source) -----------------------*/
6056