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