root/usr/src/lib/libcurses/screen/vidupdate.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1988 AT&T */
/*        All Rights Reserved   */

/*
 * University Copyright- Copyright (c) 1982, 1986, 1988
 * The Regents of the University of California
 * All Rights Reserved
 *
 * University Acknowledgment- Portions of this document are derived from
 * software developed by the University of California, Berkeley, and its
 * contributors.
 */

/*LINTLIBRARY*/

#include <sys/types.h>
#include <stdlib.h>
#include "curses_inc.h"

#ifdef PC6300PLUS
#include <fcntl.h>
#include <sys/console.h>
#endif

#define NUM_OF_SPECIFIC_TURN_OFFS       3
extern  chtype  bit_attributes[];

int Oldcolors[] = { COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
                COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE };

void
vidupdate(chtype newmode, chtype oldmode, int (*outc)(char))
{
        bool color_terminal = (cur_term->_pairs_tbl) ? TRUE : FALSE;
        chtype oldvideo = (oldmode & A_ATTRIBUTES) & ~A_COLOR;
        chtype newvideo = (newmode & A_ATTRIBUTES) & ~A_COLOR;
        int  _change_video(chtype, chtype, int (*)(char));
        void _change_color(short, int (*)(char));

        /* if colors are used, extract the color related information from */
        /* the old and new modes and then erase color-pairs fields in   */
        /* both arguments.                                              */

        if (color_terminal) {
                /* LINTED */
                short oldcolor = (short) PAIR_NUMBER(oldmode & A_COLOR);
                /* LINTED */
                short newcolor = (short) PAIR_NUMBER(newmode & A_COLOR);
                chtype turn_off = A_COLOR;

                /* erase information about video attributes that could not */
                /* have been used with colors                              */

                if (oldcolor == 0)
                        oldvideo &= ~turn_off;

                if (no_color_video != -1)
                        turn_off |= (((chtype) no_color_video) << 16);

                if (oldcolor != 0)
                        oldvideo &= ~turn_off;


                /* if the new mode contains color information, then first  */
                /* deal with video attributes, and then with colors.  This */
                /* way color information will overwrite video information. */

                if (newcolor != 0) {
                        /* erase information about video attributes that */
                        /* should not be used with colors                */

                        newvideo &= ~turn_off;

                        /* if the new and the old video modes became    */
                        /* the same don't bother with them              */

                        if (newvideo != oldvideo) {
                                if ((_change_video(newvideo, oldvideo,
                                    outc)) == -1) {
                                        _Color_pair *cur_pair =
                                            &cur_term->_cur_pair;
                                        oldcolor = -1;
                                        cur_pair->background =
                                            cur_pair->foreground = -1;
                                }
                        }
                        if (newcolor != oldcolor)
                                _change_color(newcolor, outc);
                }

                /* new mode doesn't contain any color information.  Deal */
                /* with colors first (possibly turning of the colors that */
                /* were contained in the oldmode, and then deal with video. */
                /* This way video attributes will overwrite colors.     */

                else {
                        if (newcolor != oldcolor)
                                _change_color(newcolor, outc);
                        if (newvideo != oldvideo)
                                (void) _change_video(newvideo, oldvideo, outc);
                }
        } else
                (void) _change_video(newvideo, oldvideo, outc);
}


int
_change_video(chtype newmode, chtype oldmode, int (*outc)(char))
{
        int rc = 0;

        /* If you have set_attributes let the terminfo writer */
        /* worry about it. */

        if (!set_attributes) {
        /*
         * The trick is that we want to pre-process the new and oldmode
         * so that we now know what they will really translate to on
         * the physical screen.
         * In the case where some attributes are being faked
         * we get rid of the attributes being asked for and just have
         * STANDOUT mode set.  Therefore, if STANDOUT and UNDERLINE were
         * on the screen but UNDERLINE was being faked to STANDOUT; and
         * the new mode is just UNDERLINE, we will get rid of any faked
         * modes and be left with and oldmode of STANDOUT and a new mode
         * of STANDOUT, in which case the check for newmode and oldmode
         * being equal will be true.
         *
         *
         * This test is similar to the concept explained above.
         * counter is the maximum attributes allowed on a terminal.
         * For instance, on an hp/tvi950 without set_attributes
         * the last video sequence sent will be the one the terminal
         * will be in (on that spot).  Therefore, in setupterm.c
         * if ceol_standout_glitch or magic_cookie_glitch is set
         * max_attributes is set to 1.  This is because on those terminals
         * only one attribute can be on at once.  So, we pre-process the
         * oldmode and the newmode and only leave the bits that are
         * significant.  In other words, if on an hp you ask for STANDOUT
         * and UNDERLINE it will become only STANDOUT since that is the
         * first bit that is looked at.  If then the user goes from
         * STANDOUT and UNDERLINE to STANDOUT and REVERSE the oldmode will
         * become STANDOUT and the newmode will become STANDOUT.
         *
         * This also helps the code below in that on a hp or tvi950 only
         * one bit will ever be set so that no code has to be added to
         * cut out early in case two attributes were asked for.
         */

                chtype  check_faked, modes[2];
                int     counter = max_attributes, i, j, tempmode;
                int     k = (cur_term->sgr_mode == oldmode) ? 1 : 2;

                modes[0] = newmode;
                modes[1] = oldmode;

                while (k-- > 0) {
                        if ((check_faked = (modes[k] &
                            cur_term->sgr_faked)) != A_NORMAL) {
                                modes[k] &= ~check_faked;
                                modes[k] |= A_STANDOUT;
                        }

                        if ((j = counter) >= 0) {
                                tempmode = A_NORMAL;
                                if (j > 0) {
                                        for (i = 0; i < NUM_ATTRIBUTES; i++) {
                                                if (modes[k] &
                                                    bit_attributes[i]) {
                                                        tempmode |=
                                                            bit_attributes[i];
                                                        if (--j == 0)
                                                                break;
                                                }
                                        }
                                }
                                modes[k] = tempmode;
                        }
                }
                newmode = modes[0];
                oldmode = modes[1];
        }

        if (newmode == oldmode)
                return (rc);

#ifdef DEBUG
        if (outf)
                fprintf(outf, "vidupdate oldmode=%o, newmode=%o\n",
                    oldmode, newmode);
#endif

        if (set_attributes) {
                (void) tputs(tparm(set_attributes,
                        newmode & A_STANDOUT,
                        newmode & A_UNDERLINE,
                        newmode & A_REVERSE,
                        newmode & A_BLINK,
                        newmode & A_DIM,
                        newmode & A_BOLD,
                        newmode & A_INVIS,
                        newmode & A_PROTECT,
                        newmode & A_ALTCHARSET),
                        1, outc);
                rc = -1;
        } else {
                chtype  turn_on, turn_off;
                int                     i;

                /*
                 * If we are going to turn something on anyway and we are
                 * on a glitchy terminal, don't bother turning it off
                 * since by turning something on you turn everything else off.
                 */

                if ((ceol_standout_glitch || magic_cookie_glitch >= 0) &&
                    ((turn_on = ((oldmode ^ newmode) & newmode)) !=
                    A_NORMAL)) {
                        goto turn_on_code;
        }

        if ((turn_off = (oldmode & newmode) ^ oldmode) != A_NORMAL) {
                /*
                 * Check for things to turn off.
                 * First see if we are going to turn off something
                 * that doesn't have a specific turn off capability.
                 *
                 * Then check to see if, even though there may be a specific
                 * turn off sequence, this terminal doesn't have one or
                 * the turn off sequence also turns off something else.
                 */
                if ((turn_off & ~(A_ALTCHARSET | A_STANDOUT | A_UNDERLINE)) ||
                    (turn_off != (turn_off & cur_term->check_turn_off))) {
                        (void) tputs(tparm_p0(exit_attribute_mode), 1, outc);
                        rc = -1;
                        oldmode = A_NORMAL;
                } else {
                        for (i = 0; i < NUM_OF_SPECIFIC_TURN_OFFS; i++) {
                                if (turn_off & bit_attributes[i]) {
                                        (void) tputs(tparm_p0
                                            (cur_term->turn_off_seq[i]),
                                            1, outc);
                                        oldmode &= ~bit_attributes[i];
                                        rc = -1;
                                }
                        }
                }
        }

        if ((turn_on = ((oldmode ^ newmode) & newmode)) != A_NORMAL) {
turn_on_code:

                /* Check for modes to turn on. */

                for (i = 0; i < NUM_ATTRIBUTES; i++)
                        if (turn_on & bit_attributes[i]) {
                                (void) tputs(tparm_p0(cur_term->turn_on_seq[i]),
                                    1, outc);
                                rc = -1;
                                /*
                                 * Keep turning off the bit(s) that we just
                                 * sent to the screen.  As soon as turn_on
                                 * reaches A_NORMAL we don't have to turn
                                 * anything else on and we can
                                 * break out of the loop.
                                 */
                                if ((turn_on &= ~bit_attributes[i]) ==
                                    A_NORMAL)
                                        break;
                        }
                }

                if (magic_cookie_glitch > 0)
                        (void) tputs(cursor_left, 1, outc);
        }
        cur_term->sgr_mode = newmode;
        return (rc);
}


void
_change_color(short newcolor, int (*outc)(char))
{
#ifndef PC6300PLUS
        {
        _Color_pair *ptp = cur_term->_pairs_tbl;
        /* pairs table pointer */
        _Color_pair *cur_pair = &cur_term->_cur_pair;

        /* MORE: we may have to change some stuff, depending on whether */
        /* HP terminals  will be changing the background, or not        */

        if (newcolor == 0) {
                if (orig_pair)
                        (void) tputs(tparm_p0(orig_pair), 1, outc);
                if (set_a_background || set_a_foreground ||
                    set_background || set_foreground) {
                        cur_pair->background = -1;
                        cur_pair->foreground = -1;
                }
                return;
        }

        /* if we are on HP type terminal, just send an escape sequence  */
        /* to use desired color pair (we could have done some optimization: */
        /* check if both the foreground and background of newcolor match    */
        /* the ones of cur_term->_cur_pair.  but that will happen only when */
        /* two color pairs are defined exacly the same, and probably not    */
        /* worth the effort).                                               */

        if (set_color_pair)
                (void) tputs(tparm_p1(set_color_pair, newcolor), 1, outc);

                /* on Tek model we can do some optimization.    */

        else {
                if (ptp[newcolor].background != cur_pair->background) {
                        if (set_a_background)
                                (void) tputs(tparm_p1(set_a_background,
                                    ptp[newcolor].background), 1, outc);
                        else if (set_background)
                                (void) tputs(tparm_p1(set_background,
                                    Oldcolors[ptp[newcolor].background]),
                                    1, outc);
                        cur_pair->background = ptp[newcolor].background;
                }
                if (ptp[newcolor].foreground != cur_pair->foreground) {
                        if (set_a_foreground)
                                (void) tputs(tparm_p1(set_a_foreground,
                                    ptp[newcolor].foreground), 1, outc);
                        else if (set_foreground)
                                (void) tputs(tparm_p1(set_foreground,
                                    Oldcolors[ptp[newcolor].foreground]),
                                    1, outc);
                        cur_pair->foreground = ptp[newcolor].foreground;
                }
        }
        }
#else
        {
        /* the following code is for PC6300 PLUS: it uses BOLD terminfo  */
        /* entry for turning on colors, and SGR0 for turning them off.   */
        /* Every time a new color-pair is used, we are forced to do an   */
        /* ioctl read, and the send 'enter_bold_mode' escape sequence.   */
        /* This could be improved  by using   */
        /* DIM, UNDERLINE, and REVERSE in addition to BOLD               */

        struct console con;
        _Color_pair *ptp = cur_term->_pairs_tbl;
        /* pairs table pointer */
        back = ptp[newcolor].background;
        fore = ptp[newcolor].foreground;

        (void) fflush(SP->term_file);
        ioctl(cur_term->Filedes, CONIOGETDATA, &con);
#define BOLD    4
        con.l[con.page].colors[BOLD] =
            ((back + back + (fore > 5)) * 8 + fore) & 0177;
        ioctl(cur_term->Filedes, CONIOSETDATA, &con);
        (void) tputs(enter_bold_mode, 1, outc);
        }
#endif
}