1 /*
2  * File:      screen.c
3  *
4  */
5 
6 static const char cvs_ident[] = "$Id: screen.c 51650 2010-08-26 01:34:13Z lucas $";
7 
8 #include "config.h"
9 #include "feature.h"
10 
11 /* includes */
12 #include <stdio.h>
13 #include <stdlib.h>
14 #ifdef HAVE_SYS_TIME_H
15 # include <sys/time.h>
16 #endif
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #ifdef HAVE_UNISTD_H
20 # include <unistd.h>
21 #endif
22 #ifdef HAVE_SYS_IOCTL_H
23 # include <sys/ioctl.h>
24 #endif
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <X11/Xatom.h>
28 #include <X11/Xmd.h>            /* CARD32 */
29 
30 #include "buttons.h"
31 #include "command.h"
32 #include "font.h"
33 #include "startup.h"
34 #include "screen.h"
35 #include "scrollbar.h"
36 #include "options.h"
37 #include "pixmap.h"
38 #include "profile.h"
39 #include "term.h"
40 
41 #ifdef ESCREEN
42 #  include "screamcfg.h"
43 #endif
44 
45 static int pb = 0;
46 
47 /* These arrays store the text and rendering info that were last drawn to the screen. */
48 static text_t **drawn_text = NULL;
49 static rend_t **drawn_rend = NULL;
50 
51 /* These are used for buffering during text scrolls. */
52 static text_t **buf_text = NULL;
53 static rend_t **buf_rend = NULL;
54 
55 /* Tab stop locations */
56 static char *tabs = NULL;
57 
58 #ifndef ESCREEN
59 static
60 #endif
61 screen_t screen = {
62     NULL, NULL, 0, 0, 0, 0, 0, Screen_DefaultFlags
63 };
64 
65 static screen_t swap = {
66     NULL, NULL, 0, 0, 0, 0, 0, Screen_DefaultFlags
67 };
68 
69 static save_t save = {
70     0, 0, 0, 'B', DEFAULT_RSTYLE
71 };
72 
73 static selection_t selection = {
74     NULL, 0, SELECTION_CLEAR, PRIMARY, 0,
75     {0, 0},
76     {0, 0},
77     {0, 0}
78 };
79 
80 static char charsets[4] = {
81     'B', 'B', 'B', 'B'          /* all ascii */
82 };
83 
84 static short current_screen = PRIMARY;
85 static rend_t rstyle = DEFAULT_RSTYLE;
86 static short rvideo = 0;
87 int prev_nrow = -1, prev_ncol = -1;
88 unsigned char refresh_all = 0;
89 
90 #ifdef MULTI_CHARSET
91 static short multi_byte = 0;
92 static short lost_multi = 0;
93 static enum {
94     SBYTE, WBYTE
95 } chstat = SBYTE;
96 encoding_t encoding_method = LATIN1;
97 
98 #define RESET_CHSTAT	if (chstat == WBYTE) chstat = SBYTE, lost_multi = 1
99 #else
100 #define RESET_CHSTAT
101 #endif
102 
103 /* Fill part/all of a drawn line with blanks. */
104 inline void blank_line(text_t *, rend_t *, int, rend_t);
105 inline void
blank_line(text_t * et,rend_t * er,int width,rend_t efs)106 blank_line(text_t *et, rend_t *er, int width, rend_t efs)
107 {
108 /*    int             i = width; */
109     register unsigned int i = width;
110     rend_t *r = er, fs = efs;
111 
112     MEMSET(et, ' ', i);
113     for (; i--;)
114         *r++ = fs;
115 }
116 
117 /* Create a new row in the screen buffer and initialize it. */
118 inline void blank_screen_mem(text_t **, rend_t **, int, rend_t);
119 inline void
blank_screen_mem(text_t ** tp,rend_t ** rp,int row,rend_t efs)120 blank_screen_mem(text_t **tp, rend_t **rp, int row, rend_t efs)
121 {
122     register unsigned int i = TERM_WINDOW_GET_REPORTED_COLS();
123     rend_t *r, fs = efs;
124 
125     if (!tp[row]) {
126         tp[row] = MALLOC(sizeof(text_t) * (TERM_WINDOW_GET_REPORTED_COLS() + 1));
127         rp[row] = MALLOC(sizeof(rend_t) * TERM_WINDOW_GET_REPORTED_COLS());
128     }
129     MEMSET(tp[row], ' ', i);
130     tp[row][i] = 0;
131     for (r = rp[row]; i--;)
132         *r++ = fs;
133 }
134 
135 void
scr_reset(void)136 scr_reset(void)
137 {
138     int total_rows, prev_total_rows, chscr = 0;
139     register int i, j, k;
140     text_t tc;
141 
142     D_SCREEN(("scr_reset()\n"));
143 
144     TermWin.view_start = 0;
145     RESET_CHSTAT;
146 
147     if (TERM_WINDOW_GET_REPORTED_COLS() == prev_ncol && TERM_WINDOW_GET_REPORTED_ROWS() == prev_nrow)
148         return;
149 
150     if (current_screen != PRIMARY) {
151         short tmp = TermWin.nrow;
152 
153         TermWin.nrow = prev_nrow;
154         chscr = scr_change_screen(PRIMARY);
155         TermWin.nrow = tmp;
156     }
157     if (TermWin.ncol <= 0)
158         TermWin.ncol = 80;
159     if (TermWin.nrow <= 0)
160         TermWin.nrow = 24;
161     if (TermWin.saveLines <= 0)
162         TermWin.saveLines = 0;
163 
164     total_rows = TermWin.nrow + TermWin.saveLines;
165     prev_total_rows = prev_nrow + TermWin.saveLines;
166 
167     screen.tscroll = 0;
168     screen.bscroll = (TERM_WINDOW_GET_REPORTED_ROWS() - 1);
169     if (prev_nrow == -1) {
170         /*
171          * A: first time called so just malloc everything : don't rely on realloc
172          *    Note: this is still needed so that all the scrollback lines are NULL
173          */
174         screen.text = CALLOC(text_t *, total_rows);
175         buf_text = CALLOC(text_t *, total_rows);
176         drawn_text = CALLOC(text_t *, TERM_WINDOW_GET_REPORTED_ROWS());
177         swap.text = CALLOC(text_t *, TERM_WINDOW_GET_REPORTED_ROWS());
178 
179         screen.rend = CALLOC(rend_t *, total_rows);
180         buf_rend = CALLOC(rend_t *, total_rows);
181         drawn_rend = CALLOC(rend_t *, TERM_WINDOW_GET_REPORTED_ROWS());
182         swap.rend = CALLOC(rend_t *, TERM_WINDOW_GET_REPORTED_ROWS());
183         D_SCREEN(("screen.text == %8p, screen.rend == %8p, swap.text == %8p, swap.rend == %8p\n",
184                   screen.text, screen.rend, swap.text, swap.rend));
185 
186         for (i = 0; i < TERM_WINDOW_GET_REPORTED_ROWS(); i++) {
187             j = i + TermWin.saveLines;
188             blank_screen_mem(screen.text, screen.rend, j, DEFAULT_RSTYLE);
189             blank_screen_mem(swap.text, swap.rend, i, DEFAULT_RSTYLE);
190             blank_screen_mem(drawn_text, drawn_rend, i, DEFAULT_RSTYLE);
191         }
192         TermWin.nscrolled = 0;  /* no saved lines */
193 
194     } else {
195         /*
196          * B1: add or delete rows as appropriate
197          */
198         if (TERM_WINDOW_GET_REPORTED_ROWS() < prev_nrow) {
199             /* delete rows */
200             k = MIN(TermWin.nscrolled, prev_nrow - TERM_WINDOW_GET_REPORTED_ROWS());
201             scroll_text(0, prev_nrow - 1, k, 1);
202 
203             for (i = TERM_WINDOW_GET_REPORTED_ROWS(); i < prev_nrow; i++) {
204                 j = i + TermWin.saveLines;
205                 if (screen.text[j]) {
206                     FREE(screen.text[j]);
207                     FREE(screen.rend[j]);
208                 }
209                 if (swap.text[i]) {
210                     FREE(swap.text[i]);
211                     FREE(swap.rend[i]);
212                 }
213                 if (drawn_text[i]) {
214                     FREE(drawn_text[i]);
215                     FREE(drawn_rend[i]);
216                 }
217             }
218             screen.text = REALLOC(screen.text, total_rows * sizeof(text_t *));
219             buf_text = REALLOC(buf_text, total_rows * sizeof(text_t *));
220             drawn_text = REALLOC(drawn_text, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(text_t *));
221             swap.text = REALLOC(swap.text, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(text_t *));
222 
223             screen.rend = REALLOC(screen.rend, total_rows * sizeof(rend_t *));
224             buf_rend = REALLOC(buf_rend, total_rows * sizeof(rend_t *));
225             drawn_rend = REALLOC(drawn_rend, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(rend_t *));
226             swap.rend = REALLOC(swap.rend, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(rend_t *));
227             D_SCREEN(("screen.text == %8p, screen.rend == %8p, swap.text == %8p, swap.rend == %8p\n",
228                       screen.text, screen.rend, swap.text, swap.rend));
229 
230             /* we have fewer rows so fix up number of scrolled lines */
231             UPPER_BOUND(screen.row, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
232 
233         } else if (TERM_WINDOW_GET_REPORTED_ROWS() > prev_nrow) {
234             /* add rows */
235             screen.text = REALLOC(screen.text, total_rows * sizeof(text_t *));
236             buf_text = REALLOC(buf_text, total_rows * sizeof(text_t *));
237             drawn_text = REALLOC(drawn_text, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(text_t *));
238             swap.text = REALLOC(swap.text, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(text_t *));
239 
240             screen.rend = REALLOC(screen.rend, total_rows * sizeof(rend_t *));
241             buf_rend = REALLOC(buf_rend, total_rows * sizeof(rend_t *));
242             drawn_rend = REALLOC(drawn_rend, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(rend_t *));
243             swap.rend = REALLOC(swap.rend, TERM_WINDOW_GET_REPORTED_ROWS() * sizeof(rend_t *));
244             D_SCREEN(("screen.text == %8p, screen.rend == %8p, swap.text == %8p, swap.rend == %8p\n",
245                       screen.text, screen.rend, swap.text, swap.rend));
246 
247             k = MIN(TermWin.nscrolled, TERM_WINDOW_GET_REPORTED_ROWS() - prev_nrow);
248             for (i = prev_total_rows; i < total_rows - k; i++) {
249                 screen.text[i] = NULL;
250                 blank_screen_mem(screen.text, screen.rend, i, DEFAULT_RSTYLE);
251             }
252             for ( /* i = total_rows - k */ ; i < total_rows; i++) {
253                 screen.text[i] = NULL;
254                 screen.rend[i] = NULL;
255             }
256             for (i = prev_nrow; i < TERM_WINDOW_GET_REPORTED_ROWS(); i++) {
257                 swap.text[i] = NULL;
258                 drawn_text[i] = NULL;
259                 blank_screen_mem(swap.text, swap.rend, i, DEFAULT_RSTYLE);
260                 blank_screen_mem(drawn_text, drawn_rend, i, DEFAULT_RSTYLE);
261             }
262             if (k > 0) {
263                 scroll_text(0, TERM_WINDOW_GET_REPORTED_ROWS() - 1, -k, 1);
264                 screen.row += k;
265                 TermWin.nscrolled -= k;
266                 for (i = TermWin.saveLines - TermWin.nscrolled; k--; i--) {
267                     if (!screen.text[i]) {
268                         blank_screen_mem(screen.text, screen.rend, i, DEFAULT_RSTYLE);
269                     }
270                 }
271             }
272         }
273         /* B2: resize columns */
274         if (TERM_WINDOW_GET_REPORTED_COLS() != prev_ncol) {
275             for (i = 0; i < total_rows; i++) {
276                 if (screen.text[i]) {
277                     tc = screen.text[i][prev_ncol];
278                     screen.text[i] = REALLOC(screen.text[i], (TERM_WINDOW_GET_REPORTED_COLS() + 1) * sizeof(text_t));
279                     screen.rend[i] = REALLOC(screen.rend[i], TERM_WINDOW_GET_REPORTED_COLS() * sizeof(rend_t));
280                     screen.text[i][TERM_WINDOW_GET_REPORTED_COLS()] = MIN(tc, TERM_WINDOW_GET_REPORTED_COLS());
281                     if (TERM_WINDOW_GET_REPORTED_COLS() > prev_ncol)
282                         blank_line(&(screen.text[i][prev_ncol]), &(screen.rend[i][prev_ncol]),
283                                    TERM_WINDOW_GET_REPORTED_COLS() - prev_ncol, DEFAULT_RSTYLE);
284                 }
285             }
286             for (i = 0; i < TERM_WINDOW_GET_REPORTED_ROWS(); i++) {
287                 drawn_text[i] = REALLOC(drawn_text[i], (TERM_WINDOW_GET_REPORTED_COLS() + 1) * sizeof(text_t));
288                 drawn_rend[i] = REALLOC(drawn_rend[i], TERM_WINDOW_GET_REPORTED_COLS() * sizeof(rend_t));
289                 if (swap.text[i]) {
290                     tc = swap.text[i][prev_ncol];
291                     swap.text[i] = REALLOC(swap.text[i], (TERM_WINDOW_GET_REPORTED_COLS() + 1) * sizeof(text_t));
292                     swap.rend[i] = REALLOC(swap.rend[i], TERM_WINDOW_GET_REPORTED_COLS() * sizeof(rend_t));
293                     swap.text[i][TERM_WINDOW_GET_REPORTED_COLS()] = MIN(tc, TERM_WINDOW_GET_REPORTED_COLS());
294                     if (TERM_WINDOW_GET_REPORTED_COLS() > prev_ncol)
295                         blank_line(&(swap.text[i][prev_ncol]), &(swap.rend[i][prev_ncol]),
296                                    TERM_WINDOW_GET_REPORTED_COLS() - prev_ncol, DEFAULT_RSTYLE);
297                 }
298                 if (TERM_WINDOW_GET_REPORTED_COLS() > prev_ncol)
299                     blank_line(&(drawn_text[i][prev_ncol]), &(drawn_rend[i][prev_ncol]),
300                                TERM_WINDOW_GET_REPORTED_COLS() - prev_ncol, DEFAULT_RSTYLE);
301             }
302         }
303         if (tabs)
304             FREE(tabs);
305     }
306     tabs = MALLOC(TERM_WINDOW_GET_REPORTED_COLS());
307 
308     for (i = 0; i < TERM_WINDOW_GET_REPORTED_COLS(); i++)
309         tabs[i] = (i % TABSIZE == 0) ? 1 : 0;
310 
311     prev_nrow = TERM_WINDOW_GET_REPORTED_ROWS();
312     prev_ncol = TERM_WINDOW_GET_REPORTED_COLS();
313 
314     tt_resize();
315     if (chscr) {
316         scr_change_screen(chscr);
317     }
318 }
319 
320 /* Release all screen memory. */
321 void
scr_release(void)322 scr_release(void)
323 {
324     int total_rows;
325     register int i;
326 
327     total_rows = TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines;
328     for (i = 0; i < total_rows; i++) {
329         if (screen.text[i]) {
330             FREE(screen.text[i]);
331             FREE(screen.rend[i]);
332         }
333     }
334     for (i = 0; i < TERM_WINDOW_GET_REPORTED_ROWS(); i++) {
335         FREE(drawn_text[i]);
336         FREE(drawn_rend[i]);
337         FREE(swap.text[i]);
338         FREE(swap.rend[i]);
339     }
340     FREE(screen.text);
341     FREE(screen.rend);
342     FREE(drawn_text);
343     FREE(drawn_rend);
344     FREE(swap.text);
345     FREE(swap.rend);
346     FREE(buf_text);
347     FREE(buf_rend);
348     FREE(tabs);
349 }
350 
351 /* Perform a full reset on the terminal.  Called by the "\ec" sequence or by an xterm color change. */
352 void
scr_poweron(void)353 scr_poweron(void)
354 {
355     D_SCREEN(("scr_poweron()\n"));
356 
357     /* Reset all character sets to Latin1 */
358     MEMSET(charsets, 'B', sizeof(charsets));
359     rvideo = 0;
360     /* Reset the rendering style to the default colors/style */
361     scr_rendition(0, ~RS_None);
362 #if NSCREENS
363     if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_SECONDARY_SCREEN)) {
364         /* Reset the secondary screen */
365         scr_change_screen(SECONDARY);
366         scr_erase_screen(2);
367         swap.tscroll = 0;
368         swap.bscroll = TERM_WINDOW_GET_REPORTED_ROWS() - 1;
369         swap.row = swap.col = 0;
370         swap.charset = 0;
371         swap.flags = Screen_DefaultFlags;
372     }
373 #endif
374     /* Reset the primary screen */
375     scr_change_screen(PRIMARY);
376     scr_erase_screen(2);
377     screen.row = screen.col = 0;
378     screen.charset = 0;
379     screen.flags = Screen_DefaultFlags;
380 
381     scr_cursor(SAVE);
382     TermWin.nscrolled = 0;
383     scr_reset();
384     scr_refresh(SLOW_REFRESH);
385 }
386 
387 /*
388  * Save and Restore cursor
389  * XTERM_SEQ: Save cursor   : ESC 7
390  * XTERM_SEQ: Restore cursor: ESC 8
391  */
392 void
scr_cursor(int mode)393 scr_cursor(int mode)
394 {
395     D_SCREEN(("scr_cursor(%s)\n", (mode == SAVE ? "SAVE" : "RESTORE")));
396 
397     switch (mode) {
398         case SAVE:
399             save.row = screen.row;
400             save.col = screen.col;
401             save.rstyle = rstyle;
402             save.charset = screen.charset;
403             save.charset_char = charsets[screen.charset];
404             break;
405         case RESTORE:
406             screen.row = save.row;
407             screen.col = save.col;
408             rstyle = save.rstyle;
409             screen.charset = save.charset;
410             charsets[screen.charset] = save.charset_char;
411             set_font_style();
412             break;
413     }
414 }
415 
416 /*
417  * Swap between primary and secondary screens
418  * XTERM_SEQ: Primary screen  : ESC [ ? 4 7 h
419  * XTERM_SEQ: Secondary screen: ESC [ ? 4 7 l
420  */
421 int
scr_change_screen(int scrn)422 scr_change_screen(int scrn)
423 {
424     int i, offset, tmp;
425     text_t *t0;
426     rend_t *r0;
427 
428     D_SCREEN(("scr_change_screen(%d)\n", scrn));
429 
430     TermWin.view_start = 0;
431     RESET_CHSTAT;
432 
433     if (current_screen == scrn)
434         return current_screen;
435 
436     SWAP_IT(current_screen, scrn, tmp);
437 #if NSCREENS
438     if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_SECONDARY_SCREEN)) {
439         offset = TermWin.saveLines;
440         if (!screen.text || !screen.rend)
441             return (current_screen);
442         for (i = TERM_WINDOW_GET_REPORTED_ROWS(); i--;) {
443             SWAP_IT(screen.text[i + offset], swap.text[i], t0);
444             SWAP_IT(screen.rend[i + offset], swap.rend[i], r0);
445         }
446         SWAP_IT(screen.row, swap.row, tmp);
447         SWAP_IT(screen.col, swap.col, tmp);
448         SWAP_IT(screen.charset, swap.charset, tmp);
449         SWAP_IT(screen.flags, swap.flags, tmp);
450         screen.flags |= Screen_VisibleCursor;
451         swap.flags |= Screen_VisibleCursor;
452     }
453 #else
454 # ifndef DONT_SCROLL_ME
455     if (current_screen == PRIMARY) {
456         scroll_text(0, (TERM_WINDOW_GET_REPORTED_ROWS() - 1), TERM_WINDOW_GET_REPORTED_ROWS(), 0);
457         for (i = TermWin.saveLines; i < TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines; i++)
458             if (!screen.text[i]) {
459                 blank_screen_mem(screen.text, screen.rend, i, DEFAULT_RSTYLE);
460             }
461     }
462 # endif
463 #endif
464 
465     return scrn;
466 }
467 
468 /*
469  * Change the color for following text
470  */
471 void
scr_color(unsigned int color,unsigned int Intensity)472 scr_color(unsigned int color, unsigned int Intensity)
473 {
474 
475     D_SCREEN(("scr_color(%u, %u) called.\n", color, Intensity));
476     if (color == restoreFG)
477         color = fgColor;
478     else if (color == restoreBG)
479         color = bgColor;
480     else {
481         if (Xdepth <= 2) {      /* Monochrome - ignore color changes */
482             switch (Intensity) {
483                 case RS_Bold:
484                     color = fgColor;
485                     break;
486                 case RS_Blink:
487                     color = bgColor;
488                     break;
489             }
490         } else {
491             if ((rstyle & Intensity) && ((int) color >= minColor) && (color <= maxColor)) {
492                 switch (Intensity) {
493                     case RS_Bold:
494                         if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_BOLD_BRIGHTENS_FOREGROUND)) {
495                             color += (minBright - minColor);
496                         }
497                         break;
498                     case RS_Blink:
499                         if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_BLINK_BRIGHTENS_BACKGROUND)) {
500                             color += (minBright - minColor);
501                         }
502                         break;
503                 }
504             } else if (!(rstyle & Intensity) && ((int) color >= minBright) && (color <= maxBright)) {
505                 switch (Intensity) {
506                     case RS_Bold:
507                         if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_BOLD_BRIGHTENS_FOREGROUND)) {
508                             color -= (minBright - minColor);
509                         }
510                         break;
511                     case RS_Blink:
512                         if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_BLINK_BRIGHTENS_BACKGROUND)) {
513                             color -= (minBright - minColor);
514                         }
515                         break;
516                 }
517             }
518         }
519     }
520     switch (Intensity) {
521         case RS_Bold:
522             rstyle = SET_FGCOLOR(rstyle, color);
523             break;
524         case RS_Blink:
525             rstyle = SET_BGCOLOR(rstyle, color);
526             break;
527     }
528 }
529 
530 /*
531  * Change the rendition style for following text
532  */
533 void
scr_rendition(int set,int style)534 scr_rendition(int set, int style)
535 {
536     unsigned int color;
537     int old_style = rstyle;
538 
539     D_SCREEN(("scr_rendition(%d, %d) called.\n", set, style));
540     if (set) {
541 /* A: Set style */
542         rstyle |= style;
543         switch (style) {
544             case RS_RVid:
545                 if (rvideo)
546                     rstyle &= ~RS_RVid;
547                 break;
548             case RS_Bold:
549                 color = GET_FGCOLOR(rstyle);
550                 scr_color((color == fgColor ? GET_FGCOLOR(colorfgbg) : color), RS_Bold);
551                 break;
552             case RS_Blink:
553                 color = GET_BGCOLOR(rstyle);
554                 scr_color((color == bgColor ? GET_BGCOLOR(colorfgbg) : color), RS_Blink);
555                 break;
556         }
557     } else {
558 /* B: Unset style */
559         rstyle &= ~style;
560 
561         switch (style) {
562             case ~RS_None:     /* default fg/bg colors */
563                 rstyle = DEFAULT_RSTYLE | (old_style & RS_fontMask);
564                 /* FALLTHROUGH */
565             case RS_RVid:
566                 if (rvideo)
567                     rstyle |= RS_RVid;
568                 break;
569             case RS_Bold:
570                 color = GET_FGCOLOR(rstyle);
571                 if (color >= minBright && color <= maxBright) {
572                     scr_color(color, RS_Bold);
573                     if ((rstyle & RS_fgMask) == (colorfgbg & RS_fgMask))
574                         scr_color(restoreFG, RS_Bold);
575                 }
576                 break;
577             case RS_Blink:
578                 color = GET_BGCOLOR(rstyle);
579                 if (color >= minBright && color <= maxBright) {
580                     scr_color(color, RS_Blink);
581                     if ((rstyle & RS_bgMask) == (colorfgbg & RS_bgMask))
582                         scr_color(restoreBG, RS_Blink);
583                 }
584                 break;
585         }
586     }
587 }
588 
589 /* Scroll text region from <row1> through <row2> by <count> lines */
590 int
scroll_text(int row1,int row2,int count,int spec)591 scroll_text(int row1, int row2, int count, int spec)
592 {
593     register int i, j;
594 
595     PROF_INIT(scroll_text);
596     D_SCREEN(("scroll_text(%d,%d,%d,%d): %s\n", row1, row2, count, spec, (current_screen == PRIMARY) ? "Primary" : "Secondary"));
597 
598     if (count == 0 || (row1 > row2))
599         return 0;
600     if ((count > 0) && (row1 == 0) && (current_screen == PRIMARY)) {
601         TermWin.nscrolled += count;
602         UPPER_BOUND(TermWin.nscrolled, TermWin.saveLines);
603     } else if (!spec)
604         row1 += TermWin.saveLines;
605     row2 += TermWin.saveLines;
606 
607     if (selection.op && current_screen == selection.screen) {
608         i = selection.beg.row + TermWin.saveLines;
609         j = selection.end.row + TermWin.saveLines;
610         if ((i < row1 && j > row1)
611             || (i < row2 && j > row2)
612             || (i - count < row1 && i >= row1)
613             || (i - count > row2 && i <= row2)
614             || (j - count < row1 && j >= row1)
615             || (j - count > row2 && j <= row2)) {
616             CLEAR_ALL_SELECTION;
617             selection.op = SELECTION_CLEAR;
618         } else if (j >= row1 && j <= row2) {
619             /* move selected region too */
620             selection.beg.row -= count;
621             selection.end.row -= count;
622             selection.mark.row -= count;
623         }
624     }
625     CHECK_SELECTION;
626 
627     if (count > 0) {
628 
629 /* A: scroll up */
630 
631         UPPER_BOUND(count, row2 - row1 + 1);
632 
633 /* A1: Copy and blank out lines that will get clobbered by the rotation */
634         for (i = 0, j = row1; i < count; i++, j++) {
635             buf_text[i] = screen.text[j];
636             buf_rend[i] = screen.rend[j];
637             if (!buf_text[i]) {
638                 /* A new ALLOC is done with size ncol and
639                    blankline with size prev_ncol -- Sebastien van K */
640                 buf_text[i] = MALLOC(sizeof(text_t) * (prev_ncol + 1));
641                 buf_rend[i] = MALLOC(sizeof(rend_t) * prev_ncol);
642             }
643             blank_line(buf_text[i], buf_rend[i], prev_ncol, DEFAULT_RSTYLE);
644             buf_text[i][prev_ncol] = 0;
645         }
646 /* A2: Rotate lines */
647         for (j = row1; (j + count) <= row2; j++) {
648             screen.text[j] = screen.text[j + count];
649             screen.rend[j] = screen.rend[j + count];
650         }
651 /* A3: Resurrect lines */
652         for (i = 0; i < count; i++, j++) {
653             screen.text[j] = buf_text[i];
654             screen.rend[j] = buf_rend[i];
655         }
656     } else if (count < 0) {
657 /* B: scroll down */
658 
659         count = MIN(-count, row2 - row1 + 1);
660 /* B1: Copy and blank out lines that will get clobbered by the rotation */
661         for (i = 0, j = row2; i < count; i++, j--) {
662             buf_text[i] = screen.text[j];
663             buf_rend[i] = screen.rend[j];
664             if (!buf_text[i]) {
665                 /* A new ALLOC is done with size ncol and
666                    blankline with size prev_ncol -- Sebastien van K */
667                 buf_text[i] = MALLOC(sizeof(text_t) * (prev_ncol + 1));
668                 buf_rend[i] = MALLOC(sizeof(rend_t) * prev_ncol);
669             }
670             blank_line(buf_text[i], buf_rend[i], prev_ncol, DEFAULT_RSTYLE);
671             buf_text[i][prev_ncol] = 0;
672         }
673 /* B2: Rotate lines */
674         for (j = row2; (j - count) >= row1; j--) {
675             screen.text[j] = screen.text[j - count];
676             screen.rend[j] = screen.rend[j - count];
677         }
678 /* B3: Resurrect lines */
679         for (i = 0, j = row1; i < count; i++, j++) {
680             screen.text[j] = buf_text[i];
681             screen.rend[j] = buf_rend[i];
682         }
683         count = -count;
684     }
685     PROF_DONE(scroll_text);
686     PROF_TIME(scroll_text);
687     return count;
688 }
689 
690 /*
691  * Add text given in <str> of length <len> to screen struct
692  */
693 void
scr_add_lines(const unsigned char * str,int nlines,int len)694 scr_add_lines(const unsigned char *str, int nlines, int len)
695 {
696 /*    char            c; */
697     register char c;
698 
699 /*    int             i, j, row, last_col; */
700     int last_col;
701     register int i, j, row;
702     text_t *stp;
703     rend_t *srp;
704     row_col_t beg, end;
705 
706     if (len <= 0)               /* sanity */
707         return;
708 
709     last_col = TERM_WINDOW_GET_REPORTED_COLS();
710 
711     D_SCREEN(("scr_add_lines(*,%d,%d)\n", nlines, len));
712     ZERO_SCROLLBACK;
713     if (nlines > 0) {
714         nlines += (screen.row - screen.bscroll);
715         D_SCREEN((" -> screen.row == %d, screen.bscroll == %d, new nlines == %d\n", screen.row, screen.bscroll, nlines));
716         if ((nlines > 0) && (screen.tscroll == 0) && (screen.bscroll == (TERM_WINDOW_GET_REPORTED_ROWS() - 1))) {
717             /* _at least_ this many lines need to be scrolled */
718             scroll_text(screen.tscroll, screen.bscroll, nlines, 0);
719             for (i = nlines, row = screen.bscroll + TermWin.saveLines + 1; row > 0 && i--;) {
720                 /* Move row-- to beginning of loop to avoid segfault. -- added by Sebastien van K */
721                 row--;
722                 blank_screen_mem(screen.text, screen.rend, row, rstyle);
723             }
724             screen.row -= nlines;
725         }
726     }
727     UPPER_BOUND(screen.col, last_col - 1);
728     BOUND(screen.row, -TermWin.nscrolled, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
729 
730     row = screen.row + TermWin.saveLines;
731     if (!screen.text[row]) {
732         blank_screen_mem(screen.text, screen.rend, row, DEFAULT_RSTYLE);
733     }                           /* avoid segfault -- added by Sebastien van K */
734     beg.row = screen.row;
735     beg.col = screen.col;
736     stp = screen.text[row];
737     srp = screen.rend[row];
738 
739 #ifdef MULTI_CHARSET
740     if (lost_multi && screen.col > 0 && ((srp[screen.col - 1] & RS_multiMask) == RS_multi1)
741         && *str != '\n' && *str != '\r' && *str != '\t')
742         chstat = WBYTE;
743 #endif
744 
745     for (i = 0; i < len;) {
746         c = str[i++];
747 #ifdef MULTI_CHARSET
748         if ((encoding_method != LATIN1) && (chstat == WBYTE)) {
749             rstyle |= RS_multiMask;     /* multibyte 2nd byte */
750             chstat = SBYTE;
751             if (encoding_method == EUCJ) {
752                 c |= 0x80;      /* maybe overkill, but makes it selectable */
753             }
754         } else if (chstat == SBYTE) {
755             if ((encoding_method != LATIN1) && (multi_byte || (c & 0x80))) {    /* multibyte 1st byte */
756                 rstyle &= ~RS_multiMask;
757                 rstyle |= RS_multi1;
758                 chstat = WBYTE;
759                 if (encoding_method == EUCJ) {
760                     c |= 0x80;  /* maybe overkill, but makes it selectable */
761                 }
762             } else
763 #endif
764                 switch (c) {
765                     case 127:
766                         continue;       /* ummmm..... */
767                     case '\t':
768                         scr_tab(1);
769                         continue;
770                     case '\n':
771                         LOWER_BOUND(stp[last_col], screen.col);
772                         screen.flags &= ~Screen_WrapNext;
773                         if (screen.row == screen.bscroll) {
774                             scroll_text(screen.tscroll, screen.bscroll, 1, 0);
775                             j = screen.bscroll + TermWin.saveLines;
776                             blank_screen_mem(screen.text, screen.rend, j, rstyle & ~(RS_Uline | RS_Overscore));
777                         } else if (screen.row < (TERM_WINDOW_GET_REPORTED_ROWS() - 1)) {
778                             screen.row++;
779                             row = screen.row + TermWin.saveLines;
780                         }
781                         stp = screen.text[row]; /* _must_ refresh */
782                         srp = screen.rend[row]; /* _must_ refresh */
783                         continue;
784                     case '\r':
785                         LOWER_BOUND(stp[last_col], screen.col);
786                         screen.flags &= ~Screen_WrapNext;
787                         screen.col = 0;
788                         continue;
789                     default:
790 #ifdef MULTI_CHARSET
791                         rstyle &= ~RS_multiMask;
792 #endif
793                         break;
794                 }
795 #ifdef MULTI_CHARSET
796         }
797 #endif
798         if (screen.flags & Screen_WrapNext) {
799             stp[last_col] = WRAP_CHAR;
800             if (screen.row == screen.bscroll) {
801                 scroll_text(screen.tscroll, screen.bscroll, 1, 0);
802                 j = screen.bscroll + TermWin.saveLines;
803                 /* blank_line(screen.text[j], screen.rend[j], TermWin.ncol,
804                    rstyle);    Bug fix from John Ellison - need to reset rstyle */
805                 blank_screen_mem(screen.text, screen.rend, j, rstyle & ~(RS_Uline | RS_Overscore));
806             } else if (screen.row < (TERM_WINDOW_GET_REPORTED_ROWS() - 1)) {
807                 screen.row++;
808                 row = screen.row + TermWin.saveLines;
809             }
810             stp = screen.text[row];     /* _must_ refresh */
811             srp = screen.rend[row];     /* _must_ refresh */
812             screen.col = 0;
813             screen.flags &= ~Screen_WrapNext;
814         }
815         if (screen.flags & Screen_Insert)
816             scr_insdel_chars(1, INSERT);
817         stp[screen.col] = c;
818         srp[screen.col] = rstyle;
819         if (screen.col < (last_col - 1))
820             screen.col++;
821         else {
822             stp[last_col] = last_col;
823             if (screen.flags & Screen_Autowrap)
824                 screen.flags |= Screen_WrapNext;
825             else
826                 screen.flags &= ~Screen_WrapNext;
827         }
828     }
829     LOWER_BOUND(stp[last_col], screen.col);
830     if (screen.col == 0) {
831         end.col = last_col - 1;
832         end.row = screen.row - 1;
833     } else {
834         end.col = screen.col - 1;
835         end.row = screen.row;
836     }
837     if (((selection.end.row > beg.row)
838          || (selection.end.row == beg.row && selection.end.col >= beg.col))
839         && ((selection.beg.row < end.row)
840             || (selection.beg.row == end.row && selection.beg.col <= end.col)))
841         selection_reset();
842 
843 #ifdef ESCREEN
844     if (NS_MAGIC_LINE(TermWin.screen_mode)) {
845         if (screen.row >= TERM_WINDOW_GET_ROWS()) {     /* last row -> upd-flag */
846             TermWin.screen_pending |= 1;
847         }
848     }
849 #endif
850 }
851 
852 /*
853  * Process Backspace.  Move back the cursor back a position, wrap if have to
854  * XTERM_SEQ: CTRL-H
855  */
856 void
scr_backspace(void)857 scr_backspace(void)
858 {
859 
860     RESET_CHSTAT;
861     if (screen.col == 0 && screen.row > 0) {
862         screen.col = TERM_WINDOW_GET_REPORTED_COLS() - 1;
863         screen.row--;
864     } else if (screen.flags & Screen_WrapNext) {
865         screen.flags &= ~Screen_WrapNext;
866     } else
867         scr_gotorc(0, -1, RELATIVE);
868 }
869 
870 /*
871  * Process Horizontal Tab
872  * count: +ve = forward; -ve = backwards
873  * XTERM_SEQ: CTRL-I
874  */
875 void
scr_tab(int count)876 scr_tab(int count)
877 {
878     int i, x;
879 
880     RESET_CHSTAT;
881     x = screen.col;
882     if (count == 0)
883         return;
884     else if (count > 0) {
885         for (i = x + 1; i < TERM_WINDOW_GET_REPORTED_COLS(); i++) {
886             if (tabs[i]) {
887                 x = i;
888                 if (!--count)
889                     break;
890             }
891         }
892     } else if (count < 0) {
893         for (i = x - 1; i >= 0; i--) {
894             if (tabs[i]) {
895                 x = i;
896                 if (!++count)
897                     break;
898             }
899         }
900     }
901     if (x != screen.col)
902         scr_gotorc(0, x, R_RELATIVE);
903 }
904 
905 /*
906  * Goto Row/Column
907  */
908 void
scr_gotorc(int row,int col,int relative)909 scr_gotorc(int row, int col, int relative)
910 {
911     ZERO_SCROLLBACK;
912     RESET_CHSTAT;
913 
914     screen.col = ((relative & C_RELATIVE) ? (screen.col + col) : col);
915     BOUND(screen.col, 0, TERM_WINDOW_GET_REPORTED_COLS() - 1);
916 
917     if (screen.flags & Screen_WrapNext) {
918         screen.flags &= ~Screen_WrapNext;
919     }
920     if (relative & R_RELATIVE) {
921         if (row > 0) {
922             if (screen.row <= screen.bscroll && (screen.row + row) > screen.bscroll)
923                 screen.row = screen.bscroll;
924             else
925                 screen.row += row;
926         } else if (row < 0) {
927             if (screen.row >= screen.tscroll && (screen.row + row) < screen.tscroll)
928                 screen.row = screen.tscroll;
929             else
930                 screen.row += row;
931         }
932     } else {
933         if (screen.flags & Screen_Relative) {   /* relative origin mode */
934             screen.row = row + screen.tscroll;
935             UPPER_BOUND(screen.row, screen.bscroll);
936         } else
937             screen.row = row;
938     }
939 #ifdef ESCREEN
940     if (NS_MAGIC_LINE(TermWin.screen_mode)) {
941         if (screen.row >= TERM_WINDOW_GET_ROWS()) {     /* last row -> upd-flag */
942             TermWin.screen_pending |= 1;
943         } else if (TermWin.screen_pending) {    /* left last -> upd-finis */
944             TermWin.screen_pending |= 2;
945         }
946     }
947 #endif
948     BOUND(screen.row, 0, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
949 }
950 
951 /*
952  * direction  should be UP or DN
953  */
954 void
scr_index(int direction)955 scr_index(int direction)
956 {
957     int dirn;
958 
959     dirn = ((direction == UP) ? 1 : -1);
960     D_SCREEN(("scr_index(%d)\n", dirn));
961 
962     ZERO_SCROLLBACK;
963     RESET_CHSTAT;
964 
965     if (screen.flags & Screen_WrapNext) {
966         screen.flags &= ~Screen_WrapNext;
967     }
968     if ((screen.row == screen.bscroll && direction == UP)
969         || (screen.row == screen.tscroll && direction == DN)) {
970         scroll_text(screen.tscroll, screen.bscroll, dirn, 0);
971         if (direction == UP)
972             dirn = screen.bscroll + TermWin.saveLines;
973         else
974             dirn = screen.tscroll + TermWin.saveLines;
975         blank_screen_mem(screen.text, screen.rend, dirn, rstyle);
976     } else
977         screen.row += dirn;
978     BOUND(screen.row, 0, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
979     CHECK_SELECTION;
980 }
981 
982 /*
983  * Erase part or whole of a line
984  * XTERM_SEQ: Clear line to right: ESC [ 0 K
985  * XTERM_SEQ: Clear line to left : ESC [ 1 K
986  * XTERM_SEQ: Clear whole line   : ESC [ 2 K
987  */
988 void
scr_erase_line(int mode)989 scr_erase_line(int mode)
990 {
991     int row, col, num;
992 
993     D_SCREEN(("scr_erase_line(%d) at screen row: %d\n", mode, screen.row));
994     ZERO_SCROLLBACK;
995     RESET_CHSTAT;
996 
997     if (screen.flags & Screen_WrapNext) {
998         screen.flags &= ~Screen_WrapNext;
999     }
1000 
1001     row = TermWin.saveLines + screen.row;
1002     ASSERT(row < TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines);
1003 
1004     if (screen.text[row]) {
1005         switch (mode) {
1006             case 0:            /* erase to end of line */
1007                 col = screen.col;
1008                 num = TERM_WINDOW_GET_REPORTED_COLS() - col;
1009                 UPPER_BOUND(screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()], col);
1010                 break;
1011             case 1:            /* erase to beginning of line */
1012                 col = 0;
1013                 num = screen.col + 1;
1014                 break;
1015             case 2:            /* erase whole line */
1016                 col = 0;
1017                 num = TERM_WINDOW_GET_REPORTED_COLS();
1018                 screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()] = 0;
1019                 break;
1020             default:
1021                 return;
1022         }
1023         blank_line(&(screen.text[row][col]), &(screen.rend[row][col]), num, rstyle & ~(RS_Uline | RS_Overscore));
1024     } else {
1025         blank_screen_mem(screen.text, screen.rend, row, rstyle & ~(RS_Uline | RS_Overscore));
1026     }
1027 }
1028 
1029 /*
1030  * Erase part of whole of the screen
1031  * XTERM_SEQ: Clear screen after cursor : ESC [ 0 J
1032  * XTERM_SEQ: Clear screen before cursor: ESC [ 1 J
1033  * XTERM_SEQ: Clear whole screen        : ESC [ 2 J
1034  */
1035 void
scr_erase_screen(int mode)1036 scr_erase_screen(int mode)
1037 {
1038     int row, num, row_offset;
1039     rend_t ren;
1040     long gcmask;
1041     XGCValues gcvalue;
1042     Pixmap pmap = None;
1043     Drawable draw_buffer;
1044 
1045     if (buffer_pixmap) {
1046         draw_buffer = buffer_pixmap;
1047         pmap = images[image_bg].current->pmap->pixmap;
1048     } else {
1049         draw_buffer = TermWin.vt;
1050     }
1051 
1052     D_SCREEN(("scr_erase_screen(%d) at screen row: %d\n", mode, screen.row));
1053     REFRESH_ZERO_SCROLLBACK;
1054     RESET_CHSTAT;
1055     row_offset = TermWin.saveLines;
1056 
1057 
1058     switch (mode) {
1059         case 0:                /* erase to end of screen */
1060             scr_erase_line(0);
1061             row = screen.row + 1;       /* possible OOB */
1062             num = TERM_WINDOW_GET_REPORTED_ROWS() - row;
1063             break;
1064         case 1:                /* erase to beginning of screen */
1065             scr_erase_line(1);
1066             row = 0;            /* possible OOB */
1067             num = screen.row;
1068             break;
1069         case 2:                /* erase whole screen */
1070             row = 0;
1071             num = TERM_WINDOW_GET_REPORTED_ROWS();
1072             break;
1073         default:
1074             return;
1075     }
1076     if (row >= 0 && row <= TERM_WINDOW_GET_REPORTED_ROWS()) {   /* check OOB */
1077         UPPER_BOUND(num, (TERM_WINDOW_GET_REPORTED_ROWS() - row));
1078         if (rstyle & RS_RVid || rstyle & RS_Uline || rstyle & RS_Overscore)
1079             ren = -1;
1080         else {
1081             if (GET_BGCOLOR(rstyle) == bgColor) {
1082                 ren = DEFAULT_RSTYLE;
1083                 CLEAR_ROWS(row, num);
1084             } else {
1085                 ren = (rstyle & (RS_fgMask | RS_bgMask));
1086                 gcvalue.foreground = PixColors[GET_BGCOLOR(ren)];
1087                 gcmask = GCForeground;
1088                 XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
1089                 ERASE_ROWS(row, num);
1090                 gcvalue.foreground = PixColors[fgColor];
1091                 XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
1092             }
1093         }
1094         for (; num--; row++) {
1095             blank_screen_mem(screen.text, screen.rend, row + row_offset, rstyle & ~(RS_RVid | RS_Uline | RS_Overscore));
1096             blank_screen_mem(drawn_text, drawn_rend, row, ren);
1097         }
1098     }
1099 }
1100 
1101 /*
1102  * Fill the screen with `E's
1103  * XTERM_SEQ: Screen Alignment Test: ESC # 8
1104  */
1105 void
scr_E(void)1106 scr_E(void)
1107 {
1108     int i, j;
1109     text_t *t;
1110     rend_t *r, fs;
1111 
1112     ZERO_SCROLLBACK;
1113     RESET_CHSTAT;
1114 
1115     fs = rstyle;
1116     for (i = TermWin.saveLines; i < TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines; i++) {
1117         t = screen.text[i];
1118         r = screen.rend[i];
1119         for (j = 0; j < TERM_WINDOW_GET_REPORTED_COLS(); j++) {
1120             *t++ = 'E';
1121             *r++ = fs;
1122         }
1123         *t = '\0';
1124     }
1125 }
1126 
1127 /*
1128  * Insert/Delete <count> lines
1129  */
1130 void
scr_insdel_lines(int count,int insdel)1131 scr_insdel_lines(int count, int insdel)
1132 {
1133     int end;
1134 
1135     ZERO_SCROLLBACK;
1136     RESET_CHSTAT;
1137 
1138     if (screen.row > screen.bscroll)
1139         return;
1140 
1141     end = screen.bscroll - screen.row + 1;
1142     if (count > end) {
1143         if (insdel == DELETE)
1144             return;
1145         else if (insdel == INSERT)
1146             count = end;
1147     }
1148     if (screen.flags & Screen_WrapNext) {
1149         screen.flags &= ~Screen_WrapNext;
1150     }
1151     scroll_text(screen.row, screen.bscroll, insdel * count, 0);
1152 
1153 /* fill the inserted or new lines with rstyle. TODO: correct for delete? */
1154     if (insdel == DELETE) {
1155         end = screen.bscroll + TermWin.saveLines;
1156     } else if (insdel == INSERT) {
1157         end = screen.row + count - 1 + TermWin.saveLines;
1158     }
1159     for (; count--; end--) {
1160         blank_screen_mem(screen.text, screen.rend, end, rstyle);
1161     }
1162 }
1163 
1164 /*
1165  * Insert/Delete <count> characters from the current position
1166  */
1167 void
scr_insdel_chars(int count,int insdel)1168 scr_insdel_chars(int count, int insdel)
1169 {
1170     int col, row;
1171 
1172     ZERO_SCROLLBACK;
1173     RESET_CHSTAT;
1174 
1175     if (count <= 0)
1176         return;
1177 
1178     CHECK_SELECTION;
1179     UPPER_BOUND(count, (TERM_WINDOW_GET_REPORTED_COLS() - screen.col));
1180 
1181     row = screen.row + TermWin.saveLines;
1182     screen.flags &= ~Screen_WrapNext;
1183 
1184     switch (insdel) {
1185         case INSERT:
1186             for (col = TERM_WINDOW_GET_REPORTED_COLS() - 1; (col - count) >= screen.col; col--) {
1187                 screen.text[row][col] = screen.text[row][col - count];
1188                 screen.rend[row][col] = screen.rend[row][col - count];
1189             }
1190             screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()] += count;
1191             UPPER_BOUND(screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()], TERM_WINDOW_GET_REPORTED_COLS());
1192             /* FALLTHROUGH */
1193         case ERASE:
1194             blank_line(&(screen.text[row][screen.col]), &(screen.rend[row][screen.col]), count, rstyle);
1195             break;
1196         case DELETE:
1197             for (col = screen.col; (col + count) < TERM_WINDOW_GET_REPORTED_COLS(); col++) {
1198                 screen.text[row][col] = screen.text[row][col + count];
1199                 screen.rend[row][col] = screen.rend[row][col + count];
1200             }
1201             blank_line(&(screen.text[row][TERM_WINDOW_GET_REPORTED_COLS() - count]),
1202                        &(screen.rend[row][TERM_WINDOW_GET_REPORTED_COLS() - count]), count, rstyle);
1203             screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()] -= count;
1204             if (((signed char) screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()]) < 0)
1205                 screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()] = 0;
1206             break;
1207     }
1208 #ifdef MULTI_CHARSET
1209     if ((screen.rend[row][0] & RS_multiMask) == RS_multi2) {
1210         screen.rend[row][0] &= ~RS_multiMask;
1211         screen.text[row][0] = ' ';
1212     }
1213     if ((screen.rend[row][TERM_WINDOW_GET_REPORTED_COLS() - 1] & RS_multiMask) == RS_multi1) {
1214         screen.rend[row][TERM_WINDOW_GET_REPORTED_COLS() - 1] &= ~RS_multiMask;
1215         screen.text[row][TERM_WINDOW_GET_REPORTED_COLS() - 1] = ' ';
1216     }
1217 #endif
1218 }
1219 
1220 /*
1221  * Set the scrolling region
1222  * XTERM_SEQ: Set region <top> - <bot> inclusive: ESC [ <top> ; <bot> r
1223  */
1224 void
scr_scroll_region(int top,int bot)1225 scr_scroll_region(int top, int bot)
1226 {
1227     LOWER_BOUND(top, 0);
1228     UPPER_BOUND(bot, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
1229     if (top > bot)
1230         return;
1231     screen.tscroll = top;
1232     screen.bscroll = bot;
1233     scr_gotorc(0, 0, 0);
1234 }
1235 
1236 /*
1237  * Make the cursor visible/invisible
1238  * XTERM_SEQ: Make cursor visible  : ESC [ ? 25 h
1239  * XTERM_SEQ: Make cursor invisible: ESC [ ? 25 l
1240  */
1241 void
scr_cursor_visible(int mode)1242 scr_cursor_visible(int mode)
1243 {
1244     if (mode)
1245         screen.flags |= Screen_VisibleCursor;
1246     else
1247         screen.flags &= ~Screen_VisibleCursor;
1248 }
1249 
1250 /*
1251  * Set/unset automatic wrapping
1252  * XTERM_SEQ: Set Wraparound  : ESC [ ? 7 h
1253  * XTERM_SEQ: Unset Wraparound: ESC [ ? 7 l
1254  */
1255 void
scr_autowrap(int mode)1256 scr_autowrap(int mode)
1257 {
1258     if (mode)
1259         screen.flags |= Screen_Autowrap;
1260     else
1261         screen.flags &= ~Screen_Autowrap;
1262 }
1263 
1264 /*
1265  * Set/unset margin origin mode
1266  * Absolute mode: line numbers are counted relative to top margin of screen
1267  *      and the cursor can be moved outside the scrolling region.
1268  * Relative mode: line numbers are relative to top margin of scrolling region
1269  *      and the cursor cannot be moved outside.
1270  * XTERM_SEQ: Set Absolute: ESC [ ? 6 h
1271  * XTERM_SEQ: Set Relative: ESC [ ? 6 l
1272  */
1273 void
scr_relative_origin(int mode)1274 scr_relative_origin(int mode)
1275 {
1276     if (mode)
1277         screen.flags |= Screen_Relative;
1278     else
1279         screen.flags &= ~Screen_Relative;
1280     scr_gotorc(0, 0, 0);
1281 }
1282 
1283 /*
1284  * Set insert/replace mode
1285  * XTERM_SEQ: Set Insert mode : ESC [ ? 4 h
1286  * XTERM_SEQ: Set Replace mode: ESC [ ? 4 l
1287  */
1288 void
scr_insert_mode(int mode)1289 scr_insert_mode(int mode)
1290 {
1291     if (mode)
1292         screen.flags |= Screen_Insert;
1293     else
1294         screen.flags &= ~Screen_Insert;
1295 }
1296 
1297 /*
1298  * Set/Unset tabs
1299  * XTERM_SEQ: Set tab at current column  : ESC H
1300  * XTERM_SEQ: Clear tab at current column: ESC [ 0 g
1301  * XTERM_SEQ: Clear all tabs             : ESC [ 3 g
1302  */
1303 void
scr_set_tab(int mode)1304 scr_set_tab(int mode)
1305 {
1306     if (mode < 0)
1307         MEMSET(tabs, 0, (unsigned int) TERM_WINDOW_GET_REPORTED_COLS());
1308 
1309     else if (screen.col < TERM_WINDOW_GET_REPORTED_COLS())
1310         tabs[screen.col] = (mode ? 1 : 0);
1311 }
1312 
1313 /*
1314  * Set reverse/normal video
1315  * XTERM_SEQ: Reverse video: ESC [ ? 5 h
1316  * XTERM_SEQ: Normal video : ESC [ ? 5 l
1317  */
1318 void
scr_rvideo_mode(int mode)1319 scr_rvideo_mode(int mode)
1320 {
1321     int i, j, maxlines;
1322 
1323     if (rvideo != mode) {
1324         rvideo = mode;
1325         rstyle ^= RS_RVid;
1326 
1327         maxlines = TermWin.saveLines + TERM_WINDOW_GET_REPORTED_ROWS();
1328         for (i = TermWin.saveLines; i < maxlines; i++)
1329             for (j = 0; j < TERM_WINDOW_GET_REPORTED_COLS(); j++)
1330                 screen.rend[i][j] ^= RS_RVid;
1331         scr_refresh(SLOW_REFRESH);
1332     }
1333 }
1334 
1335 /*
1336  * Report current cursor position
1337  * XTERM_SEQ: Report position: ESC [ 6 n
1338  */
1339 void
scr_report_position(void)1340 scr_report_position(void)
1341 {
1342     tt_printf((unsigned char *) "\033[%d;%dR", screen.row + 1, screen.col + 1);
1343 }
1344 
1345 /* Set font style */
1346 void
set_font_style(void)1347 set_font_style(void)
1348 {
1349     rstyle &= ~RS_fontMask;
1350     switch (charsets[screen.charset]) {
1351         case '0':              /* DEC Special Character & Line Drawing Set */
1352             rstyle |= RS_acsFont;
1353             break;
1354         case 'A':              /* United Kingdom (UK) */
1355             rstyle |= RS_ukFont;
1356             break;
1357         case 'B':              /* United States (USASCII) */
1358             break;
1359         case '<':              /* Multinational character set */
1360             break;
1361         case '5':              /* Finnish character set */
1362             break;
1363         case 'C':              /* Finnish character set */
1364             break;
1365         case 'K':              /* German character set */
1366             break;
1367     }
1368 }
1369 
1370 /*
1371  * Choose a font
1372  * XTERM_SEQ: Invoke G0 character set: CTRL-O
1373  * XTERM_SEQ: Invoke G1 character set: CTRL-N
1374  * XTERM_SEQ: Invoke G2 character set: ESC N
1375  * XTERM_SEQ: Invoke G3 character set: ESC O
1376  */
1377 void
scr_charset_choose(int set)1378 scr_charset_choose(int set)
1379 {
1380     screen.charset = set;
1381     set_font_style();
1382 }
1383 
1384 /*
1385  * Set a font
1386  * XTERM_SEQ: Set G0 character set: ESC ( <C>
1387  * XTERM_SEQ: Set G1 character set: ESC ) <C>
1388  * XTERM_SEQ: Set G2 character set: ESC * <C>
1389  * XTERM_SEQ: Set G3 character set: ESC + <C>
1390  * See set_font_style for possible values for <C>
1391  */
1392 void
scr_charset_set(int set,unsigned int ch)1393 scr_charset_set(int set, unsigned int ch)
1394 {
1395 #ifdef MULTI_CHARSET
1396     multi_byte = (set < 0);
1397     set = abs(set);
1398 #endif
1399     charsets[set] = (unsigned char) ch;
1400     set_font_style();
1401 }
1402 
1403 #ifdef MULTI_CHARSET
1404 
1405 static void latin1(unsigned char *str, int len);
1406 static void eucj2jis(unsigned char *str, int len);
1407 static void sjis2jis(unsigned char *str, int len);
1408 static void big5dummy(unsigned char *str, int len);
1409 
1410 static void (*multichar_decode) (unsigned char *str, int len) = latin1;
1411 
1412 static void
latin1(unsigned char * str,int len)1413 latin1(unsigned char *str, int len)
1414 {
1415     return;
1416     str = NULL;
1417     len = 0;
1418 }
1419 
1420 static void
eucj2jis(unsigned char * str,int len)1421 eucj2jis(unsigned char *str, int len)
1422 {
1423     register int i;
1424 
1425     for (i = 0; i < len; i++)
1426         str[i] &= 0x7F;
1427 }
1428 
1429 static void
sjis2jis(unsigned char * str,int len)1430 sjis2jis(unsigned char *str, int len)
1431 {
1432     register int i;
1433     unsigned char *high, *low;
1434 
1435     for (i = 0; i < len; i += 2, str += 2) {
1436         high = str;
1437         low = str + 1;
1438         (*high) -= (*high > 0x9F ? 0xB1 : 0x71);
1439         *high = (*high) * 2 + 1;
1440         if (*low > 0x9E) {
1441             *low -= 0x7E;
1442             (*high)++;
1443         } else {
1444             if (*low > 0x7E)
1445                 (*low)--;
1446             *low -= 0x1F;
1447         }
1448     }
1449 }
1450 
1451 static void
big5dummy(unsigned char * str,int len)1452 big5dummy(unsigned char *str, int len)
1453 {
1454     str = NULL;
1455     len = 0;
1456 }
1457 #endif
1458 
1459 void
set_multichar_encoding(const char * str)1460 set_multichar_encoding(const char *str)
1461 {
1462 #ifdef MULTI_CHARSET
1463     if (str && *str) {
1464         if (!strcasecmp(str, "utf8") || !strcasecmp(str, "ucs2")) {
1465             encoding_method = UCS2;
1466             multichar_decode = latin1;
1467         } else if (!strcasecmp(str, "sjis")) {
1468             encoding_method = SJIS;
1469             multichar_decode = sjis2jis;
1470         } else if (!strcasecmp(str, "eucj") || !strcasecmp(str, "euckr") || !strcasecmp(str, "gb")) {
1471             encoding_method = EUCJ;
1472             multichar_decode = eucj2jis;
1473         } else if (!strcasecmp(str, "big5")) {
1474             encoding_method = BIG5;
1475             multichar_decode = big5dummy;
1476         } else {
1477             encoding_method = LATIN1;
1478             multichar_decode = latin1;
1479         }
1480     }
1481 #else
1482     return;
1483     str = NULL;
1484 #endif /* MULTI_CHARSET */
1485 }
1486 
1487 /* Refresh an area */
1488 void
scr_expose(int x,int y,int width,int height)1489 scr_expose(int x, int y, int width, int height)
1490 {
1491     int i;
1492     register short nc, nr;
1493     row_col_t rect_beg, rect_end;
1494 
1495     REQUIRE(drawn_text != NULL);
1496 
1497     nc = TERM_WINDOW_GET_REPORTED_COLS() - 1;
1498     nr = TERM_WINDOW_GET_ROWS() - 1;
1499 
1500     rect_beg.col = Pixel2Col(x);
1501     BOUND(rect_beg.col, 0, nc);
1502     rect_beg.row = Pixel2Row(y);
1503     BOUND(rect_beg.row, 0, nr);
1504     rect_end.col = Pixel2Width(x + width + TermWin.fwidth - 1);
1505     BOUND(rect_end.col, 0, nc);
1506     rect_end.row = Pixel2Row(y + height + TermWin.fheight - 1);
1507     BOUND(rect_end.row, 0, nr);
1508 
1509     D_SCREEN(("scr_expose(x:%d, y:%d, w:%d, h:%d) area (c:%d,r:%d)-(c:%d,r:%d)\n", x, y, width, height, rect_beg.col, rect_beg.row,
1510               rect_end.col, rect_end.row));
1511 
1512     for (i = rect_beg.row; i <= rect_end.row; i++) {
1513         MEMSET(&(drawn_text[i][rect_beg.col]), 0, rect_end.col - rect_beg.col + 1);
1514     }
1515 }
1516 
1517 /* Move the display so that the line represented by scrollbar value Y is at
1518    the top of the screen */
1519 int
scr_move_to(int y,int len)1520 scr_move_to(int y, int len)
1521 {
1522     int start;
1523 
1524     start = TermWin.view_start;
1525     TermWin.view_start = ((len - y) * (TERM_WINDOW_GET_REPORTED_ROWS() - 1 + TermWin.nscrolled)
1526                           / (len)) - (TERM_WINDOW_GET_REPORTED_ROWS() - 1);
1527     D_SCREEN(("scr_move_to(%d, %d) view_start:%d\n", y, len, TermWin.view_start));
1528 
1529     BOUND(TermWin.view_start, 0, TermWin.nscrolled);
1530 
1531     return (TermWin.view_start - start);
1532 }
1533 
1534 /* Scroll the visible region up/down by <nlines> lines */
1535 int
scr_page(int direction,int nlines)1536 scr_page(int direction, int nlines)
1537 {
1538     int start;
1539 
1540     D_SCREEN(("scr_page(%s, %d) view_start:%d\n", ((direction == UP) ? "UP" : "DN"), nlines, TermWin.view_start));
1541 
1542     start = TermWin.view_start;
1543     BOUND(nlines, 1, TermWin.nscrolled);
1544     TermWin.view_start += ((direction == UP) ? nlines : (-nlines));
1545     BOUND(TermWin.view_start, 0, TermWin.nscrolled);
1546     return (TermWin.view_start - start);
1547 }
1548 
1549 void
scr_bell(void)1550 scr_bell(void)
1551 {
1552     XWMHints *wm_hints;
1553 
1554     if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_URG_ALERT)) {
1555         wm_hints = XGetWMHints(Xdisplay, TermWin.parent);
1556         wm_hints->flags |= XUrgencyHint;
1557         XSetWMHints(Xdisplay, TermWin.parent, wm_hints);
1558         XFree(wm_hints);
1559     }
1560 #ifndef NO_MAPALERT
1561 #ifdef MAPALERT_OPTION
1562     if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_MAP_ALERT))
1563 #endif
1564         XMapWindow(Xdisplay, TermWin.parent);
1565 #endif
1566     if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_VISUAL_BELL)) {
1567         scr_rvideo_mode(!rvideo);
1568         scr_rvideo_mode(!rvideo);
1569     } else if (!SPIF_PTR_ISNULL(rs_beep_command) && (*rs_beep_command)) {
1570         system_no_wait((char *) rs_beep_command);
1571     } else {
1572         XBell(Xdisplay, 0);
1573     }
1574 }
1575 
1576 void
scr_printscreen(int fullhist)1577 scr_printscreen(int fullhist)
1578 {
1579 #ifdef PRINTPIPE
1580     int i, r, nrows, row_offset;
1581     text_t *t;
1582     FILE *fd;
1583 
1584     if (!(fd = popen_printer()))
1585         return;
1586     nrows = TERM_WINDOW_GET_REPORTED_ROWS();
1587     if (fullhist) {
1588         /* Print the entire scrollback buffer.  Always start from the top and go all the way to the bottom. */
1589         nrows += TermWin.nscrolled;
1590         row_offset = TermWin.saveLines - TermWin.nscrolled;
1591     } else {
1592         /* Just print what's on the screen. */
1593         row_offset = TermWin.saveLines - TermWin.view_start;
1594     }
1595 
1596     for (r = 0; r < nrows; r++) {
1597         t = screen.text[r + row_offset];
1598         for (i = TERM_WINDOW_GET_REPORTED_COLS() - 1; i >= 0; i--)
1599             if (!isspace(t[i]))
1600                 break;
1601         fprintf(fd, "%.*s\n", (i + 1), t);
1602     }
1603     pclose_printer(fd);
1604 #endif
1605 }
1606 
1607 #ifdef MULTI_CHARSET
1608 int
scr_multi1(void)1609 scr_multi1(void)
1610 {
1611     rend_t rend;
1612 
1613     rend = screen.rend[screen.row + TermWin.saveLines][screen.col];
1614     return ((rend & RS_multiMask) == RS_multi1);
1615 }
1616 
1617 int
scr_multi2(void)1618 scr_multi2(void)
1619 {
1620     rend_t rend;
1621 
1622     if (screen.col == 0)
1623         return 0;
1624     rend = screen.rend[screen.row + TermWin.saveLines][screen.col - 1];
1625     return ((rend & RS_multiMask) == RS_multi2);
1626 }
1627 #endif /* MULTI_CHARSET */
1628 
1629 /*
1630  * Refresh the screen
1631  * drawn_text/drawn_rend contain the screen information before the update.
1632  * screen.text/screen.rend contain what the screen will change to.
1633  */
1634 
1635 void
scr_refresh(int type)1636 scr_refresh(int type)
1637 {
1638     int i,                      /* tmp                                       */
1639      scrrow,                    /* screen row offset                         */
1640      row_offset,                /* basic offset in screen structure          */
1641      boldlast = 0,              /* last character in some row was bold       */
1642         len, wlen,              /* text length screen/buffer                 */
1643         fprop,                  /* proportional font used                    */
1644         is_cursor,              /* cursor this position                      */
1645         rvid,                   /* reverse video this position               */
1646         fore, back,             /* desired foreground/background             */
1647         wbyte,                  /* we're in multibyte                        */
1648         xpixel,                 /* x offset for start of drawing (font)      */
1649         ypixel;                 /* y offset for start of drawing (font)      */
1650     register int col, row,      /* column/row we're processing               */
1651      rend;                      /* rendition                                 */
1652     static int focus = -1;      /* screen in focus?                          */
1653     long gcmask;                /* Graphics Context mask                     */
1654     unsigned long ltmp;
1655     rend_t rt1, rt2,            /* tmp rend values                           */
1656      lastrend;                  /* rend type of last char in drawing set     */
1657     text_t lasttext;            /* last char being replaced in drawing set   */
1658     rend_t *drp, *srp;          /* drawn-rend-pointer, screen-rend-pointer   */
1659     text_t *dtp, *stp;          /* drawn-text-pointer, screen-text-pointer   */
1660     XGCValues gcvalue;          /* Graphics Context values                   */
1661     char buf[MAX_COLS + 1];
1662     register char *buffer = buf;
1663     Pixmap pmap = images[image_bg].current->pmap->pixmap;
1664     int (*draw_string) (), (*draw_image_string) ();
1665     register int low_x = 99999, low_y = 99999, high_x = 0, high_y = 0;
1666     Drawable draw_buffer;
1667 
1668 #ifndef NO_BOLDFONT
1669     int bfont = 0;              /* we've changed font to bold font           */
1670 #endif
1671 #ifdef OPTIMIZE_HACKS
1672     register int nrows = TERM_WINDOW_GET_ROWS();
1673     register int ncols = TERM_WINDOW_GET_COLS();
1674 #endif
1675     int ascent, descent;
1676 
1677     PROF_INIT(scr_refresh);
1678 
1679     switch (type) {
1680         case NO_REFRESH:
1681             D_SCREEN(("scr_refresh(NO_REFRESH) called.\n"));
1682             break;
1683         case SLOW_REFRESH:
1684             D_SCREEN(("scr_refresh(SLOW_REFRESH) called.\n"));
1685             break;
1686         case FAST_REFRESH:
1687             D_SCREEN(("scr_refresh(FAST_REFRESH) called.\n"));
1688             break;
1689     }
1690     if (type == NO_REFRESH)
1691         return;
1692 
1693     if (buffer_pixmap) {
1694         draw_buffer = buffer_pixmap;
1695     } else {
1696         draw_buffer = TermWin.vt;
1697     }
1698 
1699     row_offset = TermWin.saveLines - TermWin.view_start;
1700     fprop = TermWin.fprop;
1701 
1702     gcvalue.foreground = PixColors[fgColor];
1703     gcvalue.background = PixColors[bgColor];
1704     wbyte = 0;
1705 
1706     XSetFont(Xdisplay, TermWin.gc, TermWin.font->fid);
1707 
1708 #if FIXME_BLOCK
1709     draw_string = XmbDrawString;
1710     draw_image_string = XmbDrawImageString;
1711 #else
1712     draw_string = XDrawString;
1713     draw_image_string = XDrawImageString;
1714 #endif
1715 
1716     BOUND(screen.row, 0, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
1717     BOUND(screen.col, 0, TERM_WINDOW_GET_REPORTED_COLS() - 1);
1718 
1719     row = screen.row + TermWin.saveLines;
1720     col = screen.col;
1721     if (screen.flags & Screen_VisibleCursor) {
1722         screen.rend[row][col] |= RS_Cursor;
1723 #ifdef MULTI_CHARSET
1724         srp = &screen.rend[row][col];
1725         if ((col < ncols - 1) && ((srp[0] & RS_multiMask) == RS_multi1)
1726             && ((srp[1] & RS_multiMask) == RS_multi2)) {
1727             screen.rend[row][col + 1] |= RS_Cursor;
1728         } else if ((col > 0) && ((srp[0] & RS_multiMask) == RS_multi2)
1729                    && ((srp[-1] & RS_multiMask) == RS_multi1)) {
1730             screen.rend[row][col - 1] |= RS_Cursor;
1731         }
1732 #endif
1733         if (focus != TermWin.focus) {
1734             focus = TermWin.focus;
1735             if ((i = screen.row - TermWin.view_start) >= 0) {
1736                 drawn_rend[i][col] = RS_attrMask;
1737 #ifdef MULTI_CHARSET
1738                 if ((col < ncols - 1) && ((srp[1] & RS_multiMask) == RS_multi2)) {
1739                     drawn_rend[i][col + 1] = RS_attrMask;
1740                 } else if ((col > 0) && ((srp[-1] & RS_multiMask) == RS_multi1)) {
1741                     drawn_rend[i][col - 1] = RS_attrMask;
1742                 }
1743 #endif
1744             }
1745         }
1746     }
1747 
1748     for (row = 0; row < nrows; row++) {
1749         scrrow = row + row_offset;
1750         stp = screen.text[scrrow];
1751         srp = screen.rend[scrrow];
1752         dtp = drawn_text[row];
1753         drp = drawn_rend[row];
1754 
1755         for (col = 0; col < ncols; col++) {
1756             if (!refresh_all) {
1757                 /* compare new text with old - if exactly the same then continue */
1758                 rt1 = srp[col];
1759                 rt2 = drp[col];
1760                 if ((stp[col] == dtp[col])      /* must match characters to skip */
1761                     &&((rt1 == rt2)     /* either rendition the same or  */
1762                        ||((stp[col] == ' ')     /* space w/ no bg change */
1763                           &&(GET_BGATTR(rt1) == GET_BGATTR(rt2))))) {
1764 #ifdef MULTI_CHARSET
1765                     /* if first byte is multibyte then compare second bytes */
1766                     if ((rt1 & RS_multiMask) != RS_multi1)
1767                         continue;
1768                     else if (stp[col + 1] == dtp[col + 1]) {
1769                         /* assume no corrupt characters on the screen */
1770                         col++;
1771                         continue;
1772                     }
1773 #else
1774                     continue;
1775 #endif
1776                 }
1777             }
1778             lasttext = dtp[col];
1779             lastrend = drp[col];
1780             /* redraw one or more characters */
1781             dtp[col] = stp[col];
1782             rend = drp[col] = srp[col];
1783 
1784             len = 0;
1785             buffer[len++] = stp[col];
1786             xpixel = Col2Pixel(col);
1787             wlen = 1;
1788 
1789 /*
1790  * Find out the longest string we can write out at once
1791  */
1792             if (fprop == 0) {   /* Fixed width font */
1793 #ifdef MULTI_CHARSET
1794                 if (((rend & RS_multiMask) == RS_multi1) && (col < ncols - 1)
1795                     && ((srp[col + 1]) & RS_multiMask) == RS_multi2) {
1796                     if (!wbyte) {
1797                         wbyte = 1;
1798                         XSetFont(Xdisplay, TermWin.gc, TermWin.mfont->fid);
1799 # if FIXME_BLOCK
1800                         draw_string = XmbDrawString;
1801                         draw_image_string = XmbDrawImageString;
1802 # else
1803                         draw_string = XDrawString16;
1804                         draw_image_string = XDrawImageString16;
1805 # endif
1806                     }
1807                     /* double stepping - we're in Multibyte mode */
1808                     for (; ++col < ncols;) {
1809                         /* XXX: could check sanity on 2nd byte */
1810                         dtp[col] = stp[col];
1811                         drp[col] = srp[col];
1812                         buffer[len++] = stp[col];
1813                         col++;
1814                         if ((col == ncols) || (srp[col] != (unsigned int) rend))
1815                             break;
1816                         if ((stp[col] == dtp[col])
1817                             && (srp[col] == drp[col])
1818                             && (stp[col + 1] == dtp[col + 1]))
1819                             break;
1820                         if (len == MAX_COLS)
1821                             break;
1822                         dtp[col] = stp[col];
1823                         drp[col] = srp[col];
1824                         buffer[len++] = stp[col];
1825                     }
1826                     col--;
1827                     if (buffer[0] & 0x80)
1828                         multichar_decode(buffer, len);
1829                     wlen = len / 2;
1830                 } else {
1831                     if ((rend & RS_multiMask) == RS_multi1) {
1832                         /* XXX : maybe do the same thing for RS_multi2 */
1833                         /* corrupt character - you're outta there */
1834                         rend &= ~RS_multiMask;
1835                         drp[col] = rend;        /* TODO check: may also want */
1836                         dtp[col] = ' '; /* to poke into stp/srp      */
1837                         buffer[0] = ' ';
1838                     }
1839                     if (wbyte) {
1840                         wbyte = 0;
1841                         XSetFont(Xdisplay, TermWin.gc, TermWin.font->fid);
1842 # if FIXME_BLOCK
1843                         draw_string = XmbDrawString;
1844                         draw_image_string = XmbDrawImageString;
1845 # else
1846                         draw_string = XDrawString;
1847                         draw_image_string = XDrawImageString;
1848 # endif
1849                     }
1850 #endif
1851                     /* single stepping - `normal' mode */
1852                     for (; ++col < ncols - 1;) {
1853                         if ((unsigned int) rend != srp[col])
1854                             break;
1855                         if ((stp[col] == dtp[col]) && (srp[col] == drp[col]))
1856                             break;
1857                         if (len == MAX_COLS)
1858                             break;
1859                         lasttext = dtp[col];
1860                         lastrend = drp[col];
1861                         dtp[col] = stp[col];
1862                         drp[col] = srp[col];
1863                         buffer[len++] = stp[col];
1864                     }
1865                     col--;
1866                     wlen = len;
1867 #ifdef MULTI_CHARSET
1868                 }
1869 #endif
1870             }
1871             buffer[len] = '\0';
1872 
1873             /* Determine the attributes for the string */
1874             fore = GET_FGCOLOR(rend);
1875             back = GET_BGCOLOR(rend);
1876             rend = GET_ATTR(rend);
1877             gcmask = 0;
1878             rvid = (rend & RS_RVid) ? 1 : 0;
1879             if (rend & RS_Select)
1880                 rvid = !rvid;
1881             if (rend & RS_Cursor) {
1882                 if (focus) {
1883                     is_cursor = 2;      /* normal cursor */
1884                     rvid = !rvid;
1885                 } else {
1886                     is_cursor = 1;      /* outline cursor */
1887                     rend &= ~RS_Cursor;
1888                 }
1889                 srp[col] &= ~RS_Cursor;
1890             } else
1891                 is_cursor = 0;
1892             switch (rend & RS_fontMask) {
1893                 case RS_acsFont:
1894                     for (i = 0; i < len; i++)
1895                         if (buffer[i] == 0x5f)
1896                             buffer[i] = 0x7f;
1897                         else if (buffer[i] > 0x5f && buffer[i] < 0x7f)
1898                             buffer[i] -= 0x5f;
1899                     break;
1900                 case RS_ukFont:
1901                     for (i = 0; i < len; i++)
1902                         if (buffer[i] == '#')
1903                             buffer[i] = 0x1e;
1904                     break;
1905             }
1906             if (rvid)
1907                 SWAP_IT(fore, back, i);
1908             if (back != bgColor) {
1909                 gcvalue.background = PixColors[back];
1910                 gcmask |= GCBackground;
1911             }
1912             if (fore != fgColor) {
1913                 gcvalue.foreground = PixColors[fore];
1914                 gcmask |= GCForeground;
1915             }
1916 #ifndef NO_BOLDUNDERLINE
1917             else if (rend & RS_Bold) {
1918                 if (PixColors[fore] != PixColors[colorBD]
1919                     && PixColors[back] != PixColors[colorBD]) {
1920                     gcvalue.foreground = PixColors[colorBD];
1921                     gcmask |= GCForeground;
1922                 }
1923             } else if (rend & RS_Uline) {
1924                 if (PixColors[fore] != PixColors[colorUL]
1925                     && PixColors[back] != PixColors[colorUL]) {
1926                     gcvalue.foreground = PixColors[colorUL];
1927                     gcmask |= GCForeground;
1928                 }
1929             }
1930 #endif
1931 #ifndef NO_CURSORCOLOR
1932             if (rend & RS_Cursor) {
1933                 if (PixColors[cursorColor] != PixColors[bgColor]) {
1934                     gcvalue.background = PixColors[cursorColor];
1935                     back = cursorColor;
1936                     gcmask |= GCBackground;
1937                 }
1938                 if (PixColors[cursorColor2] != PixColors[fgColor]) {
1939                     gcvalue.foreground = PixColors[cursorColor2];
1940                     gcmask |= GCForeground;
1941                 }
1942             }
1943 #endif
1944             if (gcmask) {
1945                 XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
1946             }
1947 #ifndef NO_BOLDFONT
1948             if (!wbyte && MONO_BOLD(rend) && TermWin.boldFont) {
1949                 XSetFont(Xdisplay, TermWin.gc, TermWin.boldFont->fid);
1950                 bfont = 1;
1951             } else if (bfont) {
1952                 bfont = 0;
1953                 XSetFont(Xdisplay, TermWin.gc, TermWin.font->fid);
1954             }
1955 #endif
1956 
1957 #ifdef MULTI_CHARSET
1958             ascent = MAX((encoding_method == LATIN1 ? 0 : TermWin.mfont->ascent), TermWin.font->ascent);
1959             descent = MAX((encoding_method == LATIN1 ? 0 : TermWin.mfont->descent), TermWin.font->descent);
1960 #else
1961             ascent = TermWin.font->ascent;
1962             descent = TermWin.font->descent;
1963 #endif
1964             ypixel = ascent + Row2Pixel(row);
1965 
1966 
1967             /* The actual drawing of the string is done here. */
1968             if (fprop) {
1969                 if (back != bgColor) {
1970                     SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
1971                     gcmask |= (GCForeground | GCBackground);
1972                     XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
1973                     XFillRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel - ascent, Width2Pixel(1), Height2Pixel(1));
1974                     SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
1975                     XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
1976                 } else {
1977                     CLEAR_CHARS(xpixel, ypixel - ascent, 1);
1978                 }
1979                 if (TermWin.font->per_char) {
1980                     int fw, cw;
1981 
1982                     fw = TermWin.fwidth;
1983                     cw = TermWin.font->per_char[((int) (*buffer))].width;
1984                     if (cw > 0 && cw < TermWin.font->max_bounds.width) {
1985                         if (fw > cw) {
1986                             xpixel += ((fw - cw) >> 1);
1987                         } else {
1988                             xpixel -= ((cw - fw) >> 1);
1989                             if (col < ncols - 1) {
1990                                 dtp[col + 1] = 0;
1991                             }
1992                         }
1993                     }
1994                 }
1995                 DRAW_STRING(draw_string, xpixel, ypixel, buffer, 1);
1996                 UPDATE_BOX(xpixel, ypixel - ascent, xpixel + Width2Pixel(1), ypixel + Height2Pixel(1));
1997                 if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_OVERSTRIKE_BOLD) && MONO_BOLD(rend)) {
1998                     DRAW_STRING(draw_string, xpixel + 1, ypixel, buffer, 1);
1999                     UPDATE_BOX(xpixel + 1, ypixel - ascent, xpixel + 1 + Width2Pixel(1), ypixel + Height2Pixel(1));
2000                 }
2001             } else {
2002 #ifdef PIXMAP_SUPPORT
2003                 if (background_is_pixmap() && (back == bgColor)) {
2004                     if (fshadow.do_shadow) {
2005                         Pixel tmp;
2006                         int xx, yy, ww, hh;
2007 
2008                         tmp = gcvalue.foreground;
2009                         xx = xpixel;
2010                         yy = ypixel - ascent;
2011                         ww = Width2Pixel(wlen);
2012                         hh = Height2Pixel(1);
2013                         CLEAR_CHARS(xpixel, ypixel - ascent, len);
2014                         if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_TOP] || fshadow.shadow[SHADOW_TOP_RIGHT]) {
2015                             yy--;
2016                             hh++;
2017                         }
2018                         if (fshadow.shadow[SHADOW_BOTTOM_LEFT] || fshadow.shadow[SHADOW_BOTTOM] || fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
2019                             hh++;
2020                             if (row < nrows - 1) {
2021                                 int ii;
2022 
2023                                 for (ii = col - len + 1; ii <= col; ii++) {
2024                                     drawn_text[row + 1][ii] = 0;
2025                                 }
2026                             }
2027                         }
2028                         if (fshadow.shadow[SHADOW_TOP_LEFT]) {
2029                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_TOP_LEFT]);
2030                             DRAW_STRING(draw_string, xpixel - 1, ypixel - 1, buffer, wlen);
2031                             if (col) {
2032                                 dtp[col - 1] = 0;
2033                             }
2034                         }
2035                         if (fshadow.shadow[SHADOW_TOP]) {
2036                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_TOP]);
2037                             DRAW_STRING(draw_string, xpixel, ypixel - 1, buffer, wlen);
2038                             if (col) {
2039                                 dtp[col] = 0;
2040                             }
2041                         }
2042                         if (fshadow.shadow[SHADOW_TOP_RIGHT]) {
2043                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_TOP_RIGHT]);
2044                             DRAW_STRING(draw_string, xpixel + 1, ypixel - 1, buffer, wlen);
2045                             if (col < ncols - 1) {
2046                                 dtp[col + 1] = 0;
2047                             }
2048                         }
2049                         if (fshadow.shadow[SHADOW_LEFT]) {
2050                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_LEFT]);
2051                             DRAW_STRING(draw_string, xpixel - 1, ypixel, buffer, wlen);
2052                             if (col) {
2053                                 dtp[col - 1] = 0;
2054                             }
2055                         }
2056                         if (fshadow.shadow[SHADOW_RIGHT]) {
2057                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_RIGHT]);
2058                             DRAW_STRING(draw_string, xpixel + 1, ypixel, buffer, wlen);
2059                             if (col < ncols - 1) {
2060                                 dtp[col + 1] = 0;
2061                             }
2062                         }
2063                         if (fshadow.shadow[SHADOW_BOTTOM_LEFT]) {
2064                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_BOTTOM_LEFT]);
2065                             DRAW_STRING(draw_string, xpixel - 1, ypixel + 1, buffer, wlen);
2066                             if (col) {
2067                                 dtp[col - 1] = 0;
2068                             }
2069                         }
2070                         if (fshadow.shadow[SHADOW_BOTTOM]) {
2071                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_BOTTOM]);
2072                             DRAW_STRING(draw_string, xpixel, ypixel + 1, buffer, wlen);
2073                             if (col) {
2074                                 dtp[col] = 0;
2075                             }
2076                         }
2077                         if (fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
2078                             XSetForeground(Xdisplay, TermWin.gc, fshadow.color[SHADOW_BOTTOM_RIGHT]);
2079                             DRAW_STRING(draw_string, xpixel + 1, ypixel + 1, buffer, wlen);
2080                             if (col < ncols - 1) {
2081                                 dtp[col + 1] = 0;
2082                             }
2083                         }
2084                         XSetForeground(Xdisplay, TermWin.gc, tmp);
2085                         DRAW_STRING(draw_string, xpixel, ypixel, buffer, wlen);
2086                         UPDATE_BOX(xx, yy, xx + ww, yy + hh);
2087                     } else {
2088                         CLEAR_CHARS(xpixel, ypixel - ascent, len);
2089                         DRAW_STRING(draw_string, xpixel, ypixel, buffer, wlen);
2090                         UPDATE_BOX(xpixel, ypixel - ascent, xpixel + Width2Pixel(wlen), ypixel + Height2Pixel(1));
2091                     }
2092                 } else
2093 #endif
2094                 {
2095 #ifdef FORCE_CLEAR_CHARS
2096                     CLEAR_CHARS(xpixel, ypixel - ascent, len);
2097 #endif
2098                     DRAW_STRING(draw_image_string, xpixel, ypixel, buffer, wlen);
2099 #ifdef MULTI_CHARSET
2100                     {
2101                         XFontStruct *font = wbyte ? TermWin.mfont : TermWin.font;
2102 
2103                         if (font->ascent < ascent || font->descent < descent) {
2104                             SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
2105                             gcmask |= (GCForeground | GCBackground);
2106                             XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
2107                             if (font->ascent < ascent) {
2108                                 XFillRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel, Row2Pixel(row), Width2Pixel(len),
2109                                                ascent - font->ascent);
2110                             }
2111                             if (font->descent < descent) {
2112                                 XFillRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel, Row2Pixel(row) + ascent + font->descent,
2113                                                Width2Pixel(len), descent - font->descent);
2114                             }
2115                             SWAP_IT(gcvalue.foreground, gcvalue.background, ltmp);
2116                             XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
2117                         }
2118                     }
2119 #endif
2120                     UPDATE_BOX(xpixel, ypixel - ascent, xpixel + Width2Pixel(wlen), ypixel + Height2Pixel(1));
2121                 }
2122             }
2123 
2124             /* do the convoluted bold overstrike */
2125             if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_OVERSTRIKE_BOLD) && MONO_BOLD(rend)) {
2126                 DRAW_STRING(draw_string, xpixel + 1, ypixel, buffer, wlen);
2127                 UPDATE_BOX(xpixel + 1, ypixel - ascent, xpixel + 1 + Width2Pixel(wlen), ypixel + Height2Pixel(1));
2128             }
2129 
2130             if (rend & RS_Uline) {
2131                 if (descent > 1) {
2132                     XDrawLine(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel + 1, xpixel + Width2Pixel(wlen) - 1, ypixel + 1);
2133                     UPDATE_BOX(xpixel, ypixel + 1, xpixel + Width2Pixel(wlen) - 1, ypixel + 1);
2134                 } else {
2135                     XDrawLine(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel - 1, xpixel + Width2Pixel(wlen) - 1, ypixel - 1);
2136                     UPDATE_BOX(xpixel, ypixel - 1, xpixel + Width2Pixel(wlen) - 1, ypixel - 1);
2137                 }
2138             }
2139             if (rend & RS_Overscore) {
2140                 if (ascent > 1) {
2141                     XDrawLine(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel - ascent, xpixel + Width2Pixel(wlen) - 1,
2142                               ypixel - ascent);
2143                     UPDATE_BOX(xpixel, ypixel + 1, xpixel + Width2Pixel(wlen) - 1, ypixel + 1);
2144                 } else {
2145                     XDrawLine(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel - 1, xpixel + Width2Pixel(wlen) - 1, ypixel - 1);
2146                     UPDATE_BOX(xpixel, ypixel - 1, xpixel + Width2Pixel(wlen) - 1, ypixel - 1);
2147                 }
2148             }
2149             if (is_cursor == 1) {
2150 #ifndef NO_CURSORCOLOR
2151                 if (PixColors[cursorColor] != PixColors[bgColor]) {
2152                     XSetForeground(Xdisplay, TermWin.gc, PixColors[cursorColor]);
2153                 }
2154 #endif
2155                 XDrawRectangle(Xdisplay, draw_buffer, TermWin.gc, xpixel, ypixel - ascent, Width2Pixel(1 + wbyte) - 1,
2156                                Height2Pixel(1) - 1);
2157                 UPDATE_BOX(xpixel, ypixel - ascent, Width2Pixel(1 + wbyte) - 1, Height2Pixel(1) - 1);
2158                 XSetForeground(Xdisplay, TermWin.gc, PixColors[fgColor]);
2159             }
2160             if (gcmask) {       /* restore normal colors */
2161                 gcvalue.foreground = PixColors[fgColor];
2162                 gcvalue.background = PixColors[bgColor];
2163                 XChangeGC(Xdisplay, TermWin.gc, gcmask, &gcvalue);
2164             }
2165             if (MONO_BOLD(lastrend)) {
2166                 if (col < ncols - 1) {
2167                     dtp[col + 1] = 0;
2168                 } else {
2169                     boldlast = 1;
2170                 }
2171             }
2172         }                       /* for (col = 0; col < TERM_WINDOW_GET_REPORTED_COLS(); col++) */
2173     }                           /* for (row = 0; row < TERM_WINDOW_GET_REPORTED_ROWS(); row++) */
2174 
2175     row = screen.row + TermWin.saveLines;
2176     col = screen.col;
2177     if (screen.flags & Screen_VisibleCursor) {
2178         screen.rend[row][col] &= ~RS_Cursor;
2179 #ifdef MULTI_CHARSET
2180         /* very low overhead so don't check properly, just wipe it all out */
2181         if (screen.col < ncols - 1)
2182             screen.rend[row][col + 1] &= ~RS_Cursor;
2183         if (screen.col > 0)
2184             screen.rend[row][col - 1] &= ~RS_Cursor;
2185 #endif
2186     }
2187     if (buffer_pixmap) {
2188         D_SCREEN(("Update box dimensions:  from (%d, %d) to (%d, %d).  Dimensions %dx%d\n", low_x, low_y, high_x, high_y,
2189                   high_x - low_x + 1, high_y - low_y + 1));
2190         XClearArea(Xdisplay, TermWin.vt, low_x, low_y, high_x - low_x + 1, high_y - low_y + 1, False);
2191         if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_LEFT] || fshadow.shadow[SHADOW_BOTTOM_LEFT]) {
2192             XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, TermWin.internalBorder - 1, 0, 1, TermWin_TotalHeight() - 1,
2193                       TermWin.internalBorder - 1, 0);
2194             XClearArea(Xdisplay, TermWin.vt, TermWin.internalBorder - 1, 0, 1, TermWin_TotalHeight() - 1, False);
2195         }
2196         if (fshadow.shadow[SHADOW_TOP_RIGHT] || fshadow.shadow[SHADOW_RIGHT] || fshadow.shadow[SHADOW_BOTTOM_RIGHT] || boldlast) {
2197             XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, TermWin_TotalWidth() - 2, 0, 1, TermWin_TotalHeight() - 1,
2198                       TermWin_TotalWidth() - 2, 0);
2199             XClearArea(Xdisplay, TermWin.vt, TermWin_TotalWidth() - 2, 0, 1, TermWin_TotalHeight() - 1, False);
2200         }
2201         if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_TOP] || fshadow.shadow[SHADOW_TOP_RIGHT]) {
2202             XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, 0, TermWin.internalBorder - 1, TermWin_TotalWidth() - 1, 1, 0,
2203                       TermWin.internalBorder - 1);
2204             XClearArea(Xdisplay, TermWin.vt, 0, TermWin.internalBorder - 1, TermWin_TotalWidth() - 1, 1, False);
2205         }
2206         if (fshadow.shadow[SHADOW_BOTTOM_LEFT] || fshadow.shadow[SHADOW_BOTTOM] || fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
2207             XCopyArea(Xdisplay, pmap, buffer_pixmap, TermWin.gc, 0, TermWin_TotalHeight() - TermWin.internalBorder,
2208                       TermWin_TotalWidth() - 1, 1, 0, TermWin_TotalHeight() - TermWin.internalBorder);
2209             XClearArea(Xdisplay, TermWin.vt, 0, TermWin_TotalHeight() - TermWin.internalBorder, TermWin_TotalWidth() - 1, 1, False);
2210         }
2211     } else {
2212         if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_LEFT] || fshadow.shadow[SHADOW_BOTTOM_LEFT]) {
2213             XClearArea(Xdisplay, TermWin.vt, TermWin.internalBorder - 1, 0, 1, TermWin_TotalHeight() - 1, False);
2214         }
2215         if ((fshadow.shadow[SHADOW_TOP_RIGHT] || fshadow.shadow[SHADOW_RIGHT] || fshadow.shadow[SHADOW_BOTTOM_RIGHT] || boldlast) && TermWin.internalBorder) {
2216             XClearArea(Xdisplay, TermWin.vt, TermWin_TotalWidth() - 2, 0, 1, TermWin_TotalHeight() - 1, False);
2217         }
2218         if (fshadow.shadow[SHADOW_TOP_LEFT] || fshadow.shadow[SHADOW_TOP] || fshadow.shadow[SHADOW_TOP_RIGHT]) {
2219             XClearArea(Xdisplay, TermWin.vt, 0, TermWin.internalBorder - 1, TermWin_TotalWidth() - 1, 1, False);
2220         }
2221         if (fshadow.shadow[SHADOW_BOTTOM_LEFT] || fshadow.shadow[SHADOW_BOTTOM] || fshadow.shadow[SHADOW_BOTTOM_RIGHT]) {
2222             XClearArea(Xdisplay, TermWin.vt, 0, TermWin_TotalHeight() - TermWin.internalBorder, TermWin_TotalWidth() - 1, 1, False);
2223         }
2224     }
2225     if (type == SLOW_REFRESH) {
2226         XSync(Xdisplay, False);
2227     }
2228     refresh_all = 0;
2229     D_SCREEN(("Exiting.\n"));
2230 
2231     PROF_DONE(scr_refresh);
2232     PROF_TIME(scr_refresh);
2233 }
2234 
2235 int
scr_strmatch(unsigned long row,unsigned long col,const char * str)2236 scr_strmatch(unsigned long row, unsigned long col, const char *str)
2237 {
2238     unsigned char c;
2239     const char *s;
2240 
2241     for (c = screen.text[row][col], s = str; s; s++) {
2242         if (c != *s) {
2243             return (0);
2244         }
2245     }
2246     return 1;
2247 }
2248 
2249 /* Find and highlight all occurances of "str" in the scrollback. */
2250 void
scr_search_scrollback(char * str)2251 scr_search_scrollback(char *str)
2252 {
2253     unsigned char *c;
2254     char *s;
2255     static char *last_str = NULL;
2256     unsigned int *i;
2257     unsigned long row, lrow, col, rows, cols, len, k;
2258 
2259     if (!str) {
2260         if (!(str = last_str)) {
2261             return;
2262         }
2263     } else {
2264         last_str = STRDUP(str);
2265     }
2266     lrow = rows = TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines;
2267     cols = TERM_WINDOW_GET_REPORTED_COLS();
2268     len = strlen(str);
2269 
2270     D_SCREEN(("%d, %d\n", rows, cols));
2271     for (row = 0; row < rows; row++) {
2272         if (screen.text[row]) {
2273             c = screen.text[row];
2274             for (s = strstr(c, str); s; s = strstr(s + 1, str)) {
2275                 unsigned long j;
2276 
2277                 col = (long) s - (long) c;
2278                 for (i = screen.rend[row] + col, j = 0; j < len; i++, j++) {
2279                     if (*i & RS_RVid) {
2280                         *i &= ~RS_RVid;
2281                     } else {
2282                         *i |= RS_RVid;
2283                     }
2284                 }
2285                 if ((long) row <= TermWin.saveLines) {
2286                     lrow = row;
2287                 }
2288             }
2289             for (s = screen.text[row] + cols - len + 1, k = len - 1; k; s++, k--) {
2290                 unsigned long j;
2291 
2292                 if ((row < rows - 1) && !strncasecmp(s, str, k) && screen.text[row + 1]
2293                     && !strncasecmp(screen.text[row + 1], str + k, len - k)) {
2294                     col = (long) s - (long) c;
2295                     for (i = &(screen.rend[row][cols - k]), j = 0; j < k; i++, j++) {
2296                         (*i & RS_RVid) ? (*i &= ~RS_RVid) : (*i |= RS_RVid);
2297                     }
2298                     for (i = screen.rend[row + 1], j = 0, k = len - k; j < k; i++, j++) {
2299                         (*i & RS_RVid) ? (*i &= ~RS_RVid) : (*i |= RS_RVid);
2300                     }
2301                     if ((long) row <= TermWin.saveLines) {
2302                         lrow = row;
2303                     }
2304                     break;
2305                 }
2306             }
2307         }
2308     }
2309     if (last_str == str) {
2310         FREE(last_str);
2311     } else {
2312         if (lrow != rows) {
2313             TermWin.view_start = rows - lrow - TERM_WINDOW_GET_REPORTED_ROWS();
2314             BOUND(TermWin.view_start, 0, TermWin.nscrolled);
2315             D_SCREEN(("New view start is %d\n", TermWin.view_start));
2316         }
2317     }
2318     scr_refresh(refresh_type);
2319 }
2320 
2321 /* Dump the entire contents of the scrollback buffer to stderr in hex and ASCII */
2322 void
scr_dump(void)2323 scr_dump(void)
2324 {
2325     unsigned char *c;
2326     unsigned int *i;
2327     unsigned long row, col, rows, cols;
2328 
2329     rows = TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines;
2330     cols = TERM_WINDOW_GET_REPORTED_COLS();
2331 
2332     D_SCREEN(("%d, %d\n", rows, cols));
2333     for (row = 0; row < rows; row++) {
2334         fprintf(stderr, "%lu:  ", row);
2335         if (screen.text[row]) {
2336             for (col = 0, c = screen.text[row]; col < cols; c++, col++) {
2337                 fprintf(stderr, "%02x ", *c);
2338             }
2339             fprintf(stderr, "\"");
2340             for (col = 0, c = screen.text[row]; col < cols; c++, col++) {
2341                 fprintf(stderr, "%c", ((isprint(*c)) ? (*c) : '.'));
2342             }
2343             fprintf(stderr, "\"");
2344             for (col = 0, i = screen.rend[row]; col < cols; i++, col++) {
2345                 fprintf(stderr, " %08x", *i);
2346             }
2347         } else {
2348             fprintf(stderr, "NULL");
2349         }
2350         fprintf(stderr, "\n");
2351         fflush(stderr);
2352     }
2353 }
2354 
2355 /* Dump the entire contents of the scrollback buffer to a file */
2356 void
scr_dump_to_file(const char * fname)2357 scr_dump_to_file(const char *fname)
2358 {
2359     int outfd;
2360     char *buff, *src, *dest;
2361     unsigned long row, col, rows, cols;
2362     struct stat st;
2363 
2364     REQUIRE(fname != NULL);
2365 
2366     rows = TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines;
2367     cols = TERM_WINDOW_GET_REPORTED_COLS();
2368     D_SCREEN(("Dumping to file \"%s\".  %d rows, %d cols\n", fname, rows, cols));
2369 
2370     /* Remove it if it's there.  If this fails, we don't
2371        care, because open() will do the right thing. */
2372     if ((stat(fname, &st) == 0) || (errno != ENOENT)) {
2373         D_SCREEN(("Refusing to use log file \"%s\" -- %s\n", fname, (errno ? strerror(errno) : "File exists")));
2374         return;
2375     }
2376 
2377     /* Only open if it's a new file, and open with permissions 0600 */
2378     outfd = open(fname, O_CREAT | O_EXCL | O_NDELAY | O_WRONLY, S_IRUSR | S_IWUSR);
2379     if (outfd < 0) {
2380         D_SCREEN(("Unable to open \"%s\" for writing -- %s\n", fname, strerror(errno)));
2381         return;
2382     }
2383     if (stat(fname, &st) || !S_ISREG(st.st_mode)) {
2384         D_SCREEN(("Race condition exploit attempt detected on \"%s\"!\n", fname));
2385         close(outfd);
2386         return;
2387     }
2388     buff = MALLOC(cols + 1);
2389     for (row = 0; row < rows; row++) {
2390         if (screen.text[row]) {
2391             for (src = screen.text[row], dest = buff, col = 0; col < cols; col++)
2392                 *dest++ = *src++;
2393             *dest++ = '\n';
2394             *dest = 0;
2395             write(outfd, buff, dest - buff);
2396         }
2397     }
2398     close(outfd);
2399     FREE(buff);
2400 }
2401 
2402 /*
2403  * If (row,col) is within a selected region of text, remove the selection
2404  * -TermWin.nscrolled <= (selection row) <= TermWin.nrow - 1
2405  */
2406 void
selection_check(void)2407 selection_check(void)
2408 {
2409     int c1, c2, r1, r2;
2410 
2411     if (current_screen != selection.screen)
2412         return;
2413 
2414     if ((selection.mark.row < -TermWin.nscrolled)
2415         || (selection.mark.row >= TERM_WINDOW_GET_ROWS())
2416         || (selection.beg.row < -TermWin.nscrolled)
2417         || (selection.beg.row >= TERM_WINDOW_GET_ROWS())
2418         || (selection.end.row < -TermWin.nscrolled)
2419         || (selection.end.row >= TERM_WINDOW_GET_ROWS())) {
2420         selection_reset();
2421         return;
2422     }
2423     r1 = (screen.row - TermWin.view_start);
2424 
2425     c1 = ((r1 - selection.mark.row) * (r1 - selection.end.row));
2426 
2427 /*
2428  * selection.mark.row > screen.row - TermWin.view_start
2429  * or
2430  * selection.end.row > screen.row - TermWin.view_start
2431  */
2432     if (c1 < 0)
2433         selection_reset();
2434     else if (c1 == 0) {
2435         if ((selection.mark.row < selection.end.row)
2436             || ((selection.mark.row == selection.end.row)
2437                 && (selection.mark.col < selection.end.col))) {
2438             r1 = selection.mark.row;
2439             c1 = selection.mark.col;
2440             r2 = selection.end.row;
2441             c2 = selection.end.col;
2442         } else {
2443             r1 = selection.end.row;
2444             c1 = selection.end.col;
2445             r2 = selection.mark.row;
2446             c2 = selection.mark.col;
2447         }
2448         if ((screen.row == r1) && (screen.row == r2)) {
2449             if ((screen.col >= c1) && (screen.col <= c2))
2450                 selection_reset();
2451         } else if (((screen.row == r1) && (screen.col >= c1))
2452                    || ((screen.row == r2) && (screen.col <= c2)))
2453             selection_reset();
2454     }
2455 }
2456 
2457 /* Write the selection out to the tty. */
2458 void
selection_write(unsigned char * data,size_t len)2459 selection_write(unsigned char *data, size_t len)
2460 {
2461     size_t num;
2462     unsigned char *p, *cr = "\r";
2463 
2464     D_SELECT(("Writing %lu characters of selection data to tty.\n", len));
2465     D_SELECT(("\n%s\n\n", safe_print_string((char *) data, len)));
2466     for (p = data, num = 0; len--; p++) {
2467         /* Write out each line, replacing newlines with carriage returns. */
2468         if (*p != '\n') {
2469             num++;
2470         } else {
2471             tt_write(data, num);
2472             tt_write(cr, 1);
2473             data += num + 1;
2474             num = 0;
2475         }
2476     }
2477     /* If there's anything left, write it out too. */
2478     if (num) {
2479         tt_write(data, num);
2480     }
2481 }
2482 
2483 /* Fetch the selection from the specified property and write it to the tty. */
2484 void
selection_fetch(Window win,unsigned prop,int delete)2485 selection_fetch(Window win, unsigned prop, int delete)
2486 {
2487     long nread;
2488     unsigned long bytes_after, nitems;
2489     unsigned char *data;
2490     Atom actual_type;
2491     int actual_fmt;
2492 
2493     D_SELECT(("Fetching selection in property %d from window 0x%08x\n", (int) prop, (int) win));
2494     if (prop == None) {
2495         return;
2496     }
2497     for (nread = 0, bytes_after = 1; bytes_after > 0;) {
2498         if ((XGetWindowProperty
2499              (Xdisplay, win, prop, (nread / 4), PROP_SIZE, delete, AnyPropertyType, &actual_type, &actual_fmt, &nitems,
2500               &bytes_after, &data) != Success)
2501             || (actual_type == None) || (!data)) {
2502             D_SELECT(("Unable to fetch the value of property %d from window 0x%08x\n", (int) prop, (int) win));
2503             if (data) {
2504                 XFree(data);
2505             }
2506             return;
2507         }
2508         nread += nitems;
2509         D_SELECT(("Got selection info:  Actual type %d (format %d), %lu items at 0x%08x, %lu bytes left over.\n",
2510                   (int) actual_type, actual_fmt, nitems, data, bytes_after));
2511 
2512         if (nitems == 0) {
2513             D_SELECT(("Retrieval of incremental selection complete.\n"));
2514             TermWin.mask &= ~(PropertyChangeMask);
2515             XSelectInput(Xdisplay, TermWin.vt, TermWin.mask);
2516             return;
2517         }
2518         if (actual_type == XA_STRING) {
2519             /* We can handle strings directly. */
2520             selection_write(data, nitems);
2521         } else if (actual_type == props[PROP_SELECTION_INCR]) {
2522             D_SELECT(("Incremental selection transfer initiated.  Length is at least %u bytes.\n",
2523                       (unsigned) *((unsigned *) data)));
2524             TermWin.mask |= PropertyChangeMask;
2525             XSelectInput(Xdisplay, TermWin.vt, TermWin.mask);
2526         } else {
2527             int size, i;
2528             XTextProperty xtextp;
2529             char **cl = NULL;
2530 
2531             /* It's not a string, so convert it to one (or more). */
2532             D_SELECT(("Selection is not a string.  Converting.\n"));
2533             xtextp.value = data;
2534             xtextp.encoding = actual_type;
2535             xtextp.format = actual_fmt;
2536             xtextp.nitems = nitems;
2537             XmbTextPropertyToTextList(Xdisplay, &xtextp, &cl, &size);
2538 
2539             if (cl) {
2540                 D_SELECT(("Got string list 0x%08x with %d strings.\n", cl, size));
2541                 for (i = 0; i < size; i++) {
2542                     if (cl[i]) {
2543                         selection_write(cl[i], strlen(cl[i]));
2544                     }
2545                 }
2546                 XFreeStringList(cl);
2547             }
2548         }
2549         if (data) {
2550             XFree(data);
2551         }
2552     }
2553 }
2554 
2555 /* Copy a specific string of a given length to the buffer specified. */
2556 void
selection_copy_string(Atom sel,char * str,size_t len)2557 selection_copy_string(Atom sel, char *str, size_t len)
2558 {
2559     D_SELECT(("Copying %ul bytes from 0x%08x to selection %d\n", len, str, (int) sel));
2560     if (!str || len == 0) {
2561         return;
2562     }
2563     if (IS_SELECTION(sel)) {
2564         D_SELECT(("Changing ownership of selection %d to my window 0x%08x\n", (int) sel, (int) TermWin.vt));
2565         XSetSelectionOwner(Xdisplay, sel, TermWin.vt, CurrentTime);
2566         if (XGetSelectionOwner(Xdisplay, sel) != TermWin.vt) {
2567             libast_print_error("Can't take ownership of selection\n");
2568         }
2569     } else {
2570         D_SELECT(("Copying selection to cut buffer %d\n", (int) sel));
2571         XChangeProperty(Xdisplay, Xroot, sel, XA_STRING, 8, PropModeReplace, str, len);
2572     }
2573 }
2574 
2575 /* Copy the currently-selected text to the buffer specified. */
2576 void
selection_copy(Atom sel)2577 selection_copy(Atom sel)
2578 {
2579     selection_copy_string(sel, selection.text, selection.len);
2580 }
2581 
2582 /* Paste the specified selection from the specified buffer. */
2583 void
selection_paste(Atom sel)2584 selection_paste(Atom sel)
2585 {
2586     D_SELECT(("Attempting to paste selection %d.\n", (int) sel));
2587     if (selection.text) {
2588         /* If we have a selection of our own, paste it. */
2589         D_SELECT(("Pasting my current selection of length %lu\n", selection.len));
2590         selection_write(selection.text, selection.len);
2591     } else if (IS_SELECTION(sel)) {
2592         /* Request the current selection be converted to the appropriate
2593            form (usually XA_STRING) and save it for us in the VT_SELECTION
2594            property.  We'll then get a SelectionNotify. */
2595         D_SELECT(("Requesting current selection (%d) -> VT_SELECTION (%d)\n", sel, props[PROP_SELECTION_DEST]));
2596 #if defined(MULTI_CHARSET)
2597         if (encoding_method != LATIN1) {
2598             XConvertSelection(Xdisplay, sel, props[PROP_COMPOUND_TEXT], props[PROP_SELECTION_DEST], TermWin.vt, CurrentTime);
2599         } else {
2600             XConvertSelection(Xdisplay, sel, XA_STRING, props[PROP_SELECTION_DEST], TermWin.vt, CurrentTime);
2601         }
2602 #else
2603         XConvertSelection(Xdisplay, sel, XA_STRING, props[PROP_SELECTION_DEST], TermWin.vt, CurrentTime);
2604 #endif
2605     } else {
2606         D_SELECT(("Pasting cut buffer %d.\n", (int) sel));
2607         selection_fetch(Xroot, sel, False);
2608     }
2609 }
2610 
2611 /* Clear the selected state of all selected text. */
2612 void
selection_reset(void)2613 selection_reset(void)
2614 {
2615     int i, j, lrow, lcol;
2616 
2617     D_SELECT(("selection_reset()\n"));
2618 
2619     lrow = TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines;
2620     lcol = TERM_WINDOW_GET_REPORTED_COLS();
2621     selection.op = SELECTION_CLEAR;
2622 
2623     i = (current_screen == PRIMARY) ? 0 : TermWin.saveLines;
2624     for (; i < lrow; i++) {
2625         if (screen.text[i]) {
2626             for (j = 0; j < lcol; j++) {
2627                 screen.rend[i][j] &= ~RS_Select;
2628             }
2629         }
2630     }
2631 }
2632 
2633 /* Delete the current selection. */
2634 void
selection_clear(void)2635 selection_clear(void)
2636 {
2637     D_SELECT(("selection_clear()\n"));
2638 
2639     if (selection.text) {
2640         FREE(selection.text);
2641     }
2642     selection.len = 0;
2643     selection_reset();
2644 }
2645 
2646 /* Set or clear between selected points (inclusive) */
2647 void
selection_setclr(int set,int startr,int startc,int endr,int endc)2648 selection_setclr(int set, int startr, int startc, int endr, int endc)
2649 {
2650     int row, col, last_col;
2651     rend_t *rend;
2652 
2653     D_SELECT(("selection_setclr(%d) %s (%d,%d)-(%d,%d)\n", set, (set ? "set  " : "clear"), startc, startr, endc, endr));
2654 
2655     if ((startr < -TermWin.nscrolled) || (endr >= TERM_WINDOW_GET_REPORTED_ROWS())) {
2656         selection_reset();
2657         return;
2658     }
2659     last_col = TERM_WINDOW_GET_REPORTED_COLS() - 1;
2660 
2661     LOWER_BOUND(startc, 0);
2662     UPPER_BOUND(endc, last_col);
2663     BOUND(startr, -TermWin.nscrolled, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
2664     BOUND(endr, -TermWin.nscrolled, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
2665 
2666     startr += TermWin.saveLines;
2667     endr += TermWin.saveLines;
2668 
2669     col = startc;
2670     if (set) {
2671         for (row = startr; row < endr; row++) {
2672             rend = &(screen.rend[row][col]);
2673             for (; col <= last_col; col++, rend++)
2674                 *rend |= RS_Select;
2675             col = 0;
2676         }
2677         rend = &(screen.rend[row][col]);
2678         for (; col <= endc; col++, rend++)
2679             *rend |= RS_Select;
2680     } else {
2681         for (row = startr; row < endr; row++) {
2682             rend = &(screen.rend[row][col]);
2683             for (; col <= last_col; col++, rend++)
2684                 *rend &= ~RS_Select;
2685             col = 0;
2686         }
2687         rend = &(screen.rend[row][col]);
2688         for (; col <= endc; col++, rend++)
2689             *rend &= ~RS_Select;
2690     }
2691 }
2692 
2693 /*
2694  * Mark a selection at the specified x/y pixel location
2695  */
2696 void
selection_start(int x,int y)2697 selection_start(int x, int y)
2698 {
2699     D_SELECT(("selection_start(%d, %d)\n", x, y));
2700     selection_start_colrow(Pixel2Col(x), Pixel2Row(y));
2701 }
2702 
2703 /*
2704  * Mark a selection at the specified col/row
2705  */
2706 void
selection_start_colrow(int col,int row)2707 selection_start_colrow(int col, int row)
2708 {
2709     int end_col;
2710 
2711     D_SELECT(("selection_start_colrow(%d, %d)\n", col, row));
2712 
2713     if (selection.op) {
2714         /* clear the old selection */
2715 
2716         if (selection.beg.row < -TermWin.nscrolled)
2717             selection_reset();
2718         else
2719             selection_setclr(0, selection.beg.row, selection.beg.col, selection.end.row, selection.end.col);
2720     }
2721     selection.op = SELECTION_INIT;
2722     BOUND(row, 0, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
2723 
2724     row -= TermWin.view_start;
2725     end_col = screen.text[row + TermWin.saveLines][TERM_WINDOW_GET_REPORTED_COLS()];
2726     if (end_col != WRAP_CHAR && col > end_col)
2727         col = TERM_WINDOW_GET_REPORTED_COLS();
2728     selection.mark.col = col;
2729     selection.mark.row = row;
2730 }
2731 
2732 /*
2733  * Copy a selection into the cut buffer
2734  * EXT: button 1 or 3 release
2735  */
2736 void
selection_make(Time tm)2737 selection_make(Time tm)
2738 {
2739     int i, col, end_col, row, end_row;
2740     text_t *new_selection_text;
2741     char *str;
2742     text_t *t;
2743 
2744     D_SELECT(("selection.op=%d, selection.clicks=%d\n", selection.op, selection.clicks));
2745     switch (selection.op) {
2746         case SELECTION_CONT:
2747             break;
2748         case SELECTION_INIT:
2749             selection_reset();
2750             selection.end.row = selection.beg.row = selection.mark.row;
2751             selection.end.col = selection.beg.col = selection.mark.col;
2752             /* FALLTHROUGH */
2753         case SELECTION_BEGIN:
2754             selection.op = SELECTION_DONE;
2755             /* FALLTHROUGH */
2756         default:
2757             return;
2758     }
2759     selection.op = SELECTION_DONE;
2760 
2761     if (selection.clicks == 4)
2762         return;                 /* nothing selected, go away */
2763 
2764     if (selection.beg.row < -TermWin.nscrolled || selection.end.row >= TERM_WINDOW_GET_REPORTED_ROWS()) {
2765         selection_reset();
2766         return;
2767     }
2768     i = (selection.end.row - selection.beg.row + 1) * (TERM_WINDOW_GET_REPORTED_COLS() + 1) + 1;
2769     str = MALLOC(i * sizeof(char));
2770     new_selection_text = (unsigned char *) str;
2771 
2772     col = MAX(selection.beg.col, 0);
2773     row = selection.beg.row + TermWin.saveLines;
2774     end_row = selection.end.row + TermWin.saveLines;
2775 /*
2776  * A: rows before end row
2777  */
2778     for (; row < end_row; row++) {
2779         t = &(screen.text[row][col]);
2780         if ((end_col = screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()]) == WRAP_CHAR)
2781             end_col = TERM_WINDOW_GET_REPORTED_COLS();
2782         for (; col < end_col; col++)
2783             *str++ = *t++;
2784         col = 0;
2785         if (screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()] != WRAP_CHAR) {
2786             if (!(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SELECT_TRAILING_SPACES))) {
2787                 for (str--; *str == ' ' || *str == '\t'; str--);
2788                 str++;
2789             }
2790             *str++ = '\n';
2791         }
2792     }
2793 /*
2794  * B: end row
2795  */
2796     t = &(screen.text[row][col]);
2797     end_col = screen.text[row][TERM_WINDOW_GET_REPORTED_COLS()];
2798     if (end_col == WRAP_CHAR || selection.end.col <= end_col) {
2799         i = 0;
2800         end_col = selection.end.col + 1;
2801     } else
2802         i = 1;
2803     UPPER_BOUND(end_col, TERM_WINDOW_GET_REPORTED_COLS());
2804     for (; col < end_col; col++)
2805         *str++ = *t++;
2806     if (!(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SELECT_TRAILING_SPACES))) {
2807         for (str--; *str == ' ' || *str == '\t'; str--);
2808         str++;
2809     }
2810     if (i)
2811         *str++ = '\n';
2812     *str = '\0';
2813     if ((i = strlen((char *) new_selection_text)) == 0) {
2814         FREE(new_selection_text);
2815         return;
2816     }
2817     selection.len = i;
2818     if (selection.text)
2819         FREE(selection.text);
2820     selection.text = new_selection_text;
2821     selection.screen = current_screen;
2822 
2823     selection_copy(XA_PRIMARY);
2824     D_SELECT(("selection.len=%d\n", selection.len));
2825     return;
2826     tm = 0;
2827 }
2828 
2829 /*
2830  * Mark or select text based upon number of clicks: 1, 2, or 3
2831  * EXT: button 1 press
2832  */
2833 void
selection_click(int clicks,int x,int y)2834 selection_click(int clicks, int x, int y)
2835 {
2836 
2837 /*
2838  *  int             r, c;
2839  *  row_col_t       ext_beg, ext_end;
2840  */
2841 
2842     D_SELECT(("selection_click(%d, %d, %d)\n", clicks, x, y));
2843 
2844     clicks = ((clicks - 1) % 3) + 1;
2845     selection.clicks = clicks;  /* save clicks so extend will work */
2846 
2847     selection_start(x, y);      /* adjusts for scroll offset */
2848     if (clicks == 2 || clicks == 3)
2849         selection_extend_colrow(selection.mark.col, selection.mark.row + TermWin.view_start, 0, 1);
2850 }
2851 
2852 /*
2853  * Select text for 2 clicks
2854  * row is given as a normal selection row value
2855  * beg.row, end.row are returned as normal selection row values
2856  */
2857 
2858 /* what do we want: spaces/tabs are delimiters or cutchars or non-cutchars */
2859 #ifdef CUTCHAR_OPTION
2860 #  define DELIMIT_TEXT(x) (strchr((rs_cutchars ? rs_cutchars : CUTCHARS), (x)))
2861 #else
2862 #  define DELIMIT_TEXT(x) (strchr(CUTCHARS, (x)))
2863 #endif
2864 #ifdef MULTI_CHARSET
2865 #define DELIMIT_REND(x)	(((x) & RS_multiMask) ? 1 : 0)
2866 #endif
2867 
2868 void
selection_delimit_word(int col,int row,row_col_t * beg,row_col_t * end)2869 selection_delimit_word(int col, int row, row_col_t *beg, row_col_t *end)
2870 {
2871     int beg_col, beg_row, end_col, end_row, last_col;
2872     int row_offset, w1;
2873     text_t *stp, *stp1, t;
2874 
2875 #ifdef MULTI_CHARSET
2876     int w2;
2877     rend_t *srp, r;
2878 
2879 #endif
2880 
2881     if (selection.clicks != 2)  /* We only handle double clicks: go away */
2882         return;
2883 
2884     if (!screen.text || !screen.rend)
2885         return;
2886 
2887     last_col = TERM_WINDOW_GET_REPORTED_COLS() - 1;
2888 
2889     if (row >= TERM_WINDOW_GET_REPORTED_ROWS()) {
2890         row = TERM_WINDOW_GET_REPORTED_ROWS() - 1;
2891         col = last_col;
2892     } else if (row < -TermWin.saveLines) {
2893         row = -TermWin.saveLines;
2894         col = 0;
2895     }
2896     beg_col = end_col = col;
2897     beg_row = end_row = row;
2898 
2899     row_offset = TermWin.saveLines;
2900 
2901 /* A: find the beginning of the word */
2902 
2903     if (!screen.text[beg_row + row_offset] || !screen.rend[beg_row + row_offset])
2904         return;
2905     if (!screen.text[end_row + row_offset] || !screen.rend[end_row + row_offset])
2906         return;
2907 #if 0
2908     if (!screen.text[beg_row + row_offset - 1] || !screen.rend[beg_row + row_offset - 1])
2909         return;
2910     if (!screen.text[end_row + row_offset + 1] || !screen.rend[end_row + row_offset + 1])
2911         return;
2912 #endif
2913 
2914     stp1 = stp = &(screen.text[beg_row + row_offset][beg_col]);
2915     w1 = DELIMIT_TEXT(*stp);
2916     if (w1 == 2)
2917         w1 = 0;
2918 #ifdef MULTI_CHARSET
2919     srp = &(screen.rend[beg_row + row_offset][beg_col]);
2920     w2 = DELIMIT_REND(*srp);
2921 #endif
2922 
2923     for (;;) {
2924         for (; beg_col > 0; beg_col--) {
2925             t = *--stp;
2926             if (DELIMIT_TEXT(t) != w1 || (w1 && *stp1 != t && BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT)))
2927                 break;
2928 #ifdef MULTI_CHARSET
2929             r = *--srp;
2930             if (DELIMIT_REND(r) != w2)
2931                 break;
2932 #endif
2933         }
2934         if (!(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT))) {
2935             if (beg_col == col && beg_col > 0) {
2936                 if (DELIMIT_TEXT(*stp)) /* space or tab or cutchar */
2937                     break;
2938 #ifdef MULTI_CHARSET
2939                 srp = &(screen.rend[beg_row + row_offset][beg_col - 1]);
2940 #endif
2941                 for (; --beg_col > 0;) {
2942                     t = *--stp;
2943                     if (DELIMIT_TEXT(t))
2944                         break;
2945 #ifdef MULTI_CHARSET
2946                     r = *--srp;
2947                     if (DELIMIT_REND(r) != w2)
2948                         break;
2949 #endif
2950                 }
2951             }
2952         }
2953         if (beg_col == 0 && (beg_row > -TermWin.nscrolled)) {
2954             stp = &(screen.text[beg_row + row_offset - 1][last_col + 1]);
2955             if (*stp == WRAP_CHAR) {
2956                 t = *(stp - 1);
2957 #ifdef MULTI_CHARSET
2958                 srp = &(screen.rend[beg_row + row_offset - 1][last_col + 1]);
2959                 r = *(srp - 1);
2960                 if (DELIMIT_TEXT(t) == w1 && (!w1 || *stp == t || !(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT)))
2961                     && DELIMIT_REND(r) == w2) {
2962                     srp--;
2963 #else
2964                 if (DELIMIT_TEXT(t) == w1 && (!w1 || *stp == t || !(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT)))) {
2965 #endif
2966                     stp--;
2967                     beg_row--;
2968                     beg_col = last_col;
2969                     continue;
2970                 }
2971             }
2972         }
2973         break;
2974     }
2975 
2976 /* B: find the end of the word */
2977 
2978 # ifdef OPTIMIZE_HACKS
2979     stp = stp1;
2980 # else
2981     stp1 = stp = &(screen.text[end_row + row_offset][end_col]);
2982 # endif
2983 
2984 #ifdef MULTI_CHARSET
2985     srp = &(screen.rend[end_row + row_offset][end_col]);
2986 #endif
2987     for (;;) {
2988         for (; end_col < last_col; end_col++) {
2989             t = *++stp;
2990             if (DELIMIT_TEXT(t) != w1 || (w1 && *stp1 != t && BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT)))
2991                 break;
2992 #ifdef MULTI_CHARSET
2993             r = *++srp;
2994             if (DELIMIT_REND(r) != w2)
2995                 break;
2996 #endif
2997         }
2998         if (!(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT))) {
2999             if (end_col == col && end_col < last_col) {
3000                 if (DELIMIT_TEXT(*stp)) /* space or tab or cutchar */
3001                     break;
3002 #ifdef MULTI_CHARSET
3003                 srp = &(screen.rend[end_row + row_offset][end_col + 1]);
3004 #endif
3005                 for (; ++end_col < last_col;) {
3006                     t = *++stp;
3007                     if (DELIMIT_TEXT(t))
3008                         break;
3009 #ifdef MULTI_CHARSET
3010                     r = *++srp;
3011                     if (DELIMIT_REND(r) != w2)
3012                         break;
3013 #endif
3014                 }
3015             }
3016         }
3017         if (end_col == last_col && (end_row < (TERM_WINDOW_GET_REPORTED_ROWS() - 1))) {
3018             if (*++stp == WRAP_CHAR) {
3019                 stp = screen.text[end_row + row_offset + 1];
3020 #ifdef MULTI_CHARSET
3021                 srp = screen.rend[end_row + row_offset + 1];
3022                 if (DELIMIT_TEXT(*stp) == w1
3023                     && (!w1 || *stp1 == *stp || !(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT)))
3024                     && DELIMIT_REND(*srp) == w2) {
3025 #else
3026                 if (DELIMIT_TEXT(*stp) == w1
3027                     && (!w1 || *stp1 == *stp || !(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_XTERM_SELECT)))) {
3028 #endif
3029                     end_row++;
3030                     end_col = 0;
3031                     continue;
3032                 }
3033             }
3034         }
3035         break;
3036     }
3037 
3038     D_SELECT(("selection_delimit_word(%d, %d) says (%d,%d)->(%d,%d)\n", col, row, beg_col, beg_row, end_col, end_row));
3039 
3040 /* Poke the values back in */
3041     beg->col = beg_col;
3042     beg->row = beg_row;
3043     end->col = end_col;
3044     end->row = end_row;
3045 }
3046 
3047 /*
3048  * Extend the selection to the specified x/y pixel location
3049  * EXT: button 3 press; button 1 or 3 drag
3050  * flag == 0 ==> button 1
3051  * flag != 0 ==> button 3
3052  */
3053 void
3054 selection_extend(int x, int y, int flag)
3055 {
3056     int col, row;
3057 
3058 /*
3059  * If we're selecting characters (single click) then we must check first
3060  * if we are at the same place as the original mark.  If we are then
3061  * select nothing.  Otherwise, if we're to the right of the mark, you have to
3062  * be _past_ a character for it to be selected.
3063  */
3064     col = Pixel2Col(x);
3065     row = Pixel2Row(y);
3066     BOUND(row, 0, TERM_WINDOW_GET_REPORTED_ROWS() - 1);
3067     if (((selection.clicks % 3) == 1) && !flag && (col == selection.mark.col && (row == selection.mark.row + TermWin.view_start))) {
3068         /* select nothing */
3069         selection_setclr(0, selection.beg.row, selection.beg.col, selection.end.row, selection.end.col);
3070         selection.beg.row = selection.end.row = selection.mark.row;
3071         selection.beg.col = selection.end.col = selection.mark.col;
3072         selection.clicks = 4;
3073         D_SELECT(("selection.clicks = 4\n"));
3074         return;
3075     }
3076     if (selection.clicks == 4)
3077         selection.clicks = 1;
3078     selection_extend_colrow(col, row, flag, 0);
3079 }
3080 
3081 /*
3082  * Extend the selection to the specified col/row
3083  */
3084 void
3085 selection_extend_colrow(int col, int row, int flag, int cont)
3086 {
3087     int old_col, end_col;
3088     row_col_t new_beg1, new_beg2, new_end1, new_end2, old_beg, old_end;
3089     enum {
3090         LEFT, RIGHT
3091     } closeto = RIGHT;
3092 
3093 #ifdef MULTI_CHARSET
3094     int r;
3095 
3096 #endif
3097 
3098     D_SELECT(("selection_extend_colrow(%d, %d, %d, %d) clicks:%d\n", col, row, flag, cont, selection.clicks));
3099 
3100     switch (selection.op) {
3101         case SELECTION_INIT:
3102             selection_reset();
3103             selection.end.col = selection.beg.col = selection.mark.col;
3104             selection.end.row = selection.beg.row = selection.mark.row;
3105             selection.op = SELECTION_BEGIN;
3106             /* FALLTHROUGH */
3107         case SELECTION_BEGIN:
3108             break;
3109         case SELECTION_DONE:
3110             selection.op = SELECTION_CONT;
3111             /* FALLTHROUGH */
3112         case SELECTION_CONT:
3113             break;
3114         case SELECTION_CLEAR:
3115             selection_start_colrow(col, row);
3116             /* FALLTHROUGH */
3117         default:
3118             return;
3119     }
3120     if ((selection.beg.row < -TermWin.nscrolled)
3121         || (selection.end.row < -TermWin.nscrolled)) {
3122         selection_reset();
3123         return;
3124     }
3125     old_col = col;
3126     BOUND(col, -1, TERM_WINDOW_GET_REPORTED_COLS());
3127     old_beg.col = selection.beg.col;
3128     old_beg.row = selection.beg.row;
3129     old_end.col = selection.end.col;
3130     old_end.row = selection.end.row;
3131 
3132     if ((selection.op == SELECTION_BEGIN) && (cont || (row != selection.mark.row || col != selection.mark.col)))
3133         selection.op = SELECTION_CONT;
3134 
3135     row -= TermWin.view_start;  /* adjust for scroll */
3136 
3137     if (flag) {
3138         if (row < selection.beg.row || (row == selection.beg.row && col < selection.beg.col))
3139             closeto = LEFT;
3140         else if (row > selection.end.row || (row == selection.end.row && col >= selection.end.col)) {
3141             /* */ ;
3142         } else if (((col - selection.beg.col)
3143                     + ((row - selection.beg.row) * TERM_WINDOW_GET_REPORTED_COLS()))
3144                    < ((selection.end.col - col)
3145                       + ((selection.end.row - row) * TERM_WINDOW_GET_REPORTED_COLS())))
3146             closeto = LEFT;
3147     }
3148     if (selection.clicks == 1) {
3149 /*
3150  * A1: extension on single click - selection between points
3151  */
3152         if (flag) {             /* button 3 extension */
3153             if (closeto == LEFT) {
3154                 selection.beg.row = row;
3155                 selection.beg.col = col;
3156                 end_col = screen.text[row + TermWin.saveLines][TERM_WINDOW_GET_REPORTED_COLS()];
3157                 if (end_col != WRAP_CHAR && selection.beg.col > end_col) {
3158                     if (selection.beg.row < selection.end.row) {
3159                         selection.beg.col = -1;
3160                         selection.beg.row++;
3161                     } else {
3162                         selection.beg.col = selection.mark.col;
3163                         selection.beg.row = selection.mark.row;
3164                     }
3165                 }
3166             } else {
3167                 selection.end.row = row;
3168                 selection.end.col = col - 1;
3169                 end_col = screen.text[row + TermWin.saveLines][TERM_WINDOW_GET_REPORTED_COLS()];
3170                 if (end_col != WRAP_CHAR && selection.end.col >= end_col)
3171                     selection.end.col = TERM_WINDOW_GET_REPORTED_COLS() - 1;
3172             }
3173         } else if ((row < selection.mark.row)
3174                    || (row == selection.mark.row && col < selection.mark.col)) {
3175             /* select left of mark character excluding mark */
3176             selection.beg.row = row;
3177             selection.beg.col = col;
3178             selection.end.row = selection.mark.row;
3179             selection.end.col = selection.mark.col - 1;
3180             if (selection.end.col >= 0) {
3181                 end_col = screen.text[row + TermWin.saveLines][TERM_WINDOW_GET_REPORTED_COLS()];
3182                 if (end_col != WRAP_CHAR && selection.beg.col > end_col) {
3183                     if (selection.beg.row < selection.end.row) {
3184                         selection.beg.col = -1;
3185                         selection.beg.row++;
3186                     } else {
3187                         selection.beg.col = selection.mark.col;
3188                         selection.beg.row = selection.mark.row;
3189                     }
3190                 }
3191             }
3192         } else {
3193             /* select right of mark character including mark */
3194             selection.beg.row = selection.mark.row;
3195             selection.beg.col = selection.mark.col;
3196             selection.end.row = row;
3197             selection.end.col = col - 1;
3198             if (old_col >= 0) {
3199                 end_col = screen.text[row + TermWin.saveLines][TERM_WINDOW_GET_REPORTED_COLS()];
3200                 if (end_col != WRAP_CHAR && selection.end.col >= end_col)
3201                     selection.end.col = TERM_WINDOW_GET_REPORTED_COLS() - 1;
3202             }
3203         }
3204 #ifdef MULTI_CHARSET
3205         if ((selection.beg.col > 0) && (selection.beg.col < TERM_WINDOW_GET_REPORTED_COLS())) {
3206             r = selection.beg.row + TermWin.saveLines;
3207             if (((screen.rend[r][selection.beg.col] & RS_multiMask) == RS_multi2)
3208                 && ((screen.rend[r][selection.beg.col - 1] & RS_multiMask) == RS_multi1))
3209                 selection.beg.col--;
3210         }
3211         if ((selection.end.col > 0) && (selection.end.col < (TERM_WINDOW_GET_REPORTED_COLS() - 1))) {
3212             r = selection.end.row + TermWin.saveLines;
3213             if (((screen.rend[r][selection.end.col] & RS_multiMask) == RS_multi1)
3214                 && ((screen.rend[r][selection.end.col + 1] & RS_multiMask) == RS_multi2))
3215                 selection.end.col++;
3216         }
3217 #endif
3218     } else if (selection.clicks == 2) {
3219 /*
3220  * A2: extension on double click - selection between words
3221  */
3222         selection_delimit_word(col, row, &new_beg2, &new_end2);
3223         if (flag && closeto == LEFT)
3224             selection_delimit_word(selection.end.col, selection.end.row, &new_beg1, &new_end1);
3225         else if (flag && closeto == RIGHT)
3226             selection_delimit_word(selection.beg.col, selection.beg.row, &new_beg1, &new_end1);
3227         else
3228             selection_delimit_word(selection.mark.col, selection.mark.row, &new_beg1, &new_end1);
3229         if ((!flag && (selection.mark.row < row || (selection.mark.row == row && selection.mark.col <= col)))
3230             || (flag && closeto == RIGHT)) {
3231             selection.beg.col = new_beg1.col;
3232             selection.beg.row = new_beg1.row;
3233             selection.end.col = new_end2.col;
3234             selection.end.row = new_end2.row;
3235         } else {
3236             selection.beg.col = new_beg2.col;
3237             selection.beg.row = new_beg2.row;
3238             selection.end.col = new_end1.col;
3239             selection.end.row = new_end1.row;
3240         }
3241     } else if (selection.clicks == 3) {
3242 /*
3243  * A3: extension on triple click - selection between lines
3244  */
3245         if (flag) {
3246             if (closeto == LEFT)
3247                 selection.beg.row = row;
3248             else
3249                 selection.end.row = row;
3250         } else if (row <= selection.mark.row) {
3251             selection.beg.row = row;
3252             selection.end.row = selection.mark.row;
3253         } else {
3254             selection.beg.row = selection.mark.row;
3255             selection.end.row = row;
3256         }
3257         if (BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SELECT_WHOLE_LINE)) {
3258             selection.beg.col = 0;
3259         } else {
3260             selection.clicks = 2;
3261             selection_delimit_word(col, row, &new_beg2, &new_end2);
3262             selection.beg.col = new_beg2.col;
3263             selection.clicks = 3;
3264         }
3265         selection.end.col = TERM_WINDOW_GET_REPORTED_COLS() - 1;
3266     }
3267     D_SELECT(("(c:%d,r:%d)-(c:%d,r:%d) old (c:%d,r:%d)-(c:%d,r:%d)\n", selection.beg.col, selection.beg.row,
3268               selection.end.col, selection.end.row, old_beg.col, old_beg.row, old_end.col, old_end.row));
3269 
3270 /*
3271  * B1: clear anything before the current selection
3272  */
3273     if ((old_beg.row < selection.beg.row) || (old_beg.row == selection.beg.row && old_beg.col < selection.beg.col)) {
3274         if (selection.beg.col < TERM_WINDOW_GET_REPORTED_COLS() - 1) {
3275             row = selection.beg.row;
3276             col = selection.beg.col + 1;
3277         } else {
3278             row = selection.beg.row + 1;
3279             col = 0;
3280         }
3281         selection_setclr(0, old_beg.row, old_beg.col, row, col);
3282     }
3283 /*
3284  * B2: clear anything after the current selection
3285  */
3286     if ((old_end.row > selection.end.row) || (old_end.row == selection.end.row && old_end.col > selection.end.col)) {
3287         if (selection.end.col > 0) {
3288             row = selection.end.row;
3289             col = selection.end.col - 1;
3290         } else {
3291             row = selection.end.row - 1;
3292             col = TERM_WINDOW_GET_REPORTED_COLS() - 1;
3293         }
3294         selection_setclr(0, row, col, old_end.row, old_end.col);
3295     }
3296 /*
3297  * B3: set everything
3298  */
3299 /* TODO: optimise this */
3300     selection_setclr(1, selection.beg.row, selection.beg.col, selection.end.row, selection.end.col);
3301     return;
3302 }
3303 
3304 /*
3305  * Double click on button 3 when already selected
3306  * EXT: button 3 double click
3307  */
3308 void
3309 selection_rotate(int x, int y)
3310 {
3311     int col, row;
3312 
3313     col = Pixel2Col(x);
3314     row = Pixel2Row(y);
3315     selection.clicks = selection.clicks % 3 + 1;
3316     selection_extend_colrow(col, row, 1, 0);
3317 }
3318 
3319 /*
3320  * Respond to a request for our current selection
3321  * EXT: SelectionRequest
3322  */
3323 void
3324 selection_send(XSelectionRequestEvent * rq)
3325 {
3326     XEvent ev;
3327     long target_list[2];
3328 
3329     ev.xselection.type = SelectionNotify;
3330     ev.xselection.property = None;
3331     ev.xselection.display = rq->display;
3332     ev.xselection.requestor = rq->requestor;
3333     ev.xselection.selection = rq->selection;
3334     ev.xselection.target = rq->target;
3335     ev.xselection.time = rq->time;
3336 
3337     if (rq->target == props[PROP_SELECTION_TARGETS]) {
3338         target_list[0] = props[PROP_SELECTION_TARGETS];
3339         target_list[1] = XA_STRING;
3340         XChangeProperty(Xdisplay, rq->requestor, rq->property, rq->target,
3341                         32, PropModeReplace, (unsigned char *) target_list,
3342                         (sizeof(target_list) / sizeof(target_list[0])));
3343         ev.xselection.property = rq->property;
3344 #ifdef MULTI_CHARSET
3345 #  ifdef X_HAVE_UTF8_STRING
3346     } else if (rq->target == props[PROP_UTF8_STRING]) {
3347         XTextProperty xtextp;
3348         char *l[1];
3349 
3350         *l = selection.text;
3351         xtextp.value = NULL;
3352         xtextp.nitems = 0;
3353         if (XmbTextListToTextProperty(Xdisplay, l, 1, XUTF8StringStyle, &xtextp) == Success) {
3354             if (xtextp.nitems > 0 && xtextp.value) {
3355                 XChangeProperty(Xdisplay, rq->requestor, rq->property,
3356                                 rq->target, 8, PropModeReplace, xtextp.value, xtextp.nitems);
3357                 ev.xselection.property = rq->property;
3358                 XFree(xtextp.value);
3359             }
3360         }
3361 #  endif /* X_HAVE_UTF8_STRING */
3362     } else if (rq->target == props[PROP_TEXT] || rq->target == props[PROP_COMPOUND_TEXT]) {
3363         XTextProperty xtextp;
3364         char *l[1];
3365 
3366         *l = selection.text;
3367         xtextp.value = NULL;
3368         xtextp.nitems = 0;
3369         if (XmbTextListToTextProperty(Xdisplay, l, 1, XCompoundTextStyle, &xtextp) == Success) {
3370             if (xtextp.nitems > 0 && xtextp.value) {
3371                 XChangeProperty(Xdisplay, rq->requestor, rq->property, props[PROP_COMPOUND_TEXT],
3372                                 8, PropModeReplace, xtextp.value, xtextp.nitems);
3373                 ev.xselection.property = rq->property;
3374                 XFree(xtextp.value);
3375             }
3376         }
3377 #endif /* MULTI_CHARSET */
3378     } else {
3379         XChangeProperty(Xdisplay, rq->requestor, rq->property, XA_STRING, 8, PropModeReplace, selection.text, selection.len);
3380         ev.xselection.property = rq->property;
3381     }
3382     XSendEvent(Xdisplay, rq->requestor, False, 0, &ev);
3383 }
3384 
3385 void                            /* drag report as used by the "twin" program */
3386 twin_mouse_drag_report(XButtonEvent * ev)
3387 {
3388     int button_number, key_state, x = Pixel2Col(ev->x), y = Pixel2Row(ev->y);
3389 
3390     switch (ev->button) {
3391         case AnyButton:        /* Button release */
3392             button_number = pb + Button1;       /* yeah, yeah */
3393             break;
3394         case Button1:          /* Button press */
3395         case Button2:
3396         case Button3:
3397             pb = button_number = ev->button - Button1;
3398             break;
3399         default:               /* Wheel mouse */
3400             button_number = 64 + ev->button - Button3 - 1;
3401             break;
3402     }
3403     key_state = ((ev->state & (ShiftMask | ControlMask))
3404                  + ((ev->state & Mod1Mask) ? 2 : 0));
3405     tt_printf((unsigned char *) "\033[5M%c%c%c%c%c",
3406               (32 + button_number + (key_state << 2)), (32 + (x & 0x7f) + 1), (32 + ((x >> 7) & 0x7f) + 1), (32 + (y & 0x7f) + 1),
3407               (32 + ((y >> 7) & 0x7f) + 1));
3408 }
3409 
3410 void
3411 mouse_report(XButtonEvent * ev)
3412 {
3413     int button_number, key_state;
3414 
3415     switch (ev->button) {
3416         case AnyButton:        /* Button release */
3417             button_number = 3;
3418             break;
3419         case Button1:          /* Button press */
3420         case Button2:
3421         case Button3:
3422             pb = button_number = ev->button - Button1;
3423             break;
3424         default:               /* Wheel mouse */
3425             button_number = 64 + ev->button - Button3 - 1;
3426             break;
3427     }
3428     key_state = ((ev->state & (ShiftMask | ControlMask))
3429                  + ((ev->state & Mod1Mask) ? 2 : 0));
3430     tt_printf((unsigned char *) "\033[M%c%c%c", (32 + button_number + (key_state << 2)), (32 + Pixel2Col(ev->x) + 1),
3431               (32 + Pixel2Row(ev->y) + 1));
3432 }
3433 
3434 void
3435 debug_colors(void)
3436 {
3437     int color;
3438     char *name[] = {
3439         "fg", "bg",
3440         "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
3441     };
3442 
3443     fprintf(stderr, "Color ( ");
3444     if (rstyle & RS_RVid)
3445         fprintf(stderr, "rvid ");
3446     if (rstyle & RS_Bold)
3447         fprintf(stderr, "bold ");
3448     if (rstyle & RS_Blink)
3449         fprintf(stderr, "blink ");
3450     if (rstyle & RS_Uline)
3451         fprintf(stderr, "uline ");
3452     if (rstyle & RS_Overscore)
3453         fprintf(stderr, "overscore ");
3454     if (rstyle & RS_Italic)
3455         fprintf(stderr, "italic ");
3456     if (rstyle & RS_Dim)
3457         fprintf(stderr, "dim ");
3458     if (rstyle & RS_Conceal)
3459         fprintf(stderr, "conceal ");
3460     fprintf(stderr, "): ");
3461 
3462     color = GET_FGCOLOR(rstyle);
3463     if (color >= minBright && color <= maxBright) {
3464         color -= (minBright - minColor);
3465         fprintf(stderr, "bright ");
3466     }
3467     fprintf(stderr, "%s on ", name[color]);
3468 
3469     color = GET_BGCOLOR(rstyle);
3470     if (color >= minBright && color <= maxBright) {
3471         color -= (minBright - minColor);
3472         fprintf(stderr, "bright ");
3473     }
3474     fprintf(stderr, "%s\n", name[color]);
3475 }
3476 
3477 #ifdef USE_XIM
3478 void
3479 xim_get_position(XPoint * pos)
3480 {
3481     pos->x = Col2Pixel(screen.col);
3482     if (scrollbar_is_visible() && !(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR_RIGHT))) {
3483         pos->x += scrollbar_trough_width();
3484     }
3485     pos->y = (Height2Pixel(screen.row)
3486 # ifdef MULTI_CHARSET
3487               + MAX((encoding_method == LATIN1 ? 0 : TermWin.mfont->ascent), TermWin.font->ascent)
3488 # else
3489               + TermWin.font->ascent
3490 # endif
3491               + TermWin.internalBorder + bbar_calc_docked_height(BBAR_DOCKED_TOP));
3492 }
3493 #endif
3494 
3495 #ifdef ESCREEN
3496 #  ifdef NS_HAVE_SCREEN
3497 void
3498 parse_screen_status_if_necessary(void)
3499 {
3500     ns_parse_screen(TermWin.screen, (TermWin.screen_pending > 1),
3501                     TERM_WINDOW_GET_REPORTED_COLS(), screen.text[TERM_WINDOW_GET_REPORTED_ROWS() + TermWin.saveLines - 1]);
3502     if (TermWin.screen_pending > 1)
3503         TermWin.screen_pending = 0;
3504 }
3505 #  endif
3506 #endif
3507