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