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