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