1 /* tcap: Unix V5, V7 and BS4.2 Termcap video driver
2 * for MicroEMACS
3 *
4 * $Id: tcap.c,v 1.190 2014/01/25 00:31:52 tom Exp $
5 *
6 */
7
8 #include "estruct.h"
9 #include "edef.h"
10
11 #if DISP_TERMCAP
12
13 #include "tcap.h"
14
15 #undef WINDOW
16 #define WINDOW vile_WINDOW
17
18 #if USE_TERMCAP
19 # define TCAPSLEN 1024
20 static char tc_parsed[TCAPSLEN];
21 #endif
22
23 static char *tc_CM, *tc_CE, *tc_CL, *tc_ME, *tc_MR, *tc_SO, *tc_SE;
24 static char *tc_TI, *tc_TE, *tc_KS, *tc_KE;
25 static char *tc_CS, *tc_dl, *tc_al, *tc_DL, *tc_AL, *tc_SF, *tc_SR;
26 static char *tc_VI, *tc_VE;
27
28 #if OPT_VIDEO_ATTRS
29 static char *tc_ZH; /* italic-start */
30 static char *tc_ZR; /* italic-end */
31 static char *tc_US; /* underline-start */
32 static char *tc_UE; /* underline-end */
33 static char *tc_MD;
34 static int tc_NC; /* no_color_video */
35 #endif
36
37 #if OPT_FLASH
38 static char *vb; /* visible-bell */
39 #endif
40
41 #if OPT_COLOR
42 /*
43 * This implementation is based on the description of SysVr4 curses found in
44 * ncurses 1.8.7, which lists the following terminal capabilities:
45 *
46 * Full name Terminfo Type Termcap Description
47 * ---------------- ------- ---- ---- -----------------------------
48 * back_color_erase "bce" bool "ut" screen erased with background color
49 * max_colors "colors" num "Co" maximum numbers of colors on screen
50 * max_pairs "pairs" num "pa" maximum number of color-pairs on the screen
51 * no_color_video "ncv" num "NC" video attributes that can't be used with colors
52 * orig_pair "op" str "op"
53 * orig_colors "oc" str "oc" set original colors
54 * initialize_color "initc" str "Ic"
55 * initialize_pair "initp" str "Ip"
56 * set_color_pair "scp" str "sp"
57 * set_a_foreground "setaf" str "AF"
58 * set_a_background "setab" str "AB"
59 * color_names "colornm" str "Yw"
60 *
61 * In this version, we don't support color pairs, since the only terminals on
62 * which it's been tested are "ANSI". The color names are hardcoded. The
63 * termcap must have the following capabilities set (or an equivalent
64 * terminfo):
65 * Co (limited to 1 .. (NCOLORS-1)
66 * AF (e.g., "\E[%a+c\036%dm")
67 * AB (e.g., "\E[%a+c\050%dm")
68 * oc (e.g., "\E[0m")
69 */
70
71 #define NO_COLOR (-1)
72 #define Num2Color(n) ((n >= 0) ? ctrans[(n) & (NCOLORS-1)] : NO_COLOR)
73
74 static char *AF;
75 static char *AB;
76 static char *Sf;
77 static char *Sb;
78 static char *OrigColors;
79 static int have_bce;
80
81 /* ANSI: black, red, green, yellow, blue, magenta, cyan, white */
82 static const char ANSI_palette[] =
83 {"0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"};
84 static const char UNKN_palette[] =
85 {"0 4 2 6 1 5 3 7 8 12 10 14 9 13 11 15"};
86 /*
87 * We don't really _know_ what the default colors are set to, so the initial
88 * values of the current_[fb]color are set to an illegal value to force the
89 * colors to be set.
90 */
91 static int given_fcolor = NO_COLOR;
92 static int given_bcolor = NO_COLOR;
93
94 static int shown_fcolor = NO_COLOR;
95 static int shown_bcolor = NO_COLOR;
96 #endif /* OPT_COLOR */
97
98 #if SYS_OS2_EMX
99 #include "os2keys.h"
100 #endif
101
102 static int colors_are_really_ANSI(void);
103 static void putpad(const char *str);
104 static void tcapscroll_delins(int from, int to, int n);
105 static void tcapscroll_reg(int from, int to, int n);
106 static void tcapscrollregion(int top, int bot);
107
108 #if OPT_COLOR
109 static void tcap_fcol(int color);
110 static void tcap_bcol(int color);
111 static int tcap_spal(const char *s);
112 #else
113 #define tcap_fcol nullterm_setfore
114 #define tcap_bcol nullterm_setback
115 #define tcap_spal nullterm_setpal
116 #endif
117
118 #if OPT_VIDEO_ATTRS
119 static void tcap_attr(UINT attr);
120 #else
121 static void tcap_rev(UINT state);
122 #endif
123
124 static int i_am_xterm;
125 static int i_was_closed;
126
127 #include "xtermkeys.h"
128
129 #ifdef HAVE_TPARM /* usually terminfo */
130 #define CALL_TPARM(cap,code) tparm(cap, (long) code, 0L,0L,0L,0L,0L,0L,0L,0L)
131 #else
132 #define CALL_TPARM(cap,code) tgoto(cap, 0, code)
133 #endif
134
135 static ENC_CHOICES my_encoding = enc_DEFAULT;
136
137 static void
tcap_set_encoding(ENC_CHOICES code)138 tcap_set_encoding(ENC_CHOICES code)
139 {
140 my_encoding = code;
141 }
142
143 static ENC_CHOICES
tcap_get_encoding(void)144 tcap_get_encoding(void)
145 {
146 return my_encoding;
147 }
148
149 static void
tcap_open(void)150 tcap_open(void)
151 {
152 #if USE_TERMCAP
153 char tc_rawdata[4096];
154 char *p = tc_parsed;
155 #endif
156 char *t;
157 char *tv_stype;
158 size_t i;
159 int j;
160 static int already_open = 0;
161 /* *INDENT-OFF* */
162 static const struct {
163 const char *name;
164 char **data;
165 } tc_strings[] = {
166 { CAPNAME("AL","il"), &tc_AL } /* add p1 lines above cursor */
167 ,{ CAPNAME("DL","dl"), &tc_DL } /* delete p1 lines, begin at cursor */
168 ,{ CAPNAME("al","il1"), &tc_al } /* add line below cursor */
169 ,{ CAPNAME("ce","el"), &tc_CE } /* clear to end of line */
170 ,{ CAPNAME("cl","clear"), &tc_CL } /* clear screen, cursor to home */
171 ,{ CAPNAME("cm","cup"), &tc_CM } /* move cursor to row p1, col p2 */
172 ,{ CAPNAME("cs","csr"), &tc_CS } /* set scrolling to rows p1 .. p2 */
173 ,{ CAPNAME("dl","dl1"), &tc_dl } /* delete line */
174 ,{ CAPNAME("ke","rmkx"), &tc_KE } /* end keypad-mode */
175 ,{ CAPNAME("ks","smkx"), &tc_KS } /* start keypad-mode */
176 ,{ CAPNAME("se","rmso"), &tc_SE } /* end standout-mode */
177 ,{ CAPNAME("sf","ind"), &tc_SF } /* scroll forward 1 line */
178 ,{ CAPNAME("so","smso"), &tc_SO } /* start standout-mode */
179 ,{ CAPNAME("mr","rev"), &tc_MR } /* start reverse-mode */
180 ,{ CAPNAME("sr","ri"), &tc_SR } /* scroll reverse 1 line */
181 ,{ CAPNAME("te","rmcup"), &tc_TE } /* end cursor-motion program */
182 ,{ CAPNAME("ti","smcup"), &tc_TI } /* initialize cursor-motion program */
183 #if OPT_COLOR
184 ,{ CAPNAME("AF","setaf"), &AF } /* set ANSI foreground-color */
185 ,{ CAPNAME("AB","setab"), &AB } /* set ANSI background-color */
186 ,{ CAPNAME("Sf","setf"), &Sf } /* set foreground-color */
187 ,{ CAPNAME("Sb","setb"), &Sb } /* set background-color */
188 ,{ CAPNAME("op","op"), &OrigColors } /* set to original color pair */
189 ,{ CAPNAME("oc","oc"), &OrigColors } /* set to original colors */
190 #endif
191 #if OPT_FLASH
192 ,{ CAPNAME("vb","flash"), &vb } /* visible bell */
193 #endif
194 #if OPT_VIDEO_ATTRS
195 ,{ CAPNAME("me","sgr0"), &tc_ME } /* turn off all attributes */
196 ,{ CAPNAME("md","bold"), &tc_MD } /* turn on bold attribute */
197 ,{ CAPNAME("us","smul"), &tc_US } /* underline-start */
198 ,{ CAPNAME("ue","rmul"), &tc_UE } /* underline-end */
199 ,{ CAPNAME("ZH","sitm"), &tc_ZH } /* italic-start */
200 ,{ CAPNAME("ZR","ritm"), &tc_ZR } /* italic-end */
201 #endif
202 ,{ CAPNAME("ve","cnorm"), &tc_VE } /* make cursor appear normal */
203 ,{ CAPNAME("vi","civis"), &tc_VI } /* make cursor invisible */
204 /* FIXME: do xmc/ug */
205 };
206 /* *INDENT-ON* */
207
208 TRACE((T_CALLED "tcap_open()\n"));
209
210 #if OPT_LOCALE
211 vl_open_mbterm();
212 #endif
213
214 if (already_open) {
215 if (i_was_closed) {
216 i_was_closed = FALSE;
217 #if OPT_XTERM
218 if (i_am_xterm) {
219 xterm_open(0);
220 }
221 #endif /* OPT_XTERM */
222 #if OPT_TITLE
223 if (auto_set_title) {
224 term.set_title(tb_values(current_title));
225 }
226 #endif
227 }
228 returnVoid();
229 }
230
231 if ((tv_stype = getenv("TERM")) == NULL) {
232 puts("Environment variable TERM not defined!");
233 ExitProgram(BADEXIT);
234 }
235 #if USE_TERMINFO
236 setupterm(tv_stype, fileno(stdout), (int *) 0);
237 #else
238 if ((tgetent(tc_rawdata, tv_stype)) != 1) {
239 fprintf(stderr, "Unknown terminal type %s!\n", tv_stype);
240 ExitProgram(BADEXIT);
241 }
242 TRACE(("tc_rawdata used %d of %d\n", strlen(tc_rawdata), sizeof(tc_rawdata)));
243 #endif
244
245 /* Get screen size from system, or else from termcap. */
246 getscreensize(&term.cols, &term.rows);
247
248 if ((term.rows <= 1)
249 && (term.rows = TGETNUM(CAPNAME("li", "lines"))) < 0) {
250 term.rows = 24;
251 }
252
253 if ((term.cols <= 1)
254 && (term.cols = TGETNUM(CAPNAME("co", "cols"))) < 0) {
255 term.cols = 80;
256 }
257 #if OPT_COLOR
258 if ((j = TGETNUM(CAPNAME("Co", "colors"))) > 0)
259 set_colors(j);
260 else
261 set_colors(2); /* white/black */
262 #endif
263
264 /* are we probably an xterm? */
265 #if OPT_XTERM
266 i_am_xterm = FALSE;
267 I_AM_XTERM(tv_stype);
268 if (i_am_xterm) {
269 xterm_open(&term);
270 }
271 #endif /* OPT_XTERM */
272
273 term.maxrows = term.rows;
274 term.maxcols = term.cols;
275
276 #if USE_TERMCAP
277 p = tc_parsed;
278 #endif
279 for (i = 0; i < TABLESIZE(tc_strings); i++) {
280 /* allow aliases */
281 if (NO_CAP(*(tc_strings[i].data))) {
282 t = TGETSTR(tc_strings[i].name, &p);
283 TRACE(("TGETSTR(%s) = %s\n", tc_strings[i].name, str_visible(t)));
284 /* simplify subsequent checks */
285 if (NO_CAP(t))
286 t = 0;
287 *(tc_strings[i].data) = t;
288 }
289 }
290
291 #if USE_TERMCAP
292 # ifdef HAVE_EXTERN_TCAP_PC
293 t = TGETSTR("pc", &p);
294 if (t)
295 PC = *t;
296 # endif
297 #endif
298
299 /* if start/end sequences are not consistent, ignore them */
300 if ((tc_SO != 0) ^ (tc_SE != 0))
301 tc_SO = tc_SE = 0;
302 if ((tc_MR != 0) ^ (tc_ME != 0))
303 tc_MR = tc_ME = 0;
304 #if OPT_VIDEO_ATTRS
305 if ((tc_MD != 0) ^ (tc_ME != 0))
306 tc_MD = tc_ME = 0;
307 if ((tc_US != 0) ^ (tc_UE != 0))
308 tc_US = tc_UE = 0;
309 if ((tc_ZH == 0) || (tc_ZR == 0)) {
310 tc_ZH = tc_US;
311 tc_ZR = tc_UE;
312 }
313 #endif
314
315 if ((tc_SO != 0 && tc_SE != 0) || (tc_MR != 0 && tc_ME != 0))
316 revexist = TRUE;
317
318 if (tc_CL == NULL || tc_CM == NULL) {
319 puts("Incomplete termcap entry\n");
320 ExitProgram(BADEXIT);
321 }
322
323 if (tc_CE == NULL) /* will we be able to use clear to EOL? */
324 eolexist = FALSE;
325
326 if (tc_CS && tc_SR) {
327 if (tc_SF == NULL) /* assume '\n' scrolls forward */
328 tc_SF = "\n";
329 term.scroll = tcapscroll_reg;
330 } else if ((tc_DL && tc_AL) || (tc_dl && tc_al)) {
331 term.scroll = tcapscroll_delins;
332 } else {
333 term.scroll = nullterm_scroll;
334 }
335 #if OPT_COLOR
336 /*
337 * If we've got one of the canonical strings for resetting to the
338 * default colors, we don't have to assume the screen is black/white.
339 */
340 if (OrigColors != 0) {
341 set_global_g_val(GVAL_FCOLOR, NO_COLOR); /* foreground color */
342 set_global_g_val(GVAL_BCOLOR, NO_COLOR); /* background color */
343 }
344
345 /* clear with current bcolor */
346 have_bce = TGETFLAG(CAPNAME("ut", "bce")) > 0;
347
348 #if OPT_VIDEO_ATTRS
349 if (OrigColors == 0)
350 OrigColors = tc_ME;
351 #endif
352 if (ncolors >= 8 && AF != 0 && AB != 0) {
353 Sf = AF;
354 Sb = AB;
355 set_palette(ANSI_palette);
356 } else if (colors_are_really_ANSI()) {
357 set_palette(ANSI_palette);
358 } else
359 set_palette(UNKN_palette);
360 #endif
361 #if OPT_VIDEO_ATTRS
362 if ((tc_NC = TGETNUM(CAPNAME("NC", "ncv"))) < 0)
363 tc_NC = 0;
364 if (tc_US == 0 && tc_UE == 0) { /* if we don't have underline, do bold */
365 tc_US = tc_MD;
366 tc_UE = tc_ME;
367 }
368 #endif
369
370 /*
371 * Read the termcap data now so tcap_init_fkeys() does not depend on the
372 * state of tgetstr() vs the buffer.
373 */
374 for (i = 0; i < TABLESIZE(keyseqs); ++i) {
375 keyseqs[i].result = TGETSTR(keyseqs[i].capname, &p);
376 #if USE_TERMINFO
377 if (NO_CAP(keyseqs[i].result))
378 keyseqs[i].result = 0;
379 #endif
380 }
381 tcap_init_fkeys();
382
383 #if USE_TERMCAP
384 TRACE(("tc_parsed used %d of %d\n", p - tc_parsed, sizeof(tc_parsed)));
385 if (p >= &tc_parsed[sizeof(tc_parsed)]) {
386 puts("Terminal description too big!\n");
387 ExitProgram(BADEXIT);
388 }
389 #endif
390 ttopen();
391 already_open = TRUE;
392
393 returnVoid();
394 }
395
396 static void
tcap_close(void)397 tcap_close(void)
398 {
399 TRACE((T_CALLED "tcap_close()\n"));
400 #if OPT_VIDEO_ATTRS
401 if (tc_SE)
402 putpad(tc_SE);
403 if (tc_ME) /* end special attributes (including color) */
404 putpad(tc_ME);
405 #endif
406 term.curmove(term.rows - 1, 0); /* cf: dumbterm.c */
407 term.eeol();
408 #if OPT_COLOR
409 shown_fcolor = shown_bcolor =
410 given_fcolor = given_bcolor = NO_COLOR;
411 #endif
412 /* all of the ways one could find the original title and restore it
413 * are too clumsy. Setting it to $TERM is a nice way to appease about
414 * 90% of the users.
415 */
416 term.set_title(getenv("TERM"));
417 #if OPT_XTERM
418 if (i_am_xterm) {
419 xterm_close();
420 }
421 #endif
422 #if OPT_LOCALE
423 vl_close_mbterm();
424 #endif
425 i_was_closed = TRUE;
426 returnVoid();
427 }
428
429 /*
430 * We open or close the keyboard when either of the following are true:
431 * a) we're changing the xterm-mouse setting
432 * b) we're spawning a subprocess (e.g., shell or pipe command)
433 */
434 static int keyboard_open = FALSE;
435
436 static void
tcap_kopen(void)437 tcap_kopen(void)
438 {
439 TRACE((T_CALLED "tcap_kopen()\n"));
440 term.mopen();
441 if (!keyboard_open) {
442 keyboard_open = TRUE;
443 if (tc_TI) {
444 putpad(tc_TI);
445 ttrow = ttcol = -1; /* 'ti' may move the cursor */
446 }
447 if (tc_KS)
448 putpad(tc_KS);
449 }
450 (void) strcpy(screen_desc, "NORMAL");
451 returnVoid();
452 }
453
454 static void
tcap_kclose(void)455 tcap_kclose(void)
456 {
457 TRACE((T_CALLED "tcap_kclose()\n"));
458 term.mclose();
459 if (keyboard_open) {
460 keyboard_open = FALSE;
461 if (tc_TE)
462 putpad(tc_TE);
463 if (tc_KE)
464 putpad(tc_KE);
465 }
466 term.flush();
467 returnVoid();
468 }
469
470 static void
tcap_move(register int row,register int col)471 tcap_move(register int row, register int col)
472 {
473 putpad(tgoto(tc_CM, col, row));
474 }
475
476 static void
begin_reverse(void)477 begin_reverse(void)
478 {
479 if (tc_MR != 0)
480 putpad(tc_MR);
481 else if (tc_SO != 0)
482 putpad(tc_SO);
483 }
484
485 static void
end_reverse(void)486 end_reverse(void)
487 {
488 if (tc_MR != 0) {
489 putpad(tc_ME);
490 } else if (tc_SO != 0) {
491 putpad(tc_SE);
492 }
493 }
494
495 #ifdef GVAL_VIDEO
496 #define REVERSED ((UINT) global_g_val(GVAL_VIDEO) & VAREV)
497 static void
set_reverse(void)498 set_reverse(void)
499 {
500 if (REVERSED)
501 begin_reverse();
502 }
503 #else
504 #define REVERSED 0
505 #define set_reverse() /* nothing */
506 #endif
507
508 #if OPT_COLOR
509 /*
510 * Accommodate brain-damaged non-bce terminals by writing a blank to each
511 * space that we'll color, return true if we moved the cursor.
512 */
513 static int
clear_non_bce(int row,int col)514 clear_non_bce(int row, int col)
515 {
516 int n;
517 int last = (row >= term.rows - 1) ? (term.cols - 1) : term.cols;
518 if (col < last) {
519 for (n = col; n < last; n++)
520 ttputc(' ');
521 return TRUE;
522 }
523 return FALSE;
524 }
525
526 static void
erase_non_bce(int row,int col)527 erase_non_bce(int row, int col)
528 {
529 if (clear_non_bce(row, col))
530 term.curmove(row, col);
531 }
532
533 #define NEED_BCE_FIX ((UINT) (!have_bce && shown_bcolor != NO_COLOR) || REVERSED)
534 #define FILL_BCOLOR(row,col) if(NEED_BCE_FIX) erase_non_bce(row, col)
535 #else
536 #define FILL_BCOLOR(row,col) /*nothing */
537 #endif
538
539 static void
tcap_eeol(void)540 tcap_eeol(void)
541 {
542 set_reverse();
543 #if OPT_COLOR
544 if (NEED_BCE_FIX) {
545 erase_non_bce(ttrow, ttcol);
546 } else
547 #endif
548 putpad(tc_CE);
549 }
550
551 static void
tcap_eeop(void)552 tcap_eeop(void)
553 {
554 set_reverse();
555 #if OPT_COLOR
556 tcap_fcol(gfcolor);
557 tcap_bcol(gbcolor);
558
559 if (NEED_BCE_FIX) {
560 int row = ttrow;
561 if (row < term.rows - 1) {
562 while (++row < term.rows) {
563 if (ttrow != row || ttcol != 0)
564 term.curmove(row, 0);
565 (void) clear_non_bce(row, 0);
566 }
567 term.curmove(ttrow, ttcol);
568 }
569 erase_non_bce(ttrow, ttcol);
570 } else
571 #endif
572 putpad(tc_CL);
573 }
574
575 /* move howmany lines starting at from to to */
576 static void
tcapscroll_reg(int from,int to,int n)577 tcapscroll_reg(int from, int to, int n)
578 {
579 int i;
580 if (to == from)
581 return;
582 if (to < from) {
583 tcapscrollregion(to, from + n - 1);
584 tcap_move(from + n - 1, 0);
585 for (i = from - to; i > 0; i--) {
586 putpad(tc_SF);
587 FILL_BCOLOR(from + n - 1, 0);
588 }
589 } else { /* from < to */
590 tcapscrollregion(from, to + n - 1);
591 tcap_move(from, 0);
592 for (i = to - from; i > 0; i--) {
593 putpad(tc_SR);
594 FILL_BCOLOR(from, 0);
595 }
596 }
597 tcapscrollregion(0, term.rows - 1);
598 }
599
600 /*
601 OPT_PRETTIER_SCROLL is prettier but slower -- it scrolls
602 a line at a time instead of all at once.
603 */
604
605 /* move howmany lines starting at from to to */
606 static void
tcapscroll_delins(int from,int to,int n)607 tcapscroll_delins(int from, int to, int n)
608 {
609 int i;
610 if (to == from)
611 return;
612 if (tc_DL && tc_AL) {
613 if (to < from) {
614 tcap_move(to, 0);
615 putpad(tgoto(tc_DL, 0, from - to));
616 tcap_move(to + n, 0);
617 putpad(tgoto(tc_AL, 0, from - to));
618 FILL_BCOLOR(to + n, 0);
619 } else {
620 tcap_move(from + n, 0);
621 putpad(tgoto(tc_DL, 0, to - from));
622 tcap_move(from, 0);
623 putpad(tgoto(tc_AL, 0, to - from));
624 FILL_BCOLOR(from + n, 0);
625 }
626 } else { /* must be dl and al */
627 #if OPT_PRETTIER_SCROLL
628 if (absol(from - to) > 1) {
629 tcapscroll_delins(from, (from < to) ? to - 1 : to + 1, n);
630 if (from < to)
631 from = to - 1;
632 else
633 from = to + 1;
634 }
635 #endif
636 if (to < from) {
637 tcap_move(to, 0);
638 for (i = from - to; i > 0; i--)
639 putpad(tc_dl);
640 tcap_move(to + n, 0);
641 for (i = from - to; i > 0; i--) {
642 putpad(tc_al);
643 FILL_BCOLOR(to + n, 0);
644 }
645 } else {
646 tcap_move(from + n, 0);
647 for (i = to - from; i > 0; i--)
648 putpad(tc_dl);
649 tcap_move(from, 0);
650 for (i = to - from; i > 0; i--) {
651 putpad(tc_al);
652 FILL_BCOLOR(from, 0);
653 }
654 }
655 }
656 }
657
658 /* cs is set up just like cm, so we use tgoto... */
659 static void
tcapscrollregion(int top,int bot)660 tcapscrollregion(int top, int bot)
661 {
662 putpad(tgoto(tc_CS, bot, top));
663 }
664
665 #if OPT_COLOR
666 /*
667 * This ugly hack is designed to work around an incompatibility built into
668 * non BSD-derived systems that implemented color based on a SVr4 manpage.
669 */
670 static int
colors_are_really_ANSI(void)671 colors_are_really_ANSI(void)
672 {
673 int ok = FALSE;
674 int n;
675 char cmp[BUFSIZ], *t;
676
677 if (Sf != 0 && Sb != 0 && ncolors == 8) {
678 for (n = 0; n < ncolors; n++) {
679 (void) lsprintf(cmp, "\033[4%dm", n);
680 if ((t = CALL_TPARM(Sb, n)) == 0 || strcmp(t, cmp))
681 break;
682 (void) lsprintf(cmp, "\033[3%dm", n);
683 if ((t = CALL_TPARM(Sf, n)) == 0 || strcmp(t, cmp))
684 break;
685 }
686 if (n >= ncolors) /* everything matched */
687 ok = TRUE;
688 }
689 return ok;
690 }
691
692 static void
show_ansi_colors(void)693 show_ansi_colors(void)
694 {
695 char *t;
696
697 if (Sf != 0 && Sb != 0) {
698 if (shown_fcolor == NO_COLOR
699 || shown_bcolor == NO_COLOR) {
700 if (OrigColors)
701 putpad(OrigColors);
702 }
703
704 if ((shown_fcolor != NO_COLOR)
705 && (t = CALL_TPARM(Sf, shown_fcolor)) != 0) {
706 putpad(t);
707 }
708 if ((shown_bcolor != NO_COLOR)
709 && (t = CALL_TPARM(Sb, shown_bcolor)) != 0) {
710 putpad(t);
711 }
712 }
713 }
714
715 static void
reinitialize_colors(void)716 reinitialize_colors(void)
717 {
718 int saved_fcolor = given_fcolor;
719 int saved_bcolor = given_bcolor;
720
721 shown_fcolor = shown_bcolor =
722 given_fcolor = given_bcolor = NO_COLOR;
723
724 tcap_fcol(saved_fcolor);
725 tcap_bcol(saved_bcolor);
726 }
727
728 static void
tcap_fcol(int color)729 tcap_fcol(int color)
730 {
731 if (color != given_fcolor) {
732 given_fcolor = color;
733 shown_fcolor = (Sf != 0) ? Num2Color(color) : NO_COLOR;
734 show_ansi_colors();
735 }
736 }
737
738 static void
tcap_bcol(int color)739 tcap_bcol(int color)
740 {
741 if (color != given_bcolor) {
742 given_bcolor = color;
743 shown_bcolor = (Sb != 0) ? Num2Color(color) : NO_COLOR;
744 show_ansi_colors();
745 }
746 }
747
748 static int
tcap_spal(const char * thePalette)749 tcap_spal(const char *thePalette) /* reset the palette registers */
750 {
751 int rc = set_ctrans(thePalette);
752 reinitialize_colors();
753 return rc;
754 }
755 #endif /* OPT_COLOR */
756
757 #if OPT_VIDEO_ATTRS
758 /*
759 * NOTE:
760 * On Linux console, the 'me' termcap setting \E[m resets _all_ attributes,
761 * including color. However, if we use 'se' instead, it doesn't clear the
762 * boldface. To compensate, we reset the colors when we put out any "ending"
763 * sequence, such as 'me'.
764 *
765 * In rxvt (2.12), setting _any_ attribute seems to clobber the color settings.
766 */
767 static void
tcap_attr(UINT attr)768 tcap_attr(UINT attr)
769 {
770 /* *INDENT-OFF* */
771 #define VA_SGR (VASEL|VAREV|VAUL|VAITAL|VABOLD)
772 static const struct {
773 char **start;
774 char **end;
775 UINT NC_bit;
776 UINT mask;
777 } tbl[] = {
778 { &tc_MR, &tc_ME, 4, VASEL|VAREV }, /* more reliable than standout */
779 { &tc_SO, &tc_SE, 1, VASEL|VAREV },
780 { &tc_US, &tc_UE, 2, VAUL },
781 { &tc_ZH, &tc_ZR, 2, VAITAL },
782 { &tc_MD, &tc_ME, 32, VABOLD },
783 };
784 /* *INDENT-ON* */
785
786 static UINT last;
787 static int all_sgr0 = -1;
788 unsigned n;
789 int colored = (ncolors > 2);
790
791 /*
792 * If we have one or more video attributes ending with the same
793 * pattern, it's likely that they all do (like a vt100). In that
794 * case, set a flag indicating that we'll have to assume that turning
795 * any one off turns all off.
796 *
797 * As a special case, look for the \E[m string, since that is often
798 * mixed with other pieces in sgr0, such as ^O or time delays.
799 */
800 if (all_sgr0 < 0) {
801 all_sgr0 = 0;
802 for (n = 0; n < TABLESIZE(tbl); n++) {
803 unsigned m = (n + 1) % TABLESIZE(tbl);
804 if (*tbl[n].end != 0
805 && *tbl[m].end != 0
806 && (!strcmp(*tbl[n].end, *tbl[m].end)
807 || strstr(*tbl[n].end, "\033[m") != 0)) {
808 all_sgr0 = 1;
809 break;
810 }
811 }
812 }
813
814 attr = VATTRIB(attr);
815 #ifdef GVAL_VIDEO
816 if (REVERSED) {
817 if (attr & VASEL)
818 attr ^= VASEL;
819 else
820 attr ^= VAREV;
821 }
822 #endif
823 if (!colored
824 && (attr & (VASPCOL | VACOLOR)) != 0) {
825 attr &= (UINT) (~(VASPCOL | VACOLOR));
826 }
827 if (attr & VASPCOL) {
828 attr = VCOLORATTR((VCOLORNUM(attr) & (ncolors - 1)));
829 } else {
830 if (attr & VACOLOR) {
831 attr &= (UINT) (VCOLORATTR(ncolors - 1) | ~VACOLOR);
832 }
833 attr &= ~(VAML | VAMLFOC);
834 }
835
836 #if OPT_COLOR
837 /*
838 * If we have a choice between color and some other attribute, choose
839 * color, since the other attributes may not be real anyway. But
840 * treat VASEL specially, otherwise we won't be able to see selections,
841 * as well as VAREV, in case it is used for visual-matches.
842 */
843 if (tc_NC != 0
844 && (attr & VACOLOR) != 0) {
845 for (n = 0; n < TABLESIZE(tbl); n++) {
846 if ((tbl[n].NC_bit & (UINT) tc_NC) != 0
847 && (tbl[n].mask & attr) != 0) {
848 if ((tbl[n].mask & (VASEL | VAREV)) != 0) {
849 attr &= (UINT) (~VACOLOR);
850 } else {
851 attr &= ~tbl[n].mask;
852 }
853 }
854 }
855 }
856 #endif
857
858 if (attr != last) {
859 register char *s;
860 UINT diff;
861 int ends = !colored;
862 #if OPT_COLOR
863 int redo_color = FALSE;
864 #endif
865
866 diff = last & ~attr;
867 /* turn OFF old attributes */
868 for (n = 0; n < TABLESIZE(tbl); n++) {
869 if ((tbl[n].mask & diff) != 0
870 && (tbl[n].mask & attr) == 0
871 && (s = *(tbl[n].end)) != 0) {
872 putpad(s);
873 #if OPT_COLOR
874 /*
875 * Any of the resets can turn off color, but especially those
876 * for reverse/bold since they use the sgr0 value.
877 */
878 if (colored)
879 redo_color = TRUE;
880 #endif
881 ends = TRUE;
882 diff &= ~(tbl[n].mask);
883 }
884 }
885 #if OPT_COLOR
886 if (redo_color) /* do this once */
887 reinitialize_colors();
888 #endif
889
890 if (all_sgr0)
891 diff = attr;
892 else
893 diff = attr & ~last;
894
895 /* turn ON new attributes */
896 for (n = 0; n < TABLESIZE(tbl); n++) {
897 if ((tbl[n].mask & diff) != 0
898 && (tbl[n].mask & attr) != 0
899 && (s = *(tbl[n].start)) != 0) {
900 putpad(s);
901 diff &= ~(tbl[n].mask);
902 }
903 }
904
905 if (ends && (attr & (VAREV | VASEL))) {
906 begin_reverse();
907 } else if (diff & VA_SGR) { /* we didn't find it */
908 end_reverse();
909 }
910 #if OPT_COLOR
911 if (colored) {
912 if (attr & VACOLOR) {
913 tcap_fcol(VCOLORNUM(attr));
914 } else if (given_fcolor != gfcolor) {
915 tcap_fcol(gfcolor);
916 }
917 }
918 #endif
919 last = attr;
920 }
921 }
922
923 #else /* highlighting is a minimum attribute */
924
925 /*
926 * change reverse video status
927 */
928 static void
tcap_rev(UINT state)929 tcap_rev(UINT state) /* FALSE = normal video, TRUE = reverse video */
930 {
931 static UINT revstate = SORTOFTRUE;
932 if (state == revstate)
933 return;
934 revstate = state;
935 if (state) {
936 begin_reverse();
937 } else {
938 end_reverse();
939 }
940 }
941
942 #endif /* OPT_VIDEO_ATTRS */
943
944 /*
945 * Hide/show cursor. We do this in levels, so we can do the "right" thing with
946 * multimotion.
947 */
948 static void
tcap_cursor(int flag)949 tcap_cursor(int flag)
950 {
951 static int level;
952 if (tc_VI != 0
953 && tc_VE != 0) {
954 if (flag) {
955 if (!++level) {
956 TRACE(("CURSOR ON\n"));
957 putpad(tc_VE);
958 }
959 } else {
960 if (!level--) {
961 TRACE(("CURSOR OFF\n"));
962 putpad(tc_VI);
963 }
964 }
965 }
966 }
967
968 static void
tcap_beep(void)969 tcap_beep(void)
970 {
971 #if OPT_FLASH
972 int hit = 0;
973
974 if (global_g_val(GMDFLASH)) {
975 if (vb) {
976 putpad(vb);
977 hit = 1;
978 }
979 }
980 if (!hit) {
981 static const char *seq[][2] =
982 {
983 {NULL, NULL}, /* vtflash = off */
984 {VTFLASH_NORMSEQ, VTFLASH_REVSEQ}, /* reverse */
985 {VTFLASH_REVSEQ, VTFLASH_NORMSEQ}, /* normal */
986 };
987 const char *str1, *str2;
988 int val;
989
990 val = global_g_val(GVAL_VTFLASH);
991 str1 = seq[val][0];
992 if (str1) {
993 str2 = seq[val][1];
994 putpad(str1);
995 term.flush();
996 catnap(150, FALSE);
997 putpad(str2);
998 hit = 1;
999 }
1000 }
1001 if (!hit)
1002 #endif
1003 ttputc(BEL);
1004 }
1005
1006 static void
putpad(const char * str)1007 putpad(const char *str)
1008 {
1009 tputs(str, 1, ttputc);
1010 }
1011
1012 TERM term =
1013 {
1014 0, /* the first four values are set dynamically */
1015 0,
1016 0,
1017 0,
1018 tcap_set_encoding,
1019 tcap_get_encoding,
1020 tcap_open,
1021 tcap_close,
1022 tcap_kopen,
1023 tcap_kclose,
1024 ttclean,
1025 ttunclean,
1026 nullterm_openup,
1027 ttgetc,
1028 vl_ttputc,
1029 tttypahead,
1030 ttflush,
1031 tcap_move,
1032 tcap_eeol,
1033 tcap_eeop,
1034 tcap_beep,
1035 #if OPT_VIDEO_ATTRS
1036 tcap_attr,
1037 #else
1038 tcap_rev,
1039 #endif
1040 nullterm_setdescrip,
1041 tcap_fcol,
1042 tcap_bcol,
1043 tcap_spal,
1044 nullterm_setccol,
1045 nullterm_scroll, /* set dynamically at open time */
1046 nullterm_pflush,
1047 nullterm_icursor,
1048 nullterm_settitle, /* filled in by xterm_open() */
1049 ttwatchfd,
1050 ttunwatchfd,
1051 tcap_cursor,
1052 nullterm_mopen, /* filled in by xterm_open() */
1053 nullterm_mclose, /* filled in by xterm_open() */
1054 nullterm_mevent, /* filled in by xterm_open() */
1055 };
1056
1057 #endif /* DISP_TERMCAP */
1058