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