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