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