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