1 /****************************************************************************
2  * Copyright (c) 2008-2012,2013 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /****************************************************************************
30  *  Author: Juergen Pfeifer                                                 *
31  *                                                                          *
32  ****************************************************************************/
33 
34 #include <curses.priv.h>
35 #define CUR ((TERMINAL*)TCB)->type.
36 #include <tic.h>
37 
38 #if HAVE_NANOSLEEP
39 #include <time.h>
40 #if HAVE_SYS_TIME_H
41 #include <sys/time.h>		/* needed for MacOS X DP3 */
42 #endif
43 #endif
44 
45 #if HAVE_SIZECHANGE
46 # if !defined(sun) || !TERMIOS
47 #  if HAVE_SYS_IOCTL_H
48 #   include <sys/ioctl.h>
49 #  endif
50 # endif
51 #endif
52 
53 MODULE_ID("$Id: tinfo_driver.c,v 1.32 2013/08/31 15:22:46 tom Exp $")
54 
55 /*
56  * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
57  * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
58  */
59 #ifdef TIOCGSIZE
60 # define IOCTL_WINSIZE TIOCGSIZE
61 # define STRUCT_WINSIZE struct ttysize
62 # define WINSIZE_ROWS(n) (int)n.ts_lines
63 # define WINSIZE_COLS(n) (int)n.ts_cols
64 #else
65 # ifdef TIOCGWINSZ
66 #  define IOCTL_WINSIZE TIOCGWINSZ
67 #  define STRUCT_WINSIZE struct winsize
68 #  define WINSIZE_ROWS(n) (int)n.ws_row
69 #  define WINSIZE_COLS(n) (int)n.ws_col
70 # endif
71 #endif
72 
73 /*
74  * These should be screen structure members.  They need to be globals for
75  * historical reasons.  So we assign them in start_color() and also in
76  * set_term()'s screen-switching logic.
77  */
78 #if USE_REENTRANT
79 NCURSES_EXPORT(int)
80 NCURSES_PUBLIC_VAR(COLOR_PAIRS) (void)
81 {
82     return CURRENT_SCREEN ? CURRENT_SCREEN->_pair_count : -1;
83 }
84 NCURSES_EXPORT(int)
85 NCURSES_PUBLIC_VAR(COLORS) (void)
86 {
87     return CURRENT_SCREEN ? CURRENT_SCREEN->_color_count : -1;
88 }
89 #else
90 NCURSES_EXPORT_VAR(int) COLOR_PAIRS = 0;
91 NCURSES_EXPORT_VAR(int) COLORS = 0;
92 #endif
93 
94 #define TCBMAGIC NCDRV_MAGIC(NCDRV_TINFO)
95 #define AssertTCB() assert(TCB!=0 && TCB->magic==TCBMAGIC)
96 #define SetSP() assert(TCB->csp!=0); sp = TCB->csp; (void) sp
97 
98 /*
99  * This routine needs to do all the work to make curscr look
100  * like newscr.
101  */
102 static int
103 drv_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
104 {
105     AssertTCB();
106     return TINFO_DOUPDATE(TCB->csp);
107 }
108 
109 static bool
110 drv_CanHandle(TERMINAL_CONTROL_BLOCK * TCB, const char *tname, int *errret)
111 {
112     bool result = FALSE;
113     int status;
114     TERMINAL *termp;
115     SCREEN *sp;
116 
117     assert(TCB != 0 && tname != 0);
118     termp = (TERMINAL *) TCB;
119     sp = TCB->csp;
120     TCB->magic = TCBMAGIC;
121 
122 #if (NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP)
123     status = _nc_setup_tinfo(tname, &termp->type);
124 #else
125     status = TGETENT_NO;
126 #endif
127 
128     /* try fallback list if entry on disk */
129     if (status != TGETENT_YES) {
130 	const TERMTYPE *fallback = _nc_fallback(tname);
131 
132 	if (fallback) {
133 	    termp->type = *fallback;
134 	    status = TGETENT_YES;
135 	}
136     }
137 
138     if (status != TGETENT_YES) {
139 	NCURSES_SP_NAME(del_curterm) (NCURSES_SP_ARGx termp);
140 	if (status == TGETENT_ERR) {
141 	    ret_error0(status, "terminals database is inaccessible\n");
142 	} else if (status == TGETENT_NO) {
143 	    ret_error1(status, "unknown terminal type.\n", tname);
144 	}
145     }
146     result = TRUE;
147 #if !USE_REENTRANT
148     strncpy(ttytype, termp->type.term_names, (size_t) NAMESIZE - 1);
149     ttytype[NAMESIZE - 1] = '\0';
150 #endif
151 
152     if (command_character)
153 	_nc_tinfo_cmdch(termp, *command_character);
154 
155     if (generic_type) {
156 	/*
157 	 * BSD 4.3's termcap contains mis-typed "gn" for wy99.  Do a sanity
158 	 * check before giving up.
159 	 */
160 	if ((VALID_STRING(cursor_address)
161 	     || (VALID_STRING(cursor_down) && VALID_STRING(cursor_home)))
162 	    && VALID_STRING(clear_screen)) {
163 	    ret_error1(TGETENT_YES, "terminal is not really generic.\n", tname);
164 	} else {
165 	    ret_error1(TGETENT_NO, "I need something more specific.\n", tname);
166 	}
167     }
168     if (hard_copy) {
169 	ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n", tname);
170     }
171 
172     return result;
173 }
174 
175 static int
176 drv_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB, int beepFlag)
177 {
178     SCREEN *sp;
179     int res = ERR;
180 
181     AssertTCB();
182     SetSP();
183 
184     /* FIXME: should make sure that we are not in altchar mode */
185     if (beepFlag) {
186 	if (bell) {
187 	    res = NCURSES_PUTP2("bell", bell);
188 	    NCURSES_SP_NAME(_nc_flush) (sp);
189 	} else if (flash_screen) {
190 	    res = NCURSES_PUTP2("flash_screen", flash_screen);
191 	    NCURSES_SP_NAME(_nc_flush) (sp);
192 	}
193     } else {
194 	if (flash_screen) {
195 	    res = NCURSES_PUTP2("flash_screen", flash_screen);
196 	    NCURSES_SP_NAME(_nc_flush) (sp);
197 	} else if (bell) {
198 	    res = NCURSES_PUTP2("bell", bell);
199 	    NCURSES_SP_NAME(_nc_flush) (sp);
200 	}
201     }
202     return res;
203 }
204 
205 /*
206  * SVr4 curses is known to interchange color codes (1,4) and (3,6), possibly
207  * to maintain compatibility with a pre-ANSI scheme.  The same scheme is
208  * also used in the FreeBSD syscons.
209  */
210 static int
211 toggled_colors(int c)
212 {
213     if (c < 16) {
214 	static const int table[] =
215 	{0, 4, 2, 6, 1, 5, 3, 7,
216 	 8, 12, 10, 14, 9, 13, 11, 15};
217 	c = table[c];
218     }
219     return c;
220 }
221 
222 static int
223 drv_print(TERMINAL_CONTROL_BLOCK * TCB, char *data, int len)
224 {
225     SCREEN *sp;
226 
227     AssertTCB();
228     SetSP();
229 #if NCURSES_EXT_FUNCS
230     return NCURSES_SP_NAME(mcprint) (TCB->csp, data, len);
231 #else
232     return ERR;
233 #endif
234 }
235 
236 static int
237 drv_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB, int fg, int bg)
238 {
239     SCREEN *sp;
240     int code = ERR;
241 
242     AssertTCB();
243     SetSP();
244 
245     if (sp != 0 && orig_pair && orig_colors && (initialize_pair != 0)) {
246 #if NCURSES_EXT_FUNCS
247 	sp->_default_color = isDefaultColor(fg) || isDefaultColor(bg);
248 	sp->_has_sgr_39_49 = (NCURSES_SP_NAME(tigetflag) (NCURSES_SP_ARGx
249 							  "AX")
250 			      == TRUE);
251 	sp->_default_fg = isDefaultColor(fg) ? COLOR_DEFAULT : (fg & C_MASK);
252 	sp->_default_bg = isDefaultColor(bg) ? COLOR_DEFAULT : (bg & C_MASK);
253 	if (sp->_color_pairs != 0) {
254 	    bool save = sp->_default_color;
255 	    sp->_default_color = TRUE;
256 	    NCURSES_SP_NAME(init_pair) (NCURSES_SP_ARGx
257 					0,
258 					(short)fg,
259 					(short)bg);
260 	    sp->_default_color = save;
261 	}
262 #endif
263 	code = OK;
264     }
265     return (code);
266 }
267 
268 static void
269 drv_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
270 	     int fore,
271 	     int color,
272 	     NCURSES_SP_OUTC outc)
273 {
274     SCREEN *sp;
275 
276     AssertTCB();
277     SetSP();
278 
279     if (fore) {
280 	if (set_a_foreground) {
281 	    TPUTS_TRACE("set_a_foreground");
282 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
283 				    TPARM_1(set_a_foreground, color), 1, outc);
284 	} else {
285 	    TPUTS_TRACE("set_foreground");
286 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
287 				    TPARM_1(set_foreground,
288 					    toggled_colors(color)), 1, outc);
289 	}
290     } else {
291 	if (set_a_background) {
292 	    TPUTS_TRACE("set_a_background");
293 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
294 				    TPARM_1(set_a_background, color), 1, outc);
295 	} else {
296 	    TPUTS_TRACE("set_background");
297 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
298 				    TPARM_1(set_background,
299 					    toggled_colors(color)), 1, outc);
300 	}
301     }
302 }
303 
304 static bool
305 drv_rescol(TERMINAL_CONTROL_BLOCK * TCB)
306 {
307     bool result = FALSE;
308     SCREEN *sp;
309 
310     AssertTCB();
311     SetSP();
312 
313     if (orig_pair != 0) {
314 	NCURSES_PUTP2("orig_pair", orig_pair);
315 	result = TRUE;
316     }
317     return result;
318 }
319 
320 static bool
321 drv_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
322 {
323     int result = FALSE;
324     SCREEN *sp;
325 
326     AssertTCB();
327     SetSP();
328 
329     if (orig_colors != 0) {
330 	NCURSES_PUTP2("orig_colors", orig_colors);
331 	result = TRUE;
332     }
333     return result;
334 }
335 
336 static int
337 drv_size(TERMINAL_CONTROL_BLOCK * TCB, int *linep, int *colp)
338 {
339     SCREEN *sp;
340     bool useEnv = TRUE;
341     bool useTioctl = TRUE;
342 
343     AssertTCB();
344     sp = TCB->csp;		/* can be null here */
345 
346     if (sp) {
347 	useEnv = sp->_use_env;
348 	useTioctl = sp->_use_tioctl;
349     } else {
350 	useEnv = _nc_prescreen.use_env;
351 	useTioctl = _nc_prescreen.use_tioctl;
352     }
353 
354     /* figure out the size of the screen */
355     T(("screen size: terminfo lines = %d columns = %d", lines, columns));
356 
357     *linep = (int) lines;
358     *colp = (int) columns;
359 
360     if (useEnv || useTioctl) {
361 	int value;
362 
363 #ifdef __EMX__
364 	{
365 	    int screendata[2];
366 	    _scrsize(screendata);
367 	    *colp = screendata[0];
368 	    *linep = ((sp != 0 && sp->_filtered)
369 		      ? 1
370 		      : screendata[1]);
371 	    T(("EMX screen size: environment LINES = %d COLUMNS = %d",
372 	       *linep, *colp));
373 	}
374 #endif
375 #if HAVE_SIZECHANGE
376 	/* try asking the OS */
377 	{
378 	    TERMINAL *termp = (TERMINAL *) TCB;
379 	    if (isatty(termp->Filedes)) {
380 		STRUCT_WINSIZE size;
381 
382 		errno = 0;
383 		do {
384 		    if (ioctl(termp->Filedes, IOCTL_WINSIZE, &size) >= 0) {
385 			*linep = ((sp != 0 && sp->_filtered)
386 				  ? 1
387 				  : WINSIZE_ROWS(size));
388 			*colp = WINSIZE_COLS(size);
389 			T(("SYS screen size: environment LINES = %d COLUMNS = %d",
390 			   *linep, *colp));
391 			break;
392 		    }
393 		} while
394 		    (errno == EINTR);
395 	    }
396 	}
397 #endif /* HAVE_SIZECHANGE */
398 
399 	if (useEnv) {
400 	    if (useTioctl) {
401 		/*
402 		 * If environment variables are used, update them.
403 		 */
404 		if ((sp == 0 || !sp->_filtered) && _nc_getenv_num("LINES") > 0) {
405 		    _nc_setenv_num("LINES", *linep);
406 		}
407 		if (_nc_getenv_num("COLUMNS") > 0) {
408 		    _nc_setenv_num("COLUMNS", *colp);
409 		}
410 	    }
411 
412 	    /*
413 	     * Finally, look for environment variables.
414 	     *
415 	     * Solaris lets users override either dimension with an environment
416 	     * variable.
417 	     */
418 	    if ((value = _nc_getenv_num("LINES")) > 0) {
419 		*linep = value;
420 		T(("screen size: environment LINES = %d", *linep));
421 	    }
422 	    if ((value = _nc_getenv_num("COLUMNS")) > 0) {
423 		*colp = value;
424 		T(("screen size: environment COLUMNS = %d", *colp));
425 	    }
426 	}
427 
428 	/* if we can't get dynamic info about the size, use static */
429 	if (*linep <= 0) {
430 	    *linep = (int) lines;
431 	}
432 	if (*colp <= 0) {
433 	    *colp = (int) columns;
434 	}
435 
436 	/* the ultimate fallback, assume fixed 24x80 size */
437 	if (*linep <= 0) {
438 	    *linep = 24;
439 	}
440 	if (*colp <= 0) {
441 	    *colp = 80;
442 	}
443 
444 	/*
445 	 * Put the derived values back in the screen-size caps, so
446 	 * tigetnum() and tgetnum() will do the right thing.
447 	 */
448 	lines = (short) (*linep);
449 	columns = (short) (*colp);
450     }
451 
452     T(("screen size is %dx%d", *linep, *colp));
453     return OK;
454 }
455 
456 static int
457 drv_getsize(TERMINAL_CONTROL_BLOCK * TCB, int *l, int *c)
458 {
459     AssertTCB();
460     assert(l != 0 && c != 0);
461     *l = lines;
462     *c = columns;
463     return OK;
464 }
465 
466 static int
467 drv_setsize(TERMINAL_CONTROL_BLOCK * TCB, int l, int c)
468 {
469     AssertTCB();
470     lines = (short) l;
471     columns = (short) c;
472     return OK;
473 }
474 
475 static int
476 drv_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
477 {
478     SCREEN *sp = TCB->csp;
479     TERMINAL *_term = (TERMINAL *) TCB;
480     int result = OK;
481 
482     AssertTCB();
483     if (setFlag) {
484 	for (;;) {
485 	    if (SET_TTY(_term->Filedes, buf) != 0) {
486 		if (errno == EINTR)
487 		    continue;
488 		if (errno == ENOTTY) {
489 		    if (sp)
490 			sp->_notty = TRUE;
491 		}
492 		result = ERR;
493 	    }
494 	    break;
495 	}
496     } else {
497 	for (;;) {
498 	    if (GET_TTY(_term->Filedes, buf) != 0) {
499 		if (errno == EINTR)
500 		    continue;
501 		result = ERR;
502 	    }
503 	    break;
504 	}
505     }
506     return result;
507 }
508 
509 static int
510 drv_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
511 {
512     SCREEN *sp;
513     TERMINAL *_term = (TERMINAL *) TCB;
514     int code = ERR;
515 
516     AssertTCB();
517     sp = TCB->csp;
518 
519     if (progFlag)		/* prog mode */
520     {
521 	if (defFlag) {
522 	    /* def_prog_mode */
523 	    /*
524 	     * Turn off the XTABS bit in the tty structure if it was on.
525 	     */
526 	    if ((drv_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
527 #ifdef TERMIOS
528 		_term->Nttyb.c_oflag &= (unsigned) ~OFLAGS_TABS;
529 #else
530 		_term->Nttyb.sg_flags &= (unsigned) ~XTABS;
531 #endif
532 		code = OK;
533 	    }
534 	} else {
535 	    /* reset_prog_mode */
536 	    if (drv_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
537 		if (sp) {
538 		    if (sp->_keypad_on)
539 			_nc_keypad(sp, TRUE);
540 		    NC_BUFFERED(sp, TRUE);
541 		}
542 		code = OK;
543 	    }
544 	}
545     } else {			/* shell mode */
546 	if (defFlag) {
547 	    /* def_shell_mode */
548 	    /*
549 	     * If XTABS was on, remove the tab and backtab capabilities.
550 	     */
551 	    if (drv_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
552 #ifdef TERMIOS
553 		if (_term->Ottyb.c_oflag & OFLAGS_TABS)
554 		    tab = back_tab = NULL;
555 #else
556 		if (_term->Ottyb.sg_flags & XTABS)
557 		    tab = back_tab = NULL;
558 #endif
559 		code = OK;
560 	    }
561 	} else {
562 	    /* reset_shell_mode */
563 	    if (sp) {
564 		_nc_keypad(sp, FALSE);
565 		NCURSES_SP_NAME(_nc_flush) (sp);
566 		NC_BUFFERED(sp, FALSE);
567 	    }
568 	    code = drv_sgmode(TCB, TRUE, &(_term->Ottyb));
569 	}
570     }
571     return (code);
572 }
573 
574 static void
575 drv_wrap(SCREEN *sp)
576 {
577     if (sp) {
578 	sp->_mouse_wrap(sp);
579 	NCURSES_SP_NAME(_nc_screen_wrap) (sp);
580 	NCURSES_SP_NAME(_nc_mvcur_wrap) (sp);	/* wrap up cursor addressing */
581     }
582 }
583 
584 static void
585 drv_release(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
586 {
587 }
588 
589 #  define SGR0_TEST(mode) (mode != 0) && (exit_attribute_mode == 0 || strcmp(mode, exit_attribute_mode))
590 
591 static void
592 drv_screen_init(SCREEN *sp)
593 {
594     TERMINAL_CONTROL_BLOCK *TCB = TCBOf(sp);
595 
596     AssertTCB();
597 
598     /*
599      * Check for mismatched graphic-rendition capabilities.  Most SVr4
600      * terminfo trees contain entries that have rmul or rmso equated to
601      * sgr0 (Solaris curses copes with those entries).  We do this only
602      * for curses, since many termcap applications assume that
603      * smso/rmso and smul/rmul are paired, and will not function
604      * properly if we remove rmso or rmul.  Curses applications
605      * shouldn't be looking at this detail.
606      */
607     sp->_use_rmso = SGR0_TEST(exit_standout_mode);
608     sp->_use_rmul = SGR0_TEST(exit_underline_mode);
609 
610     /*
611      * Check whether we can optimize scrolling under dumb terminals in
612      * case we do not have any of these capabilities, scrolling
613      * optimization will be useless.
614      */
615     sp->_scrolling = ((scroll_forward && scroll_reverse) ||
616 		      ((parm_rindex ||
617 			parm_insert_line ||
618 			insert_line) &&
619 		       (parm_index ||
620 			parm_delete_line ||
621 			delete_line)));
622 
623     NCURSES_SP_NAME(baudrate) (sp);
624 
625     NCURSES_SP_NAME(_nc_mvcur_init) (sp);
626     /* initialize terminal to a sane state */
627     NCURSES_SP_NAME(_nc_screen_init) (sp);
628 }
629 
630 static void
631 drv_init(TERMINAL_CONTROL_BLOCK * TCB)
632 {
633     TERMINAL *trm;
634 
635     AssertTCB();
636 
637     trm = (TERMINAL *) TCB;
638 
639     TCB->info.initcolor = VALID_STRING(initialize_color);
640     TCB->info.canchange = can_change;
641     TCB->info.hascolor = ((VALID_NUMERIC(max_colors) && VALID_NUMERIC(max_pairs)
642 			   && (((set_foreground != NULL)
643 				&& (set_background != NULL))
644 			       || ((set_a_foreground != NULL)
645 				   && (set_a_background != NULL))
646 			       || set_color_pair)) ? TRUE : FALSE);
647 
648     TCB->info.caninit = !(exit_ca_mode && non_rev_rmcup);
649 
650     TCB->info.maxpairs = VALID_NUMERIC(max_pairs) ? max_pairs : 0;
651     TCB->info.maxcolors = VALID_NUMERIC(max_colors) ? max_colors : 0;
652     TCB->info.numlabels = VALID_NUMERIC(num_labels) ? num_labels : 0;
653     TCB->info.labelwidth = VALID_NUMERIC(label_width) ? label_width : 0;
654     TCB->info.labelheight = VALID_NUMERIC(label_height) ? label_height : 0;
655     TCB->info.nocolorvideo = VALID_NUMERIC(no_color_video) ? no_color_video
656 	: 0;
657     TCB->info.tabsize = VALID_NUMERIC(init_tabs) ? (int) init_tabs : 8;
658 
659     TCB->info.defaultPalette = hue_lightness_saturation ? _nc_hls_palette : _nc_cga_palette;
660 
661     /*
662      * If an application calls setupterm() rather than initscr() or
663      * newterm(), we will not have the def_prog_mode() call in
664      * _nc_setupscreen().  Do it now anyway, so we can initialize the
665      * baudrate.
666      */
667     if (isatty(trm->Filedes)) {
668 	TCB->drv->mode(TCB, TRUE, TRUE);
669     }
670 }
671 
672 #define MAX_PALETTE	8
673 #define InPalette(n)	((n) >= 0 && (n) < MAX_PALETTE)
674 
675 static void
676 drv_initpair(TERMINAL_CONTROL_BLOCK * TCB, int pair, int f, int b)
677 {
678     SCREEN *sp;
679 
680     AssertTCB();
681     SetSP();
682 
683     if ((initialize_pair != NULL) && InPalette(f) && InPalette(b)) {
684 	const color_t *tp = InfoOf(sp).defaultPalette;
685 
686 	TR(TRACE_ATTRS,
687 	   ("initializing pair: pair = %d, fg=(%d,%d,%d), bg=(%d,%d,%d)",
688 	    pair,
689 	    tp[f].red, tp[f].green, tp[f].blue,
690 	    tp[b].red, tp[b].green, tp[b].blue));
691 
692 	NCURSES_PUTP2("initialize_pair",
693 		      TPARM_7(initialize_pair,
694 			      pair,
695 			      tp[f].red, tp[f].green, tp[f].blue,
696 			      tp[b].red, tp[b].green, tp[b].blue));
697     }
698 }
699 
700 static int
701 default_fg(SCREEN *sp)
702 {
703 #if NCURSES_EXT_FUNCS
704     return (sp != 0) ? sp->_default_fg : COLOR_WHITE;
705 #else
706     return COLOR_WHITE;
707 #endif
708 }
709 
710 static int
711 default_bg(SCREEN *sp)
712 {
713 #if NCURSES_EXT_FUNCS
714     return sp != 0 ? sp->_default_bg : COLOR_BLACK;
715 #else
716     return COLOR_BLACK;
717 #endif
718 }
719 
720 static void
721 drv_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
722 	      int color, int r, int g, int b)
723 {
724     SCREEN *sp = TCB->csp;
725 
726     AssertTCB();
727     if (initialize_color != NULL) {
728 	NCURSES_PUTP2("initialize_color",
729 		      TPARM_4(initialize_color, color, r, g, b));
730     }
731 }
732 
733 static void
734 drv_do_color(TERMINAL_CONTROL_BLOCK * TCB,
735 	     int old_pair,
736 	     int pair,
737 	     int reverse,
738 	     NCURSES_SP_OUTC outc)
739 {
740     SCREEN *sp = TCB->csp;
741     NCURSES_COLOR_T fg = COLOR_DEFAULT;
742     NCURSES_COLOR_T bg = COLOR_DEFAULT;
743     NCURSES_COLOR_T old_fg, old_bg;
744 
745     AssertTCB();
746     if (sp == 0)
747 	return;
748 
749     if (pair < 0 || pair >= COLOR_PAIRS) {
750 	return;
751     } else if (pair != 0) {
752 	if (set_color_pair) {
753 	    TPUTS_TRACE("set_color_pair");
754 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
755 				    TPARM_1(set_color_pair, pair), 1, outc);
756 	    return;
757 	} else if (sp != 0) {
758 	    NCURSES_SP_NAME(pair_content) (NCURSES_SP_ARGx
759 					   (short) pair,
760 					   &fg,
761 					   &bg);
762 	}
763     }
764 
765     if (old_pair >= 0
766 	&& sp != 0
767 	&& NCURSES_SP_NAME(pair_content) (NCURSES_SP_ARGx
768 					  (short) old_pair,
769 					  &old_fg,
770 					  &old_bg) !=ERR) {
771 	if ((isDefaultColor(fg) && !isDefaultColor(old_fg))
772 	    || (isDefaultColor(bg) && !isDefaultColor(old_bg))) {
773 #if NCURSES_EXT_FUNCS
774 	    /*
775 	     * A minor optimization - but extension.  If "AX" is specified in
776 	     * the terminal description, treat it as screen's indicator of ECMA
777 	     * SGR 39 and SGR 49, and assume the two sequences are independent.
778 	     */
779 	    if (sp->_has_sgr_39_49
780 		&& isDefaultColor(old_bg)
781 		&& !isDefaultColor(old_fg)) {
782 		NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx "\033[39m", 1, outc);
783 	    } else if (sp->_has_sgr_39_49
784 		       && isDefaultColor(old_fg)
785 		       && !isDefaultColor(old_bg)) {
786 		NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx "\033[49m", 1, outc);
787 	    } else
788 #endif
789 		drv_rescol(TCB);
790 	}
791     } else {
792 	drv_rescol(TCB);
793 	if (old_pair < 0)
794 	    return;
795     }
796 
797 #if NCURSES_EXT_FUNCS
798     if (isDefaultColor(fg))
799 	fg = (NCURSES_COLOR_T) default_fg(sp);
800     if (isDefaultColor(bg))
801 	bg = (NCURSES_COLOR_T) default_bg(sp);
802 #endif
803 
804     if (reverse) {
805 	NCURSES_COLOR_T xx = fg;
806 	fg = bg;
807 	bg = xx;
808     }
809 
810     TR(TRACE_ATTRS, ("setting colors: pair = %d, fg = %d, bg = %d", pair,
811 		     fg, bg));
812 
813     if (!isDefaultColor(fg)) {
814 	drv_setcolor(TCB, TRUE, fg, outc);
815     }
816     if (!isDefaultColor(bg)) {
817 	drv_setcolor(TCB, FALSE, bg, outc);
818     }
819 }
820 
821 #define xterm_kmous "\033[M"
822 static void
823 init_xterm_mouse(SCREEN *sp)
824 {
825     sp->_mouse_type = M_XTERM;
826     sp->_mouse_xtermcap = NCURSES_SP_NAME(tigetstr) (NCURSES_SP_ARGx "XM");
827     if (!VALID_STRING(sp->_mouse_xtermcap))
828 	sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";
829 }
830 
831 static void
832 drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
833 {
834     SCREEN *sp;
835 
836     AssertTCB();
837     SetSP();
838 
839     /* we know how to recognize mouse events under "xterm" */
840     if (sp != 0) {
841 	if (key_mouse != 0) {
842 	    if (!strcmp(key_mouse, xterm_kmous)
843 		|| strstr(TerminalOf(sp)->type.term_names, "xterm") != 0) {
844 		init_xterm_mouse(sp);
845 	    }
846 	} else if (strstr(TerminalOf(sp)->type.term_names, "xterm") != 0) {
847 	    if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK)
848 		init_xterm_mouse(sp);
849 	}
850     }
851 }
852 
853 static int
854 drv_testmouse(TERMINAL_CONTROL_BLOCK * TCB,
855 	      int delay
856 	      EVENTLIST_2nd(_nc_eventlist * evl))
857 {
858     int rc = 0;
859     SCREEN *sp;
860 
861     AssertTCB();
862     SetSP();
863 
864 #if USE_SYSMOUSE
865     if ((sp->_mouse_type == M_SYSMOUSE)
866 	&& (sp->_sysmouse_head < sp->_sysmouse_tail)) {
867 	rc = TW_MOUSE;
868     } else
869 #endif
870     {
871 	rc = TCBOf(sp)->drv->twait(TCBOf(sp),
872 				   TWAIT_MASK,
873 				   delay,
874 				   (int *) 0
875 				   EVENTLIST_2nd(evl));
876 #if USE_SYSMOUSE
877 	if ((sp->_mouse_type == M_SYSMOUSE)
878 	    && (sp->_sysmouse_head < sp->_sysmouse_tail)
879 	    && (rc == 0)
880 	    && (errno == EINTR)) {
881 	    rc |= TW_MOUSE;
882 	}
883 #endif
884     }
885     return rc;
886 }
887 
888 static int
889 drv_mvcur(TERMINAL_CONTROL_BLOCK * TCB, int yold, int xold, int ynew, int xnew)
890 {
891     SCREEN *sp = TCB->csp;
892     AssertTCB();
893     return NCURSES_SP_NAME(_nc_mvcur) (sp, yold, xold, ynew, xnew);
894 }
895 
896 static void
897 drv_hwlabel(TERMINAL_CONTROL_BLOCK * TCB, int labnum, char *text)
898 {
899     SCREEN *sp = TCB->csp;
900 
901     AssertTCB();
902     if (labnum > 0 && labnum <= num_labels) {
903 	NCURSES_PUTP2("plab_norm",
904 		      TPARM_2(plab_norm, labnum, text));
905     }
906 }
907 
908 static void
909 drv_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB, int OnFlag)
910 {
911     SCREEN *sp = TCB->csp;
912 
913     AssertTCB();
914     if (OnFlag) {
915 	NCURSES_PUTP2("label_on", label_on);
916     } else {
917 	NCURSES_PUTP2("label_off", label_off);
918     }
919 }
920 
921 static chtype
922 drv_conattr(TERMINAL_CONTROL_BLOCK * TCB)
923 {
924     SCREEN *sp = TCB->csp;
925     chtype attrs = A_NORMAL;
926 
927     AssertTCB();
928     if (enter_alt_charset_mode)
929 	attrs |= A_ALTCHARSET;
930 
931     if (enter_blink_mode)
932 	attrs |= A_BLINK;
933 
934     if (enter_bold_mode)
935 	attrs |= A_BOLD;
936 
937     if (enter_dim_mode)
938 	attrs |= A_DIM;
939 
940     if (enter_reverse_mode)
941 	attrs |= A_REVERSE;
942 
943     if (enter_standout_mode)
944 	attrs |= A_STANDOUT;
945 
946     if (enter_protected_mode)
947 	attrs |= A_PROTECT;
948 
949     if (enter_secure_mode)
950 	attrs |= A_INVIS;
951 
952     if (enter_underline_mode)
953 	attrs |= A_UNDERLINE;
954 
955     if (sp && sp->_coloron)
956 	attrs |= A_COLOR;
957 
958 #if USE_ITALIC
959     if (enter_italics_mode)
960 	attrs |= A_ITALIC;
961 #endif
962 
963     return (attrs);
964 }
965 
966 static void
967 drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
968 {
969     AssertTCB();
970 
971     clear_screen = 0;
972     cursor_down = parm_down_cursor = 0;
973     cursor_address = 0;
974     cursor_up = parm_up_cursor = 0;
975     row_address = 0;
976     cursor_home = carriage_return;
977 }
978 
979 static void
980 drv_initacs(TERMINAL_CONTROL_BLOCK * TCB, chtype *real_map, chtype *fake_map)
981 {
982     SCREEN *sp = TCB->csp;
983 
984     AssertTCB();
985     assert(sp != 0);
986     if (ena_acs != NULL) {
987 	NCURSES_PUTP2("ena_acs", ena_acs);
988     }
989 #if NCURSES_EXT_FUNCS
990     /*
991      * Linux console "supports" the "PC ROM" character set by the coincidence
992      * that smpch/rmpch and smacs/rmacs have the same values.  ncurses has
993      * no codepage support (see SCO Merge for an example).  Outside of the
994      * values defined in acsc, there are no definitions for the "PC ROM"
995      * character set (assumed by some applications to be codepage 437), but we
996      * allow those applications to use those codepoints.
997      *
998      * test/blue.c uses this feature.
999      */
1000 #define PCH_KLUDGE(a,b) (a != 0 && b != 0 && !strcmp(a,b))
1001     if (PCH_KLUDGE(enter_pc_charset_mode, enter_alt_charset_mode) &&
1002 	PCH_KLUDGE(exit_pc_charset_mode, exit_alt_charset_mode)) {
1003 	size_t i;
1004 	for (i = 1; i < ACS_LEN; ++i) {
1005 	    if (real_map[i] == 0) {
1006 		real_map[i] = i;
1007 		if (real_map != fake_map) {
1008 		    if (sp != 0)
1009 			sp->_screen_acs_map[i] = TRUE;
1010 		}
1011 	    }
1012 	}
1013     }
1014 #endif
1015 
1016     if (acs_chars != NULL) {
1017 	size_t i = 0;
1018 	size_t length = strlen(acs_chars);
1019 
1020 	while (i + 1 < length) {
1021 	    if (acs_chars[i] != 0 && UChar(acs_chars[i]) < ACS_LEN) {
1022 		real_map[UChar(acs_chars[i])] = UChar(acs_chars[i + 1]) | A_ALTCHARSET;
1023 		if (sp != 0)
1024 		    sp->_screen_acs_map[UChar(acs_chars[i])] = TRUE;
1025 	    }
1026 	    i += 2;
1027 	}
1028     }
1029 #ifdef TRACE
1030     /* Show the equivalent mapping, noting if it does not match the
1031      * given attribute, whether by re-ordering or duplication.
1032      */
1033     if (USE_TRACEF(TRACE_CALLS)) {
1034 	size_t n, m;
1035 	char show[ACS_LEN * 2 + 1];
1036 	for (n = 1, m = 0; n < ACS_LEN; n++) {
1037 	    if (real_map[n] != 0) {
1038 		show[m++] = (char) n;
1039 		show[m++] = (char) ChCharOf(real_map[n]);
1040 	    }
1041 	}
1042 	show[m] = 0;
1043 	if (acs_chars == NULL || strcmp(acs_chars, show))
1044 	    _tracef("%s acs_chars %s",
1045 		    (acs_chars == NULL) ? "NULL" : "READ",
1046 		    _nc_visbuf(acs_chars));
1047 	_tracef("%s acs_chars %s",
1048 		(acs_chars == NULL)
1049 		? "NULL"
1050 		: (strcmp(acs_chars, show)
1051 		   ? "DIFF"
1052 		   : "SAME"),
1053 		_nc_visbuf(show));
1054 
1055 	_nc_unlock_global(tracef);
1056     }
1057 #endif /* TRACE */
1058 }
1059 
1060 #define ENSURE_TINFO(sp) (TCBOf(sp)->drv->isTerminfo)
1061 
1062 NCURSES_EXPORT(void)
1063 _nc_cookie_init(SCREEN *sp)
1064 {
1065     bool support_cookies = USE_XMC_SUPPORT;
1066     TERMINAL_CONTROL_BLOCK *TCB = (TERMINAL_CONTROL_BLOCK *) (sp->_term);
1067 
1068     if (sp == 0 || !ENSURE_TINFO(sp))
1069 	return;
1070 
1071 #if USE_XMC_SUPPORT
1072     /*
1073      * If we have no magic-cookie support compiled-in, or if it is suppressed
1074      * in the environment, reset the support-flag.
1075      */
1076     if (magic_cookie_glitch >= 0) {
1077 	if (getenv("NCURSES_NO_MAGIC_COOKIE") != 0) {
1078 	    support_cookies = FALSE;
1079 	}
1080     }
1081 #endif
1082 
1083     if (!support_cookies && magic_cookie_glitch >= 0) {
1084 	T(("will disable attributes to work w/o magic cookies"));
1085     }
1086 
1087     if (magic_cookie_glitch > 0) {	/* tvi, wyse */
1088 
1089 	sp->_xmc_triggers = sp->_ok_attributes & XMC_CONFLICT;
1090 #if 0
1091 	/*
1092 	 * We "should" treat colors as an attribute.  The wyse350 (and its
1093 	 * clones) appear to be the only ones that have both colors and magic
1094 	 * cookies.
1095 	 */
1096 	if (has_colors()) {
1097 	    sp->_xmc_triggers |= A_COLOR;
1098 	}
1099 #endif
1100 	sp->_xmc_suppress = sp->_xmc_triggers & (chtype) ~(A_BOLD);
1101 
1102 	T(("magic cookie attributes %s", _traceattr(sp->_xmc_suppress)));
1103 	/*
1104 	 * Supporting line-drawing may be possible.  But make the regular
1105 	 * video attributes work first.
1106 	 */
1107 	acs_chars = ABSENT_STRING;
1108 	ena_acs = ABSENT_STRING;
1109 	enter_alt_charset_mode = ABSENT_STRING;
1110 	exit_alt_charset_mode = ABSENT_STRING;
1111 #if USE_XMC_SUPPORT
1112 	/*
1113 	 * To keep the cookie support simple, suppress all of the optimization
1114 	 * hooks except for clear_screen and the cursor addressing.
1115 	 */
1116 	if (support_cookies) {
1117 	    clr_eol = ABSENT_STRING;
1118 	    clr_eos = ABSENT_STRING;
1119 	    set_attributes = ABSENT_STRING;
1120 	}
1121 #endif
1122     } else if (magic_cookie_glitch == 0) {	/* hpterm */
1123     }
1124 
1125     /*
1126      * If magic cookies are not supported, cancel the strings that set
1127      * video attributes.
1128      */
1129     if (!support_cookies && magic_cookie_glitch >= 0) {
1130 	magic_cookie_glitch = ABSENT_NUMERIC;
1131 	set_attributes = ABSENT_STRING;
1132 	enter_blink_mode = ABSENT_STRING;
1133 	enter_bold_mode = ABSENT_STRING;
1134 	enter_dim_mode = ABSENT_STRING;
1135 	enter_reverse_mode = ABSENT_STRING;
1136 	enter_standout_mode = ABSENT_STRING;
1137 	enter_underline_mode = ABSENT_STRING;
1138     }
1139 
1140     /* initialize normal acs before wide, since we use mapping in the latter */
1141 #if !USE_WIDEC_SUPPORT
1142     if (_nc_unicode_locale() && _nc_locale_breaks_acs(sp->_term)) {
1143 	acs_chars = NULL;
1144 	ena_acs = NULL;
1145 	enter_alt_charset_mode = NULL;
1146 	exit_alt_charset_mode = NULL;
1147 	set_attributes = NULL;
1148     }
1149 #endif
1150 }
1151 
1152 static int
1153 drv_twait(TERMINAL_CONTROL_BLOCK * TCB,
1154 	  int mode,
1155 	  int milliseconds,
1156 	  int *timeleft
1157 	  EVENTLIST_2nd(_nc_eventlist * evl))
1158 {
1159     SCREEN *sp;
1160 
1161     AssertTCB();
1162     SetSP();
1163 
1164     return _nc_timed_wait(sp, mode, milliseconds, timeleft EVENTLIST_2nd(evl));
1165 }
1166 
1167 static int
1168 drv_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1169 {
1170     SCREEN *sp;
1171     unsigned char c2 = 0;
1172     int n;
1173 
1174     AssertTCB();
1175     assert(buf);
1176     SetSP();
1177 
1178 # if USE_PTHREADS_EINTR
1179     if ((pthread_self) && (pthread_kill) && (pthread_equal))
1180 	_nc_globals.read_thread = pthread_self();
1181 # endif
1182     n = read(sp->_ifd, &c2, (size_t) 1);
1183 #if USE_PTHREADS_EINTR
1184     _nc_globals.read_thread = 0;
1185 #endif
1186     *buf = (int) c2;
1187     return n;
1188 }
1189 
1190 static int
1191 drv_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1192 {
1193 #if HAVE_NANOSLEEP
1194     {
1195 	struct timespec request, remaining;
1196 	request.tv_sec = ms / 1000;
1197 	request.tv_nsec = (ms % 1000) * 1000000;
1198 	while (nanosleep(&request, &remaining) == -1
1199 	       && errno == EINTR) {
1200 	    request = remaining;
1201 	}
1202     }
1203 #else
1204     _nc_timed_wait(0, 0, ms, (int *) 0 EVENTLIST_2nd(0));
1205 #endif
1206     return OK;
1207 }
1208 
1209 static int
1210 __nc_putp(SCREEN *sp, const char *name GCC_UNUSED, const char *value)
1211 {
1212     int rc = ERR;
1213 
1214     if (value) {
1215 	rc = NCURSES_PUTP2(name, value);
1216     }
1217     return rc;
1218 }
1219 
1220 static int
1221 __nc_putp_flush(SCREEN *sp, const char *name, const char *value)
1222 {
1223     int rc = __nc_putp(sp, name, value);
1224     if (rc != ERR) {
1225 	NCURSES_SP_NAME(_nc_flush) (sp);
1226     }
1227     return rc;
1228 }
1229 
1230 static int
1231 drv_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag)
1232 {
1233     int ret = ERR;
1234     SCREEN *sp;
1235 
1236     AssertTCB();
1237 
1238     sp = TCB->csp;
1239 
1240     if (sp) {
1241 	if (flag) {
1242 	    (void) __nc_putp_flush(sp, "keypad_xmit", keypad_xmit);
1243 	} else if (!flag && keypad_local) {
1244 	    (void) __nc_putp_flush(sp, "keypad_local", keypad_local);
1245 	}
1246 	if (flag && !sp->_tried) {
1247 	    _nc_init_keytry(sp);
1248 	    sp->_tried = TRUE;
1249 	}
1250 	ret = OK;
1251     }
1252 
1253     return ret;
1254 }
1255 
1256 static int
1257 drv_keyok(TERMINAL_CONTROL_BLOCK * TCB, int c, int flag)
1258 {
1259     SCREEN *sp;
1260     int code = ERR;
1261     int count = 0;
1262     char *s;
1263 
1264     AssertTCB();
1265     SetSP();
1266 
1267     if (c >= 0) {
1268 	unsigned ch = (unsigned) c;
1269 	if (flag) {
1270 	    while ((s = _nc_expand_try(sp->_key_ok,
1271 				       ch, &count, (size_t) 0)) != 0
1272 		   && _nc_remove_key(&(sp->_key_ok), ch)) {
1273 		code = _nc_add_to_try(&(sp->_keytry), s, ch);
1274 		free(s);
1275 		count = 0;
1276 		if (code != OK)
1277 		    break;
1278 	    }
1279 	} else {
1280 	    while ((s = _nc_expand_try(sp->_keytry,
1281 				       ch, &count, (size_t) 0)) != 0
1282 		   && _nc_remove_key(&(sp->_keytry), ch)) {
1283 		code = _nc_add_to_try(&(sp->_key_ok), s, ch);
1284 		free(s);
1285 		count = 0;
1286 		if (code != OK)
1287 		    break;
1288 	    }
1289 	}
1290     }
1291     return (code);
1292 }
1293 
1294 static bool
1295 drv_kyExist(TERMINAL_CONTROL_BLOCK * TCB, int key)
1296 {
1297     bool res = FALSE;
1298 
1299     AssertTCB();
1300     if (TCB->csp)
1301 	res = TINFO_HAS_KEY(TCB->csp, key) == 0 ? FALSE : TRUE;
1302 
1303     return res;
1304 }
1305 
1306 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_TINFO_DRIVER = {
1307     TRUE,
1308 	drv_CanHandle,		/* CanHandle */
1309 	drv_init,		/* init */
1310 	drv_release,		/* release */
1311 	drv_size,		/* size */
1312 	drv_sgmode,		/* sgmode */
1313 	drv_conattr,		/* conattr */
1314 	drv_mvcur,		/* hwcur */
1315 	drv_mode,		/* mode */
1316 	drv_rescol,		/* rescol */
1317 	drv_rescolors,		/* rescolors */
1318 	drv_setcolor,		/* color */
1319 	drv_dobeepflash,	/* doBeepOrFlash */
1320 	drv_initpair,		/* initpair */
1321 	drv_initcolor,		/* initcolor */
1322 	drv_do_color,		/* docolor */
1323 	drv_initmouse,		/* initmouse */
1324 	drv_testmouse,		/* testmouse */
1325 	drv_setfilter,		/* setfilter */
1326 	drv_hwlabel,		/* hwlabel */
1327 	drv_hwlabelOnOff,	/* hwlabelOnOff */
1328 	drv_doupdate,		/* update */
1329 	drv_defaultcolors,	/* defaultcolors */
1330 	drv_print,		/* print */
1331 	drv_getsize,		/* getsize */
1332 	drv_setsize,		/* setsize */
1333 	drv_initacs,		/* initacs */
1334 	drv_screen_init,	/* scinit */
1335 	drv_wrap,		/* scexit */
1336 	drv_twait,		/* twait  */
1337 	drv_read,		/* read */
1338 	drv_nap,		/* nap */
1339 	drv_kpad,		/* kpad */
1340 	drv_keyok,		/* kyOk */
1341 	drv_kyExist		/* kyExist */
1342 };
1343