xref: /illumos-gate/usr/src/lib/libcurses/screen/vidupdate.c (revision 4c87aefe8930bd07275b8dd2e96ea5f24d93a52e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*LINTLIBRARY*/
43 
44 #include <sys/types.h>
45 #include <stdlib.h>
46 #include "curses_inc.h"
47 
48 #ifdef PC6300PLUS
49 #include <fcntl.h>
50 #include <sys/console.h>
51 #endif
52 
53 #define	NUM_OF_SPECIFIC_TURN_OFFS	3
54 extern	chtype	bit_attributes[];
55 
56 int Oldcolors[] = { COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
57 		COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE };
58 
59 void
60 vidupdate(chtype newmode, chtype oldmode, int (*outc)(char))
61 {
62 	bool color_terminal = (cur_term->_pairs_tbl) ? TRUE : FALSE;
63 	chtype oldvideo = (oldmode & A_ATTRIBUTES) & ~A_COLOR;
64 	chtype newvideo = (newmode & A_ATTRIBUTES) & ~A_COLOR;
65 	int  _change_video(chtype, chtype, int (*)(char));
66 	void _change_color(short, int (*)(char));
67 
68 	/* if colors are used, extract the color related information from */
69 	/* the old and new modes and then erase color-pairs fields in	*/
70 	/* both arguments.						*/
71 
72 	if (color_terminal) {
73 		/* LINTED */
74 		short oldcolor = (short) PAIR_NUMBER(oldmode & A_COLOR);
75 		/* LINTED */
76 		short newcolor = (short) PAIR_NUMBER(newmode & A_COLOR);
77 		chtype turn_off = A_COLOR;
78 
79 		/* erase information about video attributes that could not */
80 		/* have been used with colors				   */
81 
82 		if (oldcolor == 0)
83 			oldvideo &= ~turn_off;
84 
85 		if (no_color_video != -1)
86 			turn_off |= (((chtype) no_color_video) << 16);
87 
88 		if (oldcolor != 0)
89 			oldvideo &= ~turn_off;
90 
91 
92 		/* if the new mode contains color information, then first  */
93 		/* deal with video attributes, and then with colors.  This */
94 		/* way color information will overwrite video information. */
95 
96 		if (newcolor != 0) {
97 			/* erase information about video attributes that */
98 			/* should not be used with colors		 */
99 
100 			newvideo &= ~turn_off;
101 
102 			/* if the new and the old video modes became 	*/
103 			/* the same don't bother with them		*/
104 
105 			if (newvideo != oldvideo) {
106 				if ((_change_video(newvideo, oldvideo,
107 				    outc)) == -1) {
108 					_Color_pair *cur_pair =
109 					    &cur_term->_cur_pair;
110 					oldcolor = -1;
111 					cur_pair->background =
112 					    cur_pair->foreground = -1;
113 				}
114 			}
115 			if (newcolor != oldcolor)
116 				_change_color(newcolor, outc);
117 		}
118 
119 		/* new mode doesn't contain any color information.  Deal */
120 		/* with colors first (possibly turning of the colors that */
121 		/* were contained in the oldmode, and then deal with video. */
122 		/* This way video attributes will overwrite colors.	*/
123 
124 		else {
125 			if (newcolor != oldcolor)
126 				_change_color(newcolor, outc);
127 			if (newvideo != oldvideo)
128 				(void) _change_video(newvideo, oldvideo, outc);
129 		}
130 	} else
131 		(void) _change_video(newvideo, oldvideo, outc);
132 }
133 
134 
135 int
136 _change_video(chtype newmode, chtype oldmode, int (*outc)(char))
137 {
138 	int rc = 0;
139 
140 	/* If you have set_attributes let the terminfo writer */
141 	/* worry about it. */
142 
143 	if (!set_attributes) {
144 	/*
145 	 * The trick is that we want to pre-process the new and oldmode
146 	 * so that we now know what they will really translate to on
147 	 * the physical screen.
148 	 * In the case where some attributes are being faked
149 	 * we get rid of the attributes being asked for and just have
150 	 * STANDOUT mode set.  Therefore, if STANDOUT and UNDERLINE were
151 	 * on the screen but UNDERLINE was being faked to STANDOUT; and
152 	 * the new mode is just UNDERLINE, we will get rid of any faked
153 	 * modes and be left with and oldmode of STANDOUT and a new mode
154 	 * of STANDOUT, in which case the check for newmode and oldmode
155 	 * being equal will be true.
156 	 *
157 	 *
158 	 * This test is similar to the concept explained above.
159 	 * counter is the maximum attributes allowed on a terminal.
160 	 * For instance, on an hp/tvi950 without set_attributes
161 	 * the last video sequence sent will be the one the terminal
162 	 * will be in (on that spot).  Therefore, in setupterm.c
163 	 * if ceol_standout_glitch or magic_cookie_glitch is set
164 	 * max_attributes is set to 1.  This is because on those terminals
165 	 * only one attribute can be on at once.  So, we pre-process the
166 	 * oldmode and the newmode and only leave the bits that are
167 	 * significant.  In other words, if on an hp you ask for STANDOUT
168 	 * and UNDERLINE it will become only STANDOUT since that is the
169 	 * first bit that is looked at.  If then the user goes from
170 	 * STANDOUT and UNDERLINE to STANDOUT and REVERSE the oldmode will
171 	 * become STANDOUT and the newmode will become STANDOUT.
172 	 *
173 	 * This also helps the code below in that on a hp or tvi950 only
174 	 * one bit will ever be set so that no code has to be added to
175 	 * cut out early in case two attributes were asked for.
176 	 */
177 
178 		chtype	check_faked, modes[2];
179 		int	counter = max_attributes, i, j, tempmode;
180 		int	k = (cur_term->sgr_mode == oldmode) ? 1 : 2;
181 
182 		modes[0] = newmode;
183 		modes[1] = oldmode;
184 
185 		while (k-- > 0) {
186 			if ((check_faked = (modes[k] &
187 			    cur_term->sgr_faked)) != A_NORMAL) {
188 				modes[k] &= ~check_faked;
189 				modes[k] |= A_STANDOUT;
190 			}
191 
192 			if ((j = counter) >= 0) {
193 				tempmode = A_NORMAL;
194 				if (j > 0) {
195 					for (i = 0; i < NUM_ATTRIBUTES; i++) {
196 						if (modes[k] &
197 						    bit_attributes[i]) {
198 							tempmode |=
199 							    bit_attributes[i];
200 							if (--j == 0)
201 								break;
202 						}
203 					}
204 				}
205 				modes[k] = tempmode;
206 			}
207 		}
208 		newmode = modes[0];
209 		oldmode = modes[1];
210 	}
211 
212 	if (newmode == oldmode)
213 		return (rc);
214 
215 #ifdef DEBUG
216 	if (outf)
217 		fprintf(outf, "vidupdate oldmode=%o, newmode=%o\n",
218 		    oldmode, newmode);
219 #endif
220 
221 	if (set_attributes) {
222 		(void) tputs(tparm(set_attributes,
223 			newmode & A_STANDOUT,
224 			newmode & A_UNDERLINE,
225 			newmode & A_REVERSE,
226 			newmode & A_BLINK,
227 			newmode & A_DIM,
228 			newmode & A_BOLD,
229 			newmode & A_INVIS,
230 			newmode & A_PROTECT,
231 			newmode & A_ALTCHARSET),
232 			1, outc);
233 		rc = -1;
234 	} else {
235 		chtype	turn_on, turn_off;
236 		int			i;
237 
238 		/*
239 		 * If we are going to turn something on anyway and we are
240 		 * on a glitchy terminal, don't bother turning it off
241 		 * since by turning something on you turn everything else off.
242 		 */
243 
244 		if ((ceol_standout_glitch || magic_cookie_glitch >= 0) &&
245 		    ((turn_on = ((oldmode ^ newmode) & newmode)) !=
246 		    A_NORMAL)) {
247 			goto turn_on_code;
248 	}
249 
250 	if ((turn_off = (oldmode & newmode) ^ oldmode) != A_NORMAL) {
251 		/*
252 		 * Check for things to turn off.
253 		 * First see if we are going to turn off something
254 		 * that doesn't have a specific turn off capability.
255 		 *
256 		 * Then check to see if, even though there may be a specific
257 		 * turn off sequence, this terminal doesn't have one or
258 		 * the turn off sequence also turns off something else.
259 		 */
260 		if ((turn_off & ~(A_ALTCHARSET | A_STANDOUT | A_UNDERLINE)) ||
261 		    (turn_off != (turn_off & cur_term->check_turn_off))) {
262 			(void) tputs(tparm_p0(exit_attribute_mode), 1, outc);
263 			rc = -1;
264 			oldmode = A_NORMAL;
265 		} else {
266 			for (i = 0; i < NUM_OF_SPECIFIC_TURN_OFFS; i++) {
267 				if (turn_off & bit_attributes[i]) {
268 					(void) tputs(tparm_p0
269 					    (cur_term->turn_off_seq[i]),
270 					    1, outc);
271 					oldmode &= ~bit_attributes[i];
272 					rc = -1;
273 				}
274 			}
275 		}
276 	}
277 
278 	if ((turn_on = ((oldmode ^ newmode) & newmode)) != A_NORMAL) {
279 turn_on_code:
280 
281 		/* Check for modes to turn on. */
282 
283 		for (i = 0; i < NUM_ATTRIBUTES; i++)
284 			if (turn_on & bit_attributes[i]) {
285 				(void) tputs(tparm_p0(cur_term->turn_on_seq[i]),
286 				    1, outc);
287 				rc = -1;
288 				/*
289 				 * Keep turning off the bit(s) that we just
290 				 * sent to the screen.  As soon as turn_on
291 				 * reaches A_NORMAL we don't have to turn
292 				 * anything else on and we can
293 				 * break out of the loop.
294 				 */
295 				if ((turn_on &= ~bit_attributes[i]) ==
296 				    A_NORMAL)
297 					break;
298 			}
299 		}
300 
301 		if (magic_cookie_glitch > 0)
302 			(void) tputs(cursor_left, 1, outc);
303 	}
304 	cur_term->sgr_mode = newmode;
305 	return (rc);
306 }
307 
308 
309 void
310 _change_color(short newcolor, int (*outc)(char))
311 {
312 #ifndef PC6300PLUS
313 	{
314 	_Color_pair *ptp = cur_term->_pairs_tbl;
315 	/* pairs table pointer */
316 	_Color_pair *cur_pair = &cur_term->_cur_pair;
317 
318 	/* MORE: we may have to change some stuff, depending on whether */
319 	/* HP terminals  will be changing the background, or not	*/
320 
321 	if (newcolor == 0) {
322 		if (orig_pair)
323 			(void) tputs(tparm_p0(orig_pair), 1, outc);
324 		if (set_a_background || set_a_foreground ||
325 		    set_background || set_foreground) {
326 			cur_pair->background = -1;
327 			cur_pair->foreground = -1;
328 		}
329 		return;
330 	}
331 
332 	/* if we are on HP type terminal, just send an escape sequence	*/
333 	/* to use desired color pair (we could have done some optimization: */
334 	/* check if both the foreground and background of newcolor match    */
335 	/* the ones of cur_term->_cur_pair.  but that will happen only when */
336 	/* two color pairs are defined exacly the same, and probably not    */
337 	/* worth the effort).						    */
338 
339 	if (set_color_pair)
340 		(void) tputs(tparm_p1(set_color_pair, newcolor), 1, outc);
341 
342 		/* on Tek model we can do some optimization.	*/
343 
344 	else {
345 		if (ptp[newcolor].background != cur_pair->background) {
346 			if (set_a_background)
347 				(void) tputs(tparm_p1(set_a_background,
348 				    ptp[newcolor].background), 1, outc);
349 			else if (set_background)
350 				(void) tputs(tparm_p1(set_background,
351 				    Oldcolors[ptp[newcolor].background]),
352 				    1, outc);
353 			cur_pair->background = ptp[newcolor].background;
354 		}
355 		if (ptp[newcolor].foreground != cur_pair->foreground) {
356 			if (set_a_foreground)
357 				(void) tputs(tparm_p1(set_a_foreground,
358 				    ptp[newcolor].foreground), 1, outc);
359 			else if (set_foreground)
360 				(void) tputs(tparm_p1(set_foreground,
361 				    Oldcolors[ptp[newcolor].foreground]),
362 				    1, outc);
363 			cur_pair->foreground = ptp[newcolor].foreground;
364 		}
365 	}
366 	}
367 #else
368 	{
369 	/* the following code is for PC6300 PLUS: it uses BOLD terminfo  */
370 	/* entry for turning on colors, and SGR0 for turning them off.   */
371 	/* Every time a new color-pair is used, we are forced to do an   */
372 	/* ioctl read, and the send 'enter_bold_mode' escape sequence.   */
373 	/* This could be improved  by using   */
374 	/* DIM, UNDERLINE, and REVERSE in addition to BOLD		 */
375 
376 	struct console con;
377 	_Color_pair *ptp = cur_term->_pairs_tbl;
378 	/* pairs table pointer */
379 	back = ptp[newcolor].background;
380 	fore = ptp[newcolor].foreground;
381 
382 	(void) fflush(SP->term_file);
383 	ioctl(cur_term->Filedes, CONIOGETDATA, &con);
384 #define	BOLD	4
385 	con.l[con.page].colors[BOLD] =
386 	    ((back + back + (fore > 5)) * 8 + fore) & 0177;
387 	ioctl(cur_term->Filedes, CONIOSETDATA, &con);
388 	(void) tputs(enter_bold_mode, 1, outc);
389 	}
390 #endif
391 }
392