1/* $OpenBSD: lib_vid_attr.c,v 1.2 2023/10/17 09:52:09 nicm Exp $ */
2
3/****************************************************************************
4 * Copyright 2018-2020,2023 Thomas E. Dickey                                *
5 * Copyright 2002-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: Thomas E. Dickey                                                *
34 ****************************************************************************/
35
36#include <curses.priv.h>
37
38#ifndef CUR
39#define CUR SP_TERMTYPE
40#endif
41
42MODULE_ID("$Id: lib_vid_attr.c,v 1.2 2023/10/17 09:52:09 nicm Exp $")
43
44#define doPut(mode) \
45	TPUTS_TRACE(#mode); \
46	NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx mode, 1, outc)
47
48#define TurnOn(mask, mode) \
49	if ((turn_on & mask) && mode) { \
50	    TPUTS_TRACE(#mode); \
51	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx mode, 1, outc); \
52	}
53
54#define TurnOff(mask, mode) \
55	if ((turn_off & mask) && mode) { \
56	    TPUTS_TRACE(#mode); \
57	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx mode, 1, outc); \
58	    turn_off &= ~mask; \
59	}
60
61	/* if there is no current screen, assume we *can* do color */
62#define SetColorsIf(why, old_attr, old_pair) \
63	if (can_color && (why)) { \
64		TR(TRACE_ATTRS, ("old pair = %d -- new pair = %d", old_pair, color_pair)); \
65		if ((color_pair != old_pair) \
66		 || (fix_pair0 && (color_pair == 0)) \
67		 || (reverse ^ ((old_attr & A_REVERSE) != 0))) { \
68		    NCURSES_SP_NAME(_nc_do_color) (NCURSES_SP_ARGx \
69						   old_pair, color_pair, \
70						   reverse, outc); \
71		} \
72	}
73
74#define set_color(mode, pair) \
75	mode &= ALL_BUT_COLOR; \
76	mode |= (attr_t) ColorPair(pair)
77
78NCURSES_EXPORT(int)
79NCURSES_SP_NAME(vid_puts) (NCURSES_SP_DCLx
80			   attr_t newmode,
81			   NCURSES_PAIRS_T pair_arg,
82			   void *opts OPTIONAL_PAIR,
83			   NCURSES_SP_OUTC outc)
84{
85    int color_pair = pair_arg;
86#if NCURSES_EXT_COLORS
87    static attr_t previous_attr = A_NORMAL;
88    static int previous_pair = 0;
89
90    attr_t turn_on, turn_off;
91    bool reverse = FALSE;
92    bool can_color = (SP_PARM == 0 || SP_PARM->_coloron);
93#if NCURSES_EXT_FUNCS
94    bool fix_pair0 = (SP_PARM != 0 && SP_PARM->_coloron && !SP_PARM->_default_color);
95#else
96#define fix_pair0 FALSE
97#endif
98
99    if (!IsValidTIScreen(SP_PARM))
100	returnCode(ERR);
101
102    newmode &= A_ATTRIBUTES;
103    set_extended_pair(opts, color_pair);
104    T((T_CALLED("vid_puts(%s,%d)"), _traceattr(newmode), color_pair));
105
106    /* this allows us to go on whether or not newterm() has been called */
107    if (SP_PARM) {
108	previous_attr = AttrOf(SCREEN_ATTRS(SP_PARM));
109	previous_pair = GetPair(SCREEN_ATTRS(SP_PARM));
110    }
111
112    TR(TRACE_ATTRS, ("previous attribute was %s, %d",
113		     _traceattr(previous_attr), previous_pair));
114
115#if !USE_XMC_SUPPORT
116    if ((SP_PARM != 0)
117	&& (magic_cookie_glitch > 0))
118	newmode &= ~(SP_PARM->_xmc_suppress);
119#endif
120
121    /*
122     * If we have a terminal that cannot combine color with video
123     * attributes, use the colors in preference.
124     */
125    if ((color_pair != 0
126	 || fix_pair0)
127	&& (no_color_video > 0)) {
128	/*
129	 * If we had chosen the A_xxx definitions to correspond to the
130	 * no_color_video mask, we could simply shift it up and mask off the
131	 * attributes.  But we did not (actually copied Solaris' definitions).
132	 * However, this is still simpler/faster than a lookup table.
133	 *
134	 * The 63 corresponds to A_STANDOUT, A_UNDERLINE, A_REVERSE, A_BLINK,
135	 * A_DIM, A_BOLD which are 1:1 with no_color_video.  The bits that
136	 * correspond to A_INVIS, A_PROTECT (192) must be shifted up 1 and
137	 * A_ALTCHARSET (256) down 2 to line up.  We use the NCURSES_BITS
138	 * macro so this will work properly for the wide-character layout.
139	 */
140	unsigned value = (unsigned) no_color_video;
141	attr_t mask = NCURSES_BITS((value & 63)
142				   | ((value & 192) << 1)
143				   | ((value & 256) >> 2), 8);
144
145	if ((mask & A_REVERSE) != 0
146	    && (newmode & A_REVERSE) != 0) {
147	    reverse = TRUE;
148	    mask &= ~A_REVERSE;
149	}
150	newmode &= ~mask;
151    }
152
153    if (newmode == previous_attr
154	&& color_pair == previous_pair)
155	returnCode(OK);
156
157    if (reverse) {
158	newmode &= ~A_REVERSE;
159    }
160
161    turn_off = (~newmode & previous_attr) & ALL_BUT_COLOR;
162    turn_on = (newmode & ~(previous_attr & TPARM_ATTR)) & ALL_BUT_COLOR;
163
164    SetColorsIf(((color_pair == 0) && !fix_pair0), previous_attr, previous_pair);
165
166    if (newmode == A_NORMAL) {
167	if ((previous_attr & A_ALTCHARSET) && exit_alt_charset_mode) {
168	    doPut(exit_alt_charset_mode);
169	    previous_attr &= ~A_ALTCHARSET;
170	}
171	if (previous_attr) {
172	    if (exit_attribute_mode) {
173		doPut(exit_attribute_mode);
174	    } else {
175		if (!SP_PARM || SP_PARM->_use_rmul) {
176		    TurnOff(A_UNDERLINE, exit_underline_mode);
177		}
178		if (!SP_PARM || SP_PARM->_use_rmso) {
179		    TurnOff(A_STANDOUT, exit_standout_mode);
180		}
181#if USE_ITALIC
182		if (!SP_PARM || SP_PARM->_use_ritm) {
183		    TurnOff(A_ITALIC, exit_italics_mode);
184		}
185#endif
186		(void) turn_off;
187	    }
188	    previous_attr &= ALL_BUT_COLOR;
189	    previous_pair = 0;
190	}
191
192	SetColorsIf((color_pair != 0) || fix_pair0, previous_attr, previous_pair);
193    } else if (set_attributes) {
194	if (turn_on || turn_off) {
195	    TPUTS_TRACE("set_attributes");
196	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
197				    TIPARM_9(set_attributes,
198					       (newmode & A_STANDOUT) != 0,
199					       (newmode & A_UNDERLINE) != 0,
200					       (newmode & A_REVERSE) != 0,
201					       (newmode & A_BLINK) != 0,
202					       (newmode & A_DIM) != 0,
203					       (newmode & A_BOLD) != 0,
204					       (newmode & A_INVIS) != 0,
205					       (newmode & A_PROTECT) != 0,
206					       (newmode & A_ALTCHARSET) != 0),
207				    1, outc);
208	    previous_attr &= ALL_BUT_COLOR;
209	    previous_pair = 0;
210	}
211#if USE_ITALIC
212	if (!SP_PARM || SP_PARM->_use_ritm) {
213	    if (turn_on & A_ITALIC) {
214		TurnOn(A_ITALIC, enter_italics_mode);
215	    } else if (turn_off & A_ITALIC) {
216		TurnOff(A_ITALIC, exit_italics_mode);
217	    }
218	    (void) turn_off;
219	}
220#endif
221	SetColorsIf((color_pair != 0) || fix_pair0, previous_attr, previous_pair);
222    } else {
223
224	TR(TRACE_ATTRS, ("turning %s off", _traceattr(turn_off)));
225
226	TurnOff(A_ALTCHARSET, exit_alt_charset_mode);
227
228	if (!SP_PARM || SP_PARM->_use_rmul) {
229	    TurnOff(A_UNDERLINE, exit_underline_mode);
230	}
231
232	if (!SP_PARM || SP_PARM->_use_rmso) {
233	    TurnOff(A_STANDOUT, exit_standout_mode);
234	}
235#if USE_ITALIC
236	if (!SP_PARM || SP_PARM->_use_ritm) {
237	    TurnOff(A_ITALIC, exit_italics_mode);
238	}
239#endif
240	if (turn_off && exit_attribute_mode) {
241	    doPut(exit_attribute_mode);
242	    turn_on |= (newmode & ALL_BUT_COLOR);
243	    previous_attr &= ALL_BUT_COLOR;
244	    previous_pair = 0;
245	}
246	SetColorsIf((color_pair != 0) || fix_pair0, previous_attr, previous_pair);
247
248	TR(TRACE_ATTRS, ("turning %s on", _traceattr(turn_on)));
249	/* *INDENT-OFF* */
250	TurnOn(A_ALTCHARSET,	enter_alt_charset_mode);
251	TurnOn(A_BLINK,		enter_blink_mode);
252	TurnOn(A_BOLD,		enter_bold_mode);
253	TurnOn(A_DIM,		enter_dim_mode);
254	TurnOn(A_REVERSE,	enter_reverse_mode);
255	TurnOn(A_STANDOUT,	enter_standout_mode);
256	TurnOn(A_PROTECT,	enter_protected_mode);
257	TurnOn(A_INVIS,		enter_secure_mode);
258	TurnOn(A_UNDERLINE,	enter_underline_mode);
259#if USE_ITALIC
260	TurnOn(A_ITALIC,	enter_italics_mode);
261#endif
262#if USE_WIDEC_SUPPORT && defined(enter_horizontal_hl_mode)
263	TurnOn(A_HORIZONTAL,	enter_horizontal_hl_mode);
264	TurnOn(A_LEFT,		enter_left_hl_mode);
265	TurnOn(A_LOW,		enter_low_hl_mode);
266	TurnOn(A_RIGHT,		enter_right_hl_mode);
267	TurnOn(A_TOP,		enter_top_hl_mode);
268	TurnOn(A_VERTICAL,	enter_vertical_hl_mode);
269#endif
270	/* *INDENT-ON* */
271    }
272
273    if (reverse)
274	newmode |= A_REVERSE;
275
276    if (SP_PARM) {
277	SetAttr(SCREEN_ATTRS(SP_PARM), newmode);
278	SetPair(SCREEN_ATTRS(SP_PARM), color_pair);
279    } else {
280	previous_attr = newmode;
281	previous_pair = color_pair;
282    }
283
284    returnCode(OK);
285#else
286    T((T_CALLED("vid_puts(%s,%d)"), _traceattr(newmode), color_pair));
287    (void) opts;
288    set_color(newmode, color_pair);
289    returnCode(NCURSES_SP_NAME(vidputs) (NCURSES_SP_ARGx newmode, outc));
290#endif
291}
292
293#if NCURSES_SP_FUNCS
294NCURSES_EXPORT(int)
295vid_puts(attr_t newmode,
296	 NCURSES_PAIRS_T pair_arg,
297	 void *opts GCC_UNUSED,
298	 NCURSES_OUTC outc)
299{
300    SetSafeOutcWrapper(outc);
301    return NCURSES_SP_NAME(vid_puts) (CURRENT_SCREEN,
302				      newmode,
303				      pair_arg,
304				      opts,
305				      _nc_outc_wrapper);
306}
307#endif
308
309#undef vid_attr
310NCURSES_EXPORT(int)
311NCURSES_SP_NAME(vid_attr) (NCURSES_SP_DCLx
312			   attr_t newmode,
313			   NCURSES_PAIRS_T pair_arg,
314			   void *opts)
315{
316    T((T_CALLED("vid_attr(%s,%d)"), _traceattr(newmode), (int) pair_arg));
317    returnCode(NCURSES_SP_NAME(vid_puts) (NCURSES_SP_ARGx
318					  newmode,
319					  pair_arg,
320					  opts,
321					  NCURSES_SP_NAME(_nc_putchar)));
322}
323
324#if NCURSES_SP_FUNCS
325NCURSES_EXPORT(int)
326vid_attr(attr_t newmode, NCURSES_PAIRS_T pair_arg, void *opts)
327{
328    return NCURSES_SP_NAME(vid_attr) (CURRENT_SCREEN, newmode, pair_arg, opts);
329}
330#endif
331
332/*
333 * This implementation uses the same mask values for A_xxx and WA_xxx, so
334 * we can use termattrs() for part of the logic.
335 */
336NCURSES_EXPORT(attr_t)
337NCURSES_SP_NAME(term_attrs) (NCURSES_SP_DCL0)
338{
339    attr_t attrs = 0;
340
341    T((T_CALLED("term_attrs()")));
342    if (SP_PARM) {
343	attrs = NCURSES_SP_NAME(termattrs) (NCURSES_SP_ARG);
344
345#if USE_WIDEC_SUPPORT && defined(enter_horizontal_hl_mode)
346	/* these are only supported for wide-character mode */
347	if (enter_horizontal_hl_mode)
348	    attrs |= WA_HORIZONTAL;
349	if (enter_left_hl_mode)
350	    attrs |= WA_LEFT;
351	if (enter_low_hl_mode)
352	    attrs |= WA_LOW;
353	if (enter_right_hl_mode)
354	    attrs |= WA_RIGHT;
355	if (enter_top_hl_mode)
356	    attrs |= WA_TOP;
357	if (enter_vertical_hl_mode)
358	    attrs |= WA_VERTICAL;
359#endif
360    }
361
362    returnAttr(attrs);
363}
364
365#if NCURSES_SP_FUNCS
366NCURSES_EXPORT(attr_t)
367term_attrs(void)
368{
369    return NCURSES_SP_NAME(term_attrs) (CURRENT_SCREEN);
370}
371#endif
372