xref: /openbsd-src/lib/libcurses/base/lib_addch.c (revision 92dd1ec0a89df25171bc5d61a3d95ea1a68cef0b)
1 /*	$OpenBSD: lib_addch.c,v 1.1 1999/01/18 19:09:34 millert Exp $	*/
2 
3 /****************************************************************************
4  * Copyright (c) 1998 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.41 1998/06/28 00:10:21 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 render_char(WINDOW *win, chtype ch)
62 /* compute a rendition of the given char correct for the current context */
63 {
64 	chtype a = win->_attrs;
65 
66 	if (ch == ' ')
67 	{
68 		/* color in attrs has precedence over bkgd */
69 		ch = a | (win->_bkgd & COLOR_MASK(a));
70 	}
71 	else
72 	{
73 		/* color in attrs has precedence over bkgd */
74 		a |= (win->_bkgd & A_ATTRIBUTES) & COLOR_MASK(a);
75 		/* color in ch has precedence */
76 		ch |= (a & COLOR_MASK(ch));
77 	}
78 
79 	TR(TRACE_VIRTPUT, ("bkg = %lx, attrs = %lx -> ch = %lx", win->_bkgd,
80 		win->_attrs, ch));
81 
82 	return(ch);
83 }
84 
85 chtype _nc_background(WINDOW *win)
86 /* make render_char() visible while still allowing us to inline it below */
87 {
88 	return (win->_bkgd);
89 }
90 
91 chtype _nc_render(WINDOW *win, chtype ch)
92 /* make render_char() visible while still allowing us to inline it below */
93 {
94 	return render_char(win, ch);
95 }
96 
97 /* check if position is legal; if not, return error */
98 #ifndef NDEBUG			/* treat this like an assertion */
99 #define CHECK_POSITION(win, x, y) \
100 	if (y > win->_maxy \
101 	 || x > win->_maxx \
102 	 || y < 0 \
103 	 || x < 0) { \
104 		TR(TRACE_VIRTPUT, ("Alert! Win=%p _curx = %d, _cury = %d " \
105 				   "(_maxx = %d, _maxy = %d)", win, x, y, \
106 				   win->_maxx, win->_maxy)); \
107 		return(ERR); \
108 	}
109 #else
110 #define CHECK_POSITION(win, x, y) /* nothing */
111 #endif
112 
113 static inline
114 int waddch_literal(WINDOW *win, chtype ch)
115 {
116 	int x;
117 	struct ldat *line;
118 
119 	x = win->_curx;
120 
121 	CHECK_POSITION(win, x, win->_cury);
122 
123 	/*
124 	 * If we're trying to add a character at the lower-right corner more
125 	 * than once, fail.  (Moving the cursor will clear the flag).
126 	 */
127 	if (win->_flags & _WRAPPED) {
128 		if (x >= win->_maxx)
129 			return (ERR);
130 		win->_flags &= ~_WRAPPED;
131 	}
132 
133 	ch = render_char(win, ch);
134 	TR(TRACE_VIRTPUT, ("win attr = %s", _traceattr(win->_attrs)));
135 
136 	line = win->_line+win->_cury;
137 
138 	CHANGED_CELL(line,x);
139 
140 	line->text[x++] = ch;
141 
142 	TR(TRACE_VIRTPUT, ("(%d, %d) = %s", win->_cury, x, _tracechtype(ch)));
143 	if (x > win->_maxx) {
144 		/*
145 		 * The _WRAPPED flag is useful only for telling an application
146 		 * that we've just wrapped the cursor.  We don't do anything
147 		 * with this flag except set it when wrapping, and clear it
148 		 * whenever we move the cursor.  If we try to wrap at the
149 		 * lower-right corner of a window, we cannot move the cursor
150 		 * (since that wouldn't be legal).  So we return an error
151 		 * (which is what SVr4 does).  Unlike SVr4, we can successfully
152 		 * add a character to the lower-right corner.
153 		 */
154 		win->_flags |= _WRAPPED;
155 		if (++win->_cury > win->_regbottom) {
156 			win->_cury = win->_regbottom;
157 			win->_curx = win->_maxx;
158 			if (!win->_scroll)
159 				return (ERR);
160 			scroll(win);
161 		}
162 		win->_curx = 0;
163 		return (OK);
164 	}
165 	win->_curx = x;
166 	return OK;
167 }
168 
169 static inline
170 int waddch_nosync(WINDOW *win, const chtype ch)
171 /* the workhorse function -- add a character to the given window */
172 {
173 	int	x, y;
174 	int	t;
175 	const char *s;
176 
177 	if ((ch & A_ALTCHARSET)
178 	    || ((t = TextOf(ch)) > 127)
179 	    || ((s = unctrl(t))[1] == 0))
180 		return waddch_literal(win, ch);
181 
182 	x = win->_curx;
183 	y = win->_cury;
184 
185 	switch (t) {
186 	case '\t':
187 		x += (TABSIZE-(x%TABSIZE));
188 
189 		/*
190 		 * Space-fill the tab on the bottom line so that we'll get the
191 		 * "correct" cursor position.
192 		 */
193 		if ((! win->_scroll && (y == win->_regbottom))
194 		 || (x <= win->_maxx)) {
195 			chtype blank = (' ' | AttrOf(ch));
196 			while (win->_curx < x) {
197 				if (waddch_literal(win, blank) == ERR)
198 					return(ERR);
199 			}
200 			break;
201 		} else {
202 			wclrtoeol(win);
203 			win->_flags |= _WRAPPED;
204 			if (++y > win->_regbottom) {
205 				x = win->_maxx;
206 				y--;
207 				if (win->_scroll) {
208 					scroll(win);
209 					x = 0;
210 				}
211 			} else {
212 				x = 0;
213 			}
214 		}
215 		break;
216 	case '\n':
217 		wclrtoeol(win);
218 		if (++y > win->_regbottom) {
219 			y--;
220 			if (win->_scroll)
221 				scroll(win);
222 			else
223 				return (ERR);
224 		}
225 		/* FALLTHRU */
226 	case '\r':
227 		x = 0;
228 		win->_flags &= ~_WRAPPED;
229 		break;
230 	case '\b':
231 		if (x == 0)
232 			return (OK);
233 		x--;
234 		win->_flags &= ~_WRAPPED;
235 		break;
236 	default:
237 		while (*s)
238 			if (waddch_literal(win, (*s++)|AttrOf(ch)) == ERR)
239 				return ERR;
240 		return(OK);
241 	}
242 
243 	win->_curx = x;
244 	win->_cury = y;
245 
246 	return(OK);
247 }
248 
249 int _nc_waddch_nosync(WINDOW *win, const chtype c)
250 /* export copy of waddch_nosync() so the string-put functions can use it */
251 {
252     return(waddch_nosync(win, c));
253 }
254 
255 /*
256  * The versions below call _nc_synhook().  We wanted to avoid this in the
257  * version exported for string puts; they'll call _nc_synchook once at end
258  * of run.
259  */
260 
261 /* These are actual entry points */
262 
263 int waddch(WINDOW *win, const chtype ch)
264 {
265 	int code = ERR;
266 
267 	TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), win, _tracechtype(ch)));
268 
269 	if (win && (waddch_nosync(win, ch) != ERR))
270 	{
271 		_nc_synchook(win);
272 		code = OK;
273 	}
274 
275 	TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_RETURN("%d"), code));
276 	return(code);
277 }
278 
279 int wechochar(WINDOW *win, const chtype ch)
280 {
281 	int code = ERR;
282 
283 	TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win, _tracechtype(ch)));
284 
285 	if (win && (waddch_nosync(win, ch) != ERR))
286 	{
287 		bool	save_immed = win->_immed;
288 		win->_immed = TRUE;
289 		_nc_synchook(win);
290 		win->_immed = save_immed;
291 		code = OK;
292 	}
293 	TR(TRACE_VIRTPUT|TRACE_CCALLS, (T_RETURN("%d"), code));
294 	return(code);
295 }
296