xref: /openbsd/lib/libcurses/base/lib_addch.c (revision 404b540a)
1 /*	$OpenBSD: lib_addch.c,v 1.4 2001/01/22 18:01:37 millert Exp $	*/
2 
3 /****************************************************************************
4  * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc.              *
5  *                                                                          *
6  * Permission is hereby granted, free of charge, to any person obtaining a  *
7  * copy of this software and associated documentation files (the            *
8  * "Software"), to deal in the Software without restriction, including      *
9  * without limitation the rights to use, copy, modify, merge, publish,      *
10  * distribute, distribute with modifications, sublicense, and/or sell       *
11  * copies of the Software, and to permit persons to whom the Software is    *
12  * furnished to do so, subject to the following conditions:                 *
13  *                                                                          *
14  * The above copyright notice and this permission notice shall be included  *
15  * in all copies or substantial portions of the Software.                   *
16  *                                                                          *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24  *                                                                          *
25  * Except as contained in this notice, the name(s) of the above copyright   *
26  * holders shall not be used in advertising or otherwise to promote the     *
27  * sale, use or other dealings in this Software without prior written       *
28  * authorization.                                                           *
29  ****************************************************************************/
30 
31 /****************************************************************************
32  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34  ****************************************************************************/
35 
36 /*
37 **	lib_addch.c
38 **
39 **	The routine waddch().
40 **
41 */
42 
43 #include <curses.priv.h>
44 #include <ctype.h>
45 
46 MODULE_ID("$From: lib_addch.c,v 1.47 2000/12/10 02:43:26 tom Exp $")
47 
48 /*
49  * Ugly microtweaking alert.  Everything from here to end of module is
50  * likely to be speed-critical -- profiling data sure says it is!
51  * Most of the important screen-painting functions are shells around
52  * waddch().  So we make every effort to reduce function-call overhead
53  * by inlining stuff, even at the cost of making wrapped copies for
54  * export.  Also we supply some internal versions that don't call the
55  * window sync hook, for use by string-put functions.
56  */
57 
58 /* Return bit mask for clearing color pair number if given ch has color */
59 #define COLOR_MASK(ch) (~(chtype)((ch)&A_COLOR?A_COLOR:0))
60 
61 static inline chtype
62 render_char(WINDOW *win, chtype ch)
63 /* compute a rendition of the given char correct for the current context */
64 {
65     chtype a = win->_attrs;
66 
67     if (ch == ' ') {
68 	/* color in attrs has precedence over bkgd */
69 	ch = a | (win->_bkgd & COLOR_MASK(a));
70     } else {
71 	/* color in attrs has precedence over bkgd */
72 	a |= (win->_bkgd & A_ATTRIBUTES) & COLOR_MASK(a);
73 	/* color in ch has precedence */
74 	ch |= (a & COLOR_MASK(ch));
75     }
76 
77     TR(TRACE_VIRTPUT, ("bkg = %lx, attrs = %lx -> ch = %lx", win->_bkgd,
78 		       win->_attrs, ch));
79 
80     return (ch);
81 }
82 
83 NCURSES_EXPORT(chtype)
84 _nc_background
85 (WINDOW *win)
86 /* make render_char() visible while still allowing us to inline it below */
87 {
88     return (win->_bkgd);
89 }
90 
91 NCURSES_EXPORT(chtype)
92 _nc_render
93 (WINDOW *win, chtype ch)
94 /* make render_char() visible while still allowing us to inline it below */
95 {
96     return render_char(win, ch);
97 }
98 
99 /* check if position is legal; if not, return error */
100 #ifndef NDEBUG			/* treat this like an assertion */
101 #define CHECK_POSITION(win, x, y) \
102 	if (y > win->_maxy \
103 	 || x > win->_maxx \
104 	 || y < 0 \
105 	 || x < 0) { \
106 		TR(TRACE_VIRTPUT, ("Alert! Win=%p _curx = %d, _cury = %d " \
107 				   "(_maxx = %d, _maxy = %d)", win, x, y, \
108 				   win->_maxx, win->_maxy)); \
109 		return(ERR); \
110 	}
111 #else
112 #define CHECK_POSITION(win, x, y)	/* nothing */
113 #endif
114 
115 static inline int
116 waddch_literal(WINDOW *win, chtype ch)
117 {
118     int x;
119     struct ldat *line;
120 
121     x = win->_curx;
122 
123     CHECK_POSITION(win, x, win->_cury);
124 
125     /*
126      * If we're trying to add a character at the lower-right corner more
127      * than once, fail.  (Moving the cursor will clear the flag).
128      */
129 #if 0				/* Solaris 2.6 allows updating the corner more than once */
130     if (win->_flags & _WRAPPED) {
131 	if (x >= win->_maxx)
132 	    return (ERR);
133 	win->_flags &= ~_WRAPPED;
134     }
135 #endif
136 
137     ch = render_char(win, ch);
138     TR(TRACE_VIRTPUT, ("win attr = %s", _traceattr(win->_attrs)));
139 
140     line = win->_line + win->_cury;
141 
142     CHANGED_CELL(line, x);
143 
144     line->text[x++] = ch;
145 
146     TR(TRACE_VIRTPUT, ("(%d, %d) = %s", win->_cury, x, _tracechtype(ch)));
147     if (x > win->_maxx) {
148 	/*
149 	 * The _WRAPPED flag is useful only for telling an application that
150 	 * we've just wrapped the cursor.  We don't do anything with this flag
151 	 * except set it when wrapping, and clear it whenever we move the
152 	 * cursor.  If we try to wrap at the lower-right corner of a window, we
153 	 * cannot move the cursor (since that wouldn't be legal).  So we return
154 	 * an error (which is what SVr4 does).  Unlike SVr4, we can
155 	 * successfully add a character to the lower-right corner (Solaris 2.6
156 	 * does this also, however).
157 	 */
158 	win->_flags |= _WRAPPED;
159 	if (++win->_cury > win->_regbottom) {
160 	    win->_cury = win->_regbottom;
161 	    win->_curx = win->_maxx;
162 	    if (!win->_scroll)
163 		return (ERR);
164 	    scroll(win);
165 	}
166 	win->_curx = 0;
167 	return (OK);
168     }
169     win->_curx = x;
170     return OK;
171 }
172 
173 static inline int
174 waddch_nosync(WINDOW *win, const chtype ch)
175 /* the workhorse function -- add a character to the given window */
176 {
177     int x, y;
178     chtype t = 0;
179     const char *s = 0;
180 
181     if ((ch & A_ALTCHARSET)
182 	|| ((t = TextOf(ch)) > 127)
183 	|| ((s = unctrl(t))[1] == 0))
184 	return waddch_literal(win, ch);
185 
186     x = win->_curx;
187     y = win->_cury;
188 
189     switch (t) {
190     case '\t':
191 	x += (TABSIZE - (x % TABSIZE));
192 
193 	/*
194 	 * Space-fill the tab on the bottom line so that we'll get the
195 	 * "correct" cursor position.
196 	 */
197 	if ((!win->_scroll && (y == win->_regbottom))
198 	    || (x <= win->_maxx)) {
199 	    chtype blank = (' ' | AttrOf(ch));
200 	    while (win->_curx < x) {
201 		if (waddch_literal(win, blank) == ERR)
202 		    return (ERR);
203 	    }
204 	    break;
205 	} else {
206 	    wclrtoeol(win);
207 	    win->_flags |= _WRAPPED;
208 	    if (++y > win->_regbottom) {
209 		x = win->_maxx;
210 		y--;
211 		if (win->_scroll) {
212 		    scroll(win);
213 		    x = 0;
214 		}
215 	    } else {
216 		x = 0;
217 	    }
218 	}
219 	break;
220     case '\n':
221 	wclrtoeol(win);
222 	if (++y > win->_regbottom) {
223 	    y--;
224 	    if (win->_scroll)
225 		scroll(win);
226 	    else
227 		return (ERR);
228 	}
229 	/* FALLTHRU */
230     case '\r':
231 	x = 0;
232 	win->_flags &= ~_WRAPPED;
233 	break;
234     case '\b':
235 	if (x == 0)
236 	    return (OK);
237 	x--;
238 	win->_flags &= ~_WRAPPED;
239 	break;
240     default:
241 	while (*s)
242 	    if (waddch_literal(win, (*s++) | AttrOf(ch)) == ERR)
243 		return ERR;
244 	return (OK);
245     }
246 
247     win->_curx = x;
248     win->_cury = y;
249 
250     return (OK);
251 }
252 
253 NCURSES_EXPORT(int)
254 _nc_waddch_nosync
255 (WINDOW *win, const chtype c)
256 /* export copy of waddch_nosync() so the string-put functions can use it */
257 {
258     return (waddch_nosync(win, c));
259 }
260 
261 /*
262  * The versions below call _nc_synhook().  We wanted to avoid this in the
263  * version exported for string puts; they'll call _nc_synchook once at end
264  * of run.
265  */
266 
267 /* These are actual entry points */
268 
269 NCURSES_EXPORT(int)
270 waddch
271 (WINDOW *win, const chtype ch)
272 {
273     int code = ERR;
274 
275     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), win,
276 				      _tracechtype(ch)));
277 
278     if (win && (waddch_nosync(win, ch) != ERR)) {
279 	_nc_synchook(win);
280 	code = OK;
281     }
282 
283     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
284     return (code);
285 }
286 
287 NCURSES_EXPORT(int)
288 wechochar
289 (WINDOW *win, const chtype ch)
290 {
291     int code = ERR;
292 
293     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win,
294 				      _tracechtype(ch)));
295 
296     if (win && (waddch_nosync(win, ch) != ERR)) {
297 	bool save_immed = win->_immed;
298 	win->_immed = TRUE;
299 	_nc_synchook(win);
300 	win->_immed = save_immed;
301 	code = OK;
302     }
303     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
304     return (code);
305 }
306