1/****************************************************************************
2 * Copyright 2018-2020,2023 Thomas E. Dickey                                *
3 * Copyright 1998-2014,2017 Free Software Foundation, Inc.                  *
4 *                                                                          *
5 * Permission is hereby granted, free of charge, to any person obtaining a  *
6 * copy of this software and associated documentation files (the            *
7 * "Software"), to deal in the Software without restriction, including      *
8 * without limitation the rights to use, copy, modify, merge, publish,      *
9 * distribute, distribute with modifications, sublicense, and/or sell       *
10 * copies of the Software, and to permit persons to whom the Software is    *
11 * furnished to do so, subject to the following conditions:                 *
12 *                                                                          *
13 * The above copyright notice and this permission notice shall be included  *
14 * in all copies or substantial portions of the Software.                   *
15 *                                                                          *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23 *                                                                          *
24 * Except as contained in this notice, the name(s) of the above copyright   *
25 * holders shall not be used in advertising or otherwise to promote the     *
26 * sale, use or other dealings in this Software without prior written       *
27 * authorization.                                                           *
28 ****************************************************************************/
29
30/****************************************************************************
31 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
32 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
33 *     and: Thomas E. Dickey                        1996-on                 *
34 *     and: Juergen Pfeifer                         2009                    *
35 ****************************************************************************/
36
37/*
38 *	vidputs(newmode, outc)
39 *
40 *	newmode is taken to be the logical 'or' of the symbols in curses.h
41 *	representing graphic renditions.  The terminal is set to be in all of
42 *	the given modes, if possible.
43 *
44 *	if the new attribute is normal
45 *		if exit-alt-char-set exists
46 *			emit it
47 *		emit exit-attribute-mode
48 *	else if set-attributes exists
49 *		use it to set exactly what you want
50 *	else
51 *		if exit-attribute-mode exists
52 *			turn off everything
53 *		else
54 *			turn off those which can be turned off and aren't in
55 *			newmode.
56 *		turn on each mode which should be on and isn't, one by one
57 *
58 *	NOTE that this algorithm won't achieve the desired mix of attributes
59 *	in some cases, but those are probably just those cases in which it is
60 *	actually impossible, anyway, so...
61 *
62 * 	NOTE that we cannot assume that there's no interaction between color
63 *	and other attribute resets.  So each time we reset color (or other
64 *	attributes) we'll have to be prepared to restore the other.
65 */
66
67#include <curses.priv.h>
68
69#ifndef CUR
70#define CUR SP_TERMTYPE
71#endif
72
73MODULE_ID("$Id: lib_vidattr.c,v 1.79 2023/04/28 20:59:26 tom Exp $")
74
75#define doPut(mode) \
76	TPUTS_TRACE(#mode); \
77	NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx mode, 1, outc)
78
79#define TurnOn(mask, mode) \
80	if ((turn_on & mask) && mode) { \
81	    TPUTS_TRACE(#mode); \
82	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx mode, 1, outc); \
83	}
84
85#define TurnOff(mask, mode) \
86	if ((turn_off & mask) && mode) { \
87	    TPUTS_TRACE(#mode); \
88	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx mode, 1, outc); \
89	    turn_off &= ~mask; \
90	}
91
92	/* if there is no current screen, assume we *can* do color */
93#define SetColorsIf(why, old_attr) \
94	if (can_color && (why)) { \
95		int old_pair = PairNumber(old_attr); \
96		TR(TRACE_ATTRS, ("old pair = %d -- new pair = %d", old_pair, pair)); \
97		if ((pair != old_pair) \
98		 || (fix_pair0 && (pair == 0)) \
99		 || (reverse ^ ((old_attr & A_REVERSE) != 0))) { \
100		     NCURSES_SP_NAME(_nc_do_color) (NCURSES_SP_ARGx \
101				     (short) old_pair, \
102				     (short) pair, \
103				     reverse, outc); \
104		} \
105	}
106
107#define PreviousAttr _nc_prescreen.previous_attr
108
109NCURSES_EXPORT(int)
110NCURSES_SP_NAME(vidputs) (NCURSES_SP_DCLx
111			  chtype newmode,
112			  NCURSES_SP_OUTC outc)
113{
114    attr_t turn_on, turn_off;
115    int pair;
116    bool reverse = FALSE;
117    bool can_color = (SP_PARM == 0 || SP_PARM->_coloron);
118#if NCURSES_EXT_FUNCS
119    bool fix_pair0 = (SP_PARM != 0 && SP_PARM->_coloron && !SP_PARM->_default_color);
120#else
121#define fix_pair0 FALSE
122#endif
123
124    newmode &= A_ATTRIBUTES;
125
126    T((T_CALLED("vidputs(%p,%s)"), (void *) SP_PARM, _traceattr(newmode)));
127
128    if (!IsValidTIScreen(SP_PARM))
129	returnCode(ERR);
130
131    /* this allows us to go on whether or not newterm() has been called */
132    if (SP_PARM)
133	PreviousAttr = AttrOf(SCREEN_ATTRS(SP_PARM));
134
135    TR(TRACE_ATTRS, ("previous attribute was %s", _traceattr(PreviousAttr)));
136
137    if ((SP_PARM != 0)
138	&& (magic_cookie_glitch > 0)) {
139#if USE_XMC_SUPPORT
140	static const chtype table[] =
141	{
142	    A_STANDOUT,
143	    A_UNDERLINE,
144	    A_REVERSE,
145	    A_BLINK,
146	    A_DIM,
147	    A_BOLD,
148	    A_INVIS,
149	    A_PROTECT,
150#if USE_ITALIC
151	    A_ITALIC,
152#endif
153	};
154	unsigned n;
155	int used = 0;
156#ifdef max_attributes		/* not in U/Win */
157	int limit = (max_attributes <= 0) ? 1 : max_attributes;
158#else
159	int limit = 1;
160#endif
161	chtype retain = 0;
162
163	/*
164	 * Limit the number of attribute bits set in the newmode according to
165	 * the terminfo max_attributes value.
166	 */
167	for (n = 0; n < SIZEOF(table); ++n) {
168	    if ((table[n] & SP_PARM->_ok_attributes) == 0) {
169		newmode &= ~table[n];
170	    } else if ((table[n] & newmode) != 0) {
171		if (used++ >= limit) {
172		    newmode &= ~table[n];
173		    if (newmode == retain)
174			break;
175		} else {
176		    retain = newmode;
177		}
178	    }
179	}
180#else
181	newmode &= ~(SP_PARM->_xmc_suppress);
182#endif
183	TR(TRACE_ATTRS, ("suppressed attribute is %s", _traceattr(newmode)));
184    }
185
186    /*
187     * If we have a terminal that cannot combine color with video
188     * attributes, use the colors in preference.
189     */
190    if (((newmode & A_COLOR) != 0
191	 || fix_pair0)
192	&& (no_color_video > 0)) {
193	/*
194	 * If we had chosen the A_xxx definitions to correspond to the
195	 * no_color_video mask, we could simply shift it up and mask off the
196	 * attributes.  But we did not (actually copied Solaris' definitions).
197	 * However, this is still simpler/faster than a lookup table.
198	 *
199	 * The 63 corresponds to A_STANDOUT, A_UNDERLINE, A_REVERSE, A_BLINK,
200	 * A_DIM, A_BOLD which are 1:1 with no_color_video.  The bits that
201	 * correspond to A_INVIS, A_PROTECT (192) must be shifted up 1 and
202	 * A_ALTCHARSET (256) down 2 to line up.  We use the NCURSES_BITS
203	 * macro so this will work properly for the wide-character layout.
204	 */
205	unsigned value = (unsigned) no_color_video;
206	attr_t mask = NCURSES_BITS((value & 63)
207				   | ((value & 192) << 1)
208				   | ((value & 256) >> 2), 8);
209
210	if ((mask & A_REVERSE) != 0
211	    && (newmode & A_REVERSE) != 0) {
212	    reverse = TRUE;
213	    mask &= ~A_REVERSE;
214	}
215	newmode &= ~mask;
216    }
217
218    if (newmode == PreviousAttr)
219	returnCode(OK);
220
221    pair = PairNumber(newmode);
222
223    if (reverse) {
224	newmode &= ~A_REVERSE;
225    }
226
227    turn_off = (~newmode & PreviousAttr) & ALL_BUT_COLOR;
228    turn_on = (newmode & ~(PreviousAttr & TPARM_ATTR)) & ALL_BUT_COLOR;
229
230    SetColorsIf(((pair == 0) && !fix_pair0), PreviousAttr);
231
232    if (newmode == A_NORMAL) {
233	if ((PreviousAttr & A_ALTCHARSET) && exit_alt_charset_mode) {
234	    doPut(exit_alt_charset_mode);
235	    PreviousAttr &= ~A_ALTCHARSET;
236	}
237	if (PreviousAttr) {
238	    if (exit_attribute_mode) {
239		doPut(exit_attribute_mode);
240	    } else {
241		if (!SP_PARM || SP_PARM->_use_rmul) {
242		    TurnOff(A_UNDERLINE, exit_underline_mode);
243		}
244		if (!SP_PARM || SP_PARM->_use_rmso) {
245		    TurnOff(A_STANDOUT, exit_standout_mode);
246		}
247#if USE_ITALIC
248		if (!SP_PARM || SP_PARM->_use_ritm) {
249		    TurnOff(A_ITALIC, exit_italics_mode);
250		}
251#endif
252		(void) turn_off;
253	    }
254	    PreviousAttr &= ALL_BUT_COLOR;
255	}
256
257	SetColorsIf((pair != 0) || fix_pair0, PreviousAttr);
258    } else if (set_attributes) {
259	if (turn_on || turn_off) {
260	    TPUTS_TRACE("set_attributes");
261	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
262				    TIPARM_9(set_attributes,
263					     (newmode & A_STANDOUT) != 0,
264					     (newmode & A_UNDERLINE) != 0,
265					     (newmode & A_REVERSE) != 0,
266					     (newmode & A_BLINK) != 0,
267					     (newmode & A_DIM) != 0,
268					     (newmode & A_BOLD) != 0,
269					     (newmode & A_INVIS) != 0,
270					     (newmode & A_PROTECT) != 0,
271					     (newmode & A_ALTCHARSET) != 0),
272				    1, outc);
273	    PreviousAttr &= ALL_BUT_COLOR;
274	}
275#if USE_ITALIC
276	if (!SP_PARM || SP_PARM->_use_ritm) {
277	    if (turn_on & A_ITALIC) {
278		TurnOn(A_ITALIC, enter_italics_mode);
279	    } else if (turn_off & A_ITALIC) {
280		TurnOff(A_ITALIC, exit_italics_mode);
281	    }
282	    (void) turn_off;
283	}
284#endif
285	SetColorsIf((pair != 0) || fix_pair0, PreviousAttr);
286    } else {
287
288	TR(TRACE_ATTRS, ("turning %s off", _traceattr(turn_off)));
289
290	TurnOff(A_ALTCHARSET, exit_alt_charset_mode);
291
292	if (!SP_PARM || SP_PARM->_use_rmul) {
293	    TurnOff(A_UNDERLINE, exit_underline_mode);
294	}
295
296	if (!SP_PARM || SP_PARM->_use_rmso) {
297	    TurnOff(A_STANDOUT, exit_standout_mode);
298	}
299#if USE_ITALIC
300	if (!SP_PARM || SP_PARM->_use_ritm) {
301	    TurnOff(A_ITALIC, exit_italics_mode);
302	}
303#endif
304	if (turn_off && exit_attribute_mode) {
305	    doPut(exit_attribute_mode);
306	    turn_on |= (newmode & ALL_BUT_COLOR);
307	    PreviousAttr &= ALL_BUT_COLOR;
308	}
309	SetColorsIf((pair != 0) || fix_pair0, PreviousAttr);
310
311	TR(TRACE_ATTRS, ("turning %s on", _traceattr(turn_on)));
312	/* *INDENT-OFF* */
313	TurnOn(A_ALTCHARSET,	enter_alt_charset_mode);
314	TurnOn(A_BLINK,		enter_blink_mode);
315	TurnOn(A_BOLD,		enter_bold_mode);
316	TurnOn(A_DIM,		enter_dim_mode);
317	TurnOn(A_REVERSE,	enter_reverse_mode);
318	TurnOn(A_STANDOUT,	enter_standout_mode);
319	TurnOn(A_PROTECT,	enter_protected_mode);
320	TurnOn(A_INVIS,		enter_secure_mode);
321	TurnOn(A_UNDERLINE,	enter_underline_mode);
322#if USE_ITALIC
323	TurnOn(A_ITALIC,	enter_italics_mode);
324#endif
325#if USE_WIDEC_SUPPORT && defined(enter_horizontal_hl_mode)
326	TurnOn(A_HORIZONTAL,	enter_horizontal_hl_mode);
327	TurnOn(A_LEFT,		enter_left_hl_mode);
328	TurnOn(A_LOW,		enter_low_hl_mode);
329	TurnOn(A_RIGHT,		enter_right_hl_mode);
330	TurnOn(A_TOP,		enter_top_hl_mode);
331	TurnOn(A_VERTICAL,	enter_vertical_hl_mode);
332#endif
333	/* *INDENT-ON* */
334    }
335
336    if (reverse)
337	newmode |= A_REVERSE;
338
339    if (SP_PARM)
340	SetAttr(SCREEN_ATTRS(SP_PARM), newmode);
341    else
342	PreviousAttr = newmode;
343
344    returnCode(OK);
345}
346
347#if NCURSES_SP_FUNCS
348NCURSES_EXPORT(int)
349vidputs(chtype newmode, NCURSES_OUTC outc)
350{
351    SetSafeOutcWrapper(outc);
352    return NCURSES_SP_NAME(vidputs) (CURRENT_SCREEN,
353				     newmode,
354				     _nc_outc_wrapper);
355}
356#endif
357
358NCURSES_EXPORT(int)
359NCURSES_SP_NAME(vidattr) (NCURSES_SP_DCLx chtype newmode)
360{
361    T((T_CALLED("vidattr(%p,%s)"), (void *) SP_PARM, _traceattr(newmode)));
362    returnCode(NCURSES_SP_NAME(vidputs) (NCURSES_SP_ARGx
363					 newmode,
364					 NCURSES_SP_NAME(_nc_putchar)));
365}
366
367#if NCURSES_SP_FUNCS
368NCURSES_EXPORT(int)
369vidattr(chtype newmode)
370{
371    return NCURSES_SP_NAME(vidattr) (CURRENT_SCREEN, newmode);
372}
373#endif
374
375NCURSES_EXPORT(chtype)
376NCURSES_SP_NAME(termattrs) (NCURSES_SP_DCL0)
377{
378    chtype attrs = A_NORMAL;
379
380    T((T_CALLED("termattrs(%p)"), (void *) SP_PARM));
381
382    if (HasTerminal(SP_PARM)) {
383#ifdef USE_TERM_DRIVER
384	attrs = CallDriver(SP_PARM, td_conattr);
385#else /* ! USE_TERM_DRIVER */
386
387	if (enter_alt_charset_mode)
388	    attrs |= A_ALTCHARSET;
389
390	if (enter_blink_mode)
391	    attrs |= A_BLINK;
392
393	if (enter_bold_mode)
394	    attrs |= A_BOLD;
395
396	if (enter_dim_mode)
397	    attrs |= A_DIM;
398
399	if (enter_reverse_mode)
400	    attrs |= A_REVERSE;
401
402	if (enter_standout_mode)
403	    attrs |= A_STANDOUT;
404
405	if (enter_protected_mode)
406	    attrs |= A_PROTECT;
407
408	if (enter_secure_mode)
409	    attrs |= A_INVIS;
410
411	if (enter_underline_mode)
412	    attrs |= A_UNDERLINE;
413
414	if (SP_PARM->_coloron)
415	    attrs |= A_COLOR;
416
417#if USE_ITALIC
418	if (enter_italics_mode)
419	    attrs |= A_ITALIC;
420#endif
421
422#endif /* USE_TERM_DRIVER */
423    }
424    returnChtype(attrs);
425}
426
427#if NCURSES_SP_FUNCS
428NCURSES_EXPORT(chtype)
429termattrs(void)
430{
431    return NCURSES_SP_NAME(termattrs) (CURRENT_SCREEN);
432}
433#endif
434