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