Back to index

tetex-bin  3.0
lib_addch.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2003,2004 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /*
00030 **     lib_addch.c
00031 **
00032 **     The routine waddch().
00033 **
00034 */
00035 
00036 #include <curses.priv.h>
00037 #include <ctype.h>
00038 
00039 MODULE_ID("$Id: lib_addch.c,v 1.84 2004/09/26 00:10:54 tom Exp $")
00040 
00041 /*
00042  * Ugly microtweaking alert.  Everything from here to end of module is
00043  * likely to be speed-critical -- profiling data sure says it is!
00044  * Most of the important screen-painting functions are shells around
00045  * waddch().  So we make every effort to reduce function-call overhead
00046  * by inlining stuff, even at the cost of making wrapped copies for
00047  * export.  Also we supply some internal versions that don't call the
00048  * window sync hook, for use by string-put functions.
00049  */
00050 
00051 /* Return bit mask for clearing color pair number if given ch has color */
00052 #define COLOR_MASK(ch) (~(attr_t)((ch)&A_COLOR?A_COLOR:0))
00053 
00054 static inline NCURSES_CH_T
00055 render_char(WINDOW *win, NCURSES_CH_T ch)
00056 /* compute a rendition of the given char correct for the current context */
00057 {
00058     attr_t a = win->_attrs;
00059 
00060     if (ISBLANK(ch) && AttrOf(ch) == A_NORMAL) {
00061        /* color in attrs has precedence over bkgrnd */
00062        ch = win->_nc_bkgd;
00063        SetAttr(ch, a | (AttrOf(win->_nc_bkgd) & COLOR_MASK(a)));
00064     } else {
00065        /* color in attrs has precedence over bkgrnd */
00066        a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a);
00067        /* color in ch has precedence */
00068        AddAttr(ch, (a & COLOR_MASK(AttrOf(ch))));
00069     }
00070 
00071     TR(TRACE_VIRTPUT, ("render_char bkg %s, attrs %s -> ch %s",
00072                      _tracech_t2(1, CHREF(win->_nc_bkgd)),
00073                      _traceattr(win->_attrs),
00074                      _tracech_t2(3, CHREF(ch))));
00075 
00076     return (ch);
00077 }
00078 
00079 NCURSES_EXPORT(NCURSES_CH_T)
00080 _nc_render(WINDOW *win, NCURSES_CH_T ch)
00081 /* make render_char() visible while still allowing us to inline it below */
00082 {
00083     return render_char(win, ch);
00084 }
00085 
00086 /* check if position is legal; if not, return error */
00087 #ifndef NDEBUG                     /* treat this like an assertion */
00088 #define CHECK_POSITION(win, x, y) \
00089        if (y > win->_maxy \
00090         || x > win->_maxx \
00091         || y < 0 \
00092         || x < 0) { \
00093               TR(TRACE_VIRTPUT, ("Alert! Win=%p _curx = %d, _cury = %d " \
00094                                "(_maxx = %d, _maxy = %d)", win, x, y, \
00095                                win->_maxx, win->_maxy)); \
00096               return(ERR); \
00097        }
00098 #else
00099 #define CHECK_POSITION(win, x, y)  /* nothing */
00100 #endif
00101 
00102 static
00103 #if !USE_WIDEC_SUPPORT             /* cannot be inline if it is recursive */
00104 inline
00105 #endif
00106 int
00107 waddch_literal(WINDOW *win, NCURSES_CH_T ch)
00108 {
00109     int x;
00110     int y;
00111     struct ldat *line;
00112 
00113     x = win->_curx;
00114     y = win->_cury;
00115 
00116     CHECK_POSITION(win, x, y);
00117 
00118     /*
00119      * If we're trying to add a character at the lower-right corner more
00120      * than once, fail.  (Moving the cursor will clear the flag).
00121      */
00122 #if 0                       /* Solaris 2.6 allows updating the corner more than once */
00123     if (win->_flags & _WRAPPED) {
00124        if (x >= win->_maxx)
00125            return (ERR);
00126        win->_flags &= ~_WRAPPED;
00127     }
00128 #endif
00129 
00130     ch = render_char(win, ch);
00131 
00132     line = win->_line + y;
00133 
00134     CHANGED_CELL(line, x);
00135 
00136     /*
00137      * Build up multibyte characters until we have a wide-character.
00138      */
00139     if_WIDEC({
00140        if (WINDOW_EXT(win, addch_used) != 0 || !Charable(ch)) {
00141            char *buffer = WINDOW_EXT(win, addch_work);
00142            int len;
00143            mbstate_t state;
00144            wchar_t result;
00145 
00146            if ((WINDOW_EXT(win, addch_used) != 0) &&
00147               (WINDOW_EXT(win, addch_x) != x ||
00148                WINDOW_EXT(win, addch_y) != y)) {
00149               /* discard the incomplete multibyte character */
00150               WINDOW_EXT(win, addch_used) = 0;
00151               TR(TRACE_VIRTPUT,
00152                  ("Alert discarded multibyte on move (%d,%d) -> (%d,%d)",
00153                   WINDOW_EXT(win, addch_y), WINDOW_EXT(win, addch_x),
00154                   y, x));
00155            }
00156            WINDOW_EXT(win, addch_x) = x;
00157            WINDOW_EXT(win, addch_y) = y;
00158 
00159            memset(&state, 0, sizeof(state));
00160            buffer[WINDOW_EXT(win, addch_used)] = CharOf(ch);
00161            WINDOW_EXT(win, addch_used) += 1;
00162            buffer[WINDOW_EXT(win, addch_used)] = '\0';
00163            if ((len = mbrtowc(&result,
00164                             buffer,
00165                             WINDOW_EXT(win, addch_used), &state)) > 0) {
00166               attr_t attrs = AttrOf(ch);
00167               SetChar(ch, result, attrs);
00168               WINDOW_EXT(win, addch_used) = 0;
00169               if (is8bits(CharOf(ch))) {
00170                   const char *s = unctrl(CharOf(ch));
00171                   if (s[1] != 0) {
00172                      return waddstr(win, s);
00173                   }
00174               }
00175            } else {
00176               if (len == -1) {
00177                   /*
00178                    * An error occurred.  We could either discard everything,
00179                    * or assume that the error was in the previous input.
00180                    * Try the latter.
00181                    */
00182                   TR(TRACE_VIRTPUT, ("Alert! mbrtowc returns error"));
00183                   buffer[0] = CharOf(ch);
00184                   WINDOW_EXT(win, addch_used) = 1;
00185               }
00186               return OK;
00187            }
00188        }
00189     });
00190 
00191     /*
00192      * Handle non-spacing characters
00193      */
00194     if_WIDEC({
00195        if (wcwidth(CharOf(ch)) == 0) {
00196            int i;
00197            if ((x > 0 && y >= 0)
00198               || ((y = win->_cury - 1) >= 0 &&
00199                   (x = win->_maxx) > 0)) {
00200               wchar_t *chars = (win->_line[y].text[x - 1].chars);
00201               for (i = 0; i < CCHARW_MAX; ++i) {
00202                   if (chars[i] == 0) {
00203                      chars[i] = CharOf(ch);
00204                      break;
00205                   }
00206               }
00207            }
00208            goto testwrapping;
00209        }
00210     });
00211     line->text[x++] = ch;
00212     /*
00213      * Provide for multi-column characters
00214      */
00215     if_WIDEC({
00216        int len = wcwidth(CharOf(ch));
00217        while (len-- > 1) {
00218            if (x + (len - 1) > win->_maxx) {
00219               NCURSES_CH_T blank = NewChar2(BLANK_TEXT, BLANK_ATTR);
00220               AddAttr(blank, AttrOf(ch));
00221               if (waddch_literal(win, blank) != ERR)
00222                   return waddch_literal(win, ch);
00223               return ERR;
00224            }
00225            AddAttr(line->text[x++], WA_NAC);
00226            TR(TRACE_VIRTPUT, ("added NAC %d", x - 1));
00227        }
00228     }
00229   testwrapping:
00230     );
00231 
00232     TR(TRACE_VIRTPUT, ("cell (%d, %d..%d) = %s",
00233                      win->_cury, win->_curx, x - 1,
00234                      _tracech_t(CHREF(ch))));
00235 
00236     if (x > win->_maxx) {
00237        /*
00238         * The _WRAPPED flag is useful only for telling an application that
00239         * we've just wrapped the cursor.  We don't do anything with this flag
00240         * except set it when wrapping, and clear it whenever we move the
00241         * cursor.  If we try to wrap at the lower-right corner of a window, we
00242         * cannot move the cursor (since that wouldn't be legal).  So we return
00243         * an error (which is what SVr4 does).  Unlike SVr4, we can
00244         * successfully add a character to the lower-right corner (Solaris 2.6
00245         * does this also, however).
00246         */
00247        win->_flags |= _WRAPPED;
00248        if (++win->_cury > win->_regbottom) {
00249            win->_cury = win->_regbottom;
00250            win->_curx = win->_maxx;
00251            if (!win->_scroll)
00252               return (ERR);
00253            scroll(win);
00254        }
00255        win->_curx = 0;
00256        return (OK);
00257     }
00258     win->_curx = x;
00259     return OK;
00260 }
00261 
00262 static inline int
00263 waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
00264 /* the workhorse function -- add a character to the given window */
00265 {
00266     int x, y;
00267     chtype t = CharOf(ch);
00268     const char *s = 0;
00269 
00270     /*
00271      * If we are using the alternate character set, forget about locale.
00272      * Otherwise, if unctrl() returns a single-character or the locale
00273      * claims the code is printable, treat it that way.
00274      */
00275     if ((AttrOf(ch) & A_ALTCHARSET)
00276        || ((s = unctrl(t))[1] == 0 ||
00277            (
00278               isprint(t)
00279 #if USE_WIDEC_SUPPORT
00280               || WINDOW_EXT(win, addch_used)
00281 #endif
00282            )))
00283        return waddch_literal(win, ch);
00284 
00285     /*
00286      * Handle carriage control and other codes that are not printable, or are
00287      * known to expand to more than one character according to unctrl().
00288      */
00289     x = win->_curx;
00290     y = win->_cury;
00291 
00292     switch (t) {
00293     case '\t':
00294        x += (TABSIZE - (x % TABSIZE));
00295 
00296        /*
00297         * Space-fill the tab on the bottom line so that we'll get the
00298         * "correct" cursor position.
00299         */
00300        if ((!win->_scroll && (y == win->_regbottom))
00301            || (x <= win->_maxx)) {
00302            NCURSES_CH_T blank = NewChar2(BLANK_TEXT, BLANK_ATTR);
00303            AddAttr(blank, AttrOf(ch));
00304            while (win->_curx < x) {
00305               if (waddch_literal(win, blank) == ERR)
00306                   return (ERR);
00307            }
00308            break;
00309        } else {
00310            wclrtoeol(win);
00311            win->_flags |= _WRAPPED;
00312            if (++y > win->_regbottom) {
00313               x = win->_maxx;
00314               y--;
00315               if (win->_scroll) {
00316                   scroll(win);
00317                   x = 0;
00318               }
00319            } else {
00320               x = 0;
00321            }
00322        }
00323        break;
00324     case '\n':
00325        wclrtoeol(win);
00326        if (++y > win->_regbottom) {
00327            y--;
00328            if (win->_scroll)
00329               scroll(win);
00330            else
00331               return (ERR);
00332        }
00333        /* FALLTHRU */
00334     case '\r':
00335        x = 0;
00336        win->_flags &= ~_WRAPPED;
00337        break;
00338     case '\b':
00339        if (x == 0)
00340            return (OK);
00341        x--;
00342        win->_flags &= ~_WRAPPED;
00343        break;
00344     default:
00345        while (*s) {
00346            NCURSES_CH_T sch;
00347            SetChar(sch, *s++, AttrOf(ch));
00348            if (waddch_literal(win, sch) == ERR)
00349               return ERR;
00350        }
00351        return (OK);
00352     }
00353 
00354     win->_curx = x;
00355     win->_cury = y;
00356 
00357     return (OK);
00358 }
00359 
00360 NCURSES_EXPORT(int)
00361 _nc_waddch_nosync(WINDOW *win, const NCURSES_CH_T c)
00362 /* export copy of waddch_nosync() so the string-put functions can use it */
00363 {
00364     return (waddch_nosync(win, c));
00365 }
00366 
00367 /*
00368  * The versions below call _nc_synhook().  We wanted to avoid this in the
00369  * version exported for string puts; they'll call _nc_synchook once at end
00370  * of run.
00371  */
00372 
00373 /* These are actual entry points */
00374 
00375 NCURSES_EXPORT(int)
00376 waddch(WINDOW *win, const chtype ch)
00377 {
00378     int code = ERR;
00379     NCURSES_CH_T wch;
00380     SetChar2(wch, ch);
00381 
00382     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), win,
00383                                   _tracechtype(ch)));
00384 
00385     if (win && (waddch_nosync(win, wch) != ERR)) {
00386        _nc_synchook(win);
00387        code = OK;
00388     }
00389 
00390     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
00391     return (code);
00392 }
00393 
00394 NCURSES_EXPORT(int)
00395 wechochar(WINDOW *win, const chtype ch)
00396 {
00397     int code = ERR;
00398     NCURSES_CH_T wch;
00399     SetChar2(wch, ch);
00400 
00401     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win,
00402                                   _tracechtype(ch)));
00403 
00404     if (win && (waddch_nosync(win, wch) != ERR)) {
00405        bool save_immed = win->_immed;
00406        win->_immed = TRUE;
00407        _nc_synchook(win);
00408        win->_immed = save_immed;
00409        code = OK;
00410     }
00411     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
00412     return (code);
00413 }