X-Git-Url: http://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Fbase%2Flib_addch.c;h=cc3e8781dac801687b52fdd41508a181125a3c11;hp=69c17c34709bf0cd43cde2177ab3570efa3677ea;hb=a21e1b511e3c79ea7c4786f6b0e48850e7fa94f0;hpb=b1f61d9f3aa244512045a6b02e759825d7049d34 diff --git a/ncurses/base/lib_addch.c b/ncurses/base/lib_addch.c index 69c17c34..cc3e8781 100644 --- a/ncurses/base/lib_addch.c +++ b/ncurses/base/lib_addch.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * + * Copyright (c) 1998-2006,2008 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * @@ -26,11 +26,6 @@ * authorization. * ****************************************************************************/ -/**************************************************************************** - * Author: Zeyd M. Ben-Halim 1992,1995 * - * and: Eric S. Raymond * - ****************************************************************************/ - /* ** lib_addch.c ** @@ -41,7 +36,9 @@ #include #include -MODULE_ID("$Id: lib_addch.c,v 1.44 2000/05/20 21:13:11 tom Exp $") +MODULE_ID("$Id: lib_addch.c,v 1.112 2008/05/17 19:08:40 Miroslav.Lichvar Exp $") + +static const NCURSES_CH_T blankchar = NewChar(BLANK_TEXT); /* * Ugly microtweaking alert. Everything from here to end of module is @@ -54,39 +51,56 @@ MODULE_ID("$Id: lib_addch.c,v 1.44 2000/05/20 21:13:11 tom Exp $") */ /* Return bit mask for clearing color pair number if given ch has color */ -#define COLOR_MASK(ch) (~(chtype)((ch)&A_COLOR?A_COLOR:0)) +#define COLOR_MASK(ch) (~(attr_t)((ch) & A_COLOR ? A_COLOR : 0)) -static inline chtype -render_char(WINDOW *win, chtype ch) +static NCURSES_INLINE NCURSES_CH_T +render_char(WINDOW *win, NCURSES_CH_T ch) /* compute a rendition of the given char correct for the current context */ { - chtype a = win->_attrs; - - if (ch == ' ') { - /* color in attrs has precedence over bkgd */ - ch = a | (win->_bkgd & COLOR_MASK(a)); + attr_t a = WINDOW_ATTRS(win); + int pair = GetPair(ch); + + if (ISBLANK(ch) + && AttrOf(ch) == A_NORMAL + && pair == 0) { + /* color/pair in attrs has precedence over bkgrnd */ + ch = win->_nc_bkgd; + SetAttr(ch, a | AttrOf(win->_nc_bkgd)); + if ((pair = GET_WINDOW_PAIR(win)) == 0) + pair = GetPair(win->_nc_bkgd); + SetPair(ch, pair); } else { - /* color in attrs has precedence over bkgd */ - a |= (win->_bkgd & A_ATTRIBUTES) & COLOR_MASK(a); + /* color in attrs has precedence over bkgrnd */ + a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a); /* color in ch has precedence */ - ch |= (a & COLOR_MASK(ch)); + if (pair == 0) { + if ((pair = GET_WINDOW_PAIR(win)) == 0) + pair = GetPair(win->_nc_bkgd); + } +#if 0 + if (pair > 255) { + NCURSES_CH_T fixme = ch; + SetPair(fixme, pair); + } +#endif + AddAttr(ch, (a & COLOR_MASK(AttrOf(ch)))); + SetPair(ch, pair); } - TR(TRACE_VIRTPUT, ("bkg = %lx, attrs = %lx -> ch = %lx", win->_bkgd, - win->_attrs, ch)); + TR(TRACE_VIRTPUT, + ("render_char bkg %s (%d), attrs %s (%d) -> ch %s (%d)", + _tracech_t2(1, CHREF(win->_nc_bkgd)), + GetPair(win->_nc_bkgd), + _traceattr(WINDOW_ATTRS(win)), + GET_WINDOW_PAIR(win), + _tracech_t2(3, CHREF(ch)), + GetPair(ch))); return (ch); } -chtype -_nc_background(WINDOW *win) -/* make render_char() visible while still allowing us to inline it below */ -{ - return (win->_bkgd); -} - -chtype -_nc_render(WINDOW *win, chtype ch) +NCURSES_EXPORT(NCURSES_CH_T) +_nc_render(WINDOW *win, NCURSES_CH_T ch) /* make render_char() visible while still allowing us to inline it below */ { return render_char(win, ch); @@ -108,77 +122,306 @@ _nc_render(WINDOW *win, chtype ch) #define CHECK_POSITION(win, x, y) /* nothing */ #endif -static inline int -waddch_literal(WINDOW *win, chtype ch) +static bool +newline_forces_scroll(WINDOW *win, NCURSES_SIZE_T * ypos) { - int x; - struct ldat *line; - - x = win->_curx; + bool result = FALSE; - CHECK_POSITION(win, x, win->_cury); + if (*ypos >= win->_regtop && *ypos == win->_regbottom) { + *ypos = win->_regbottom; + result = TRUE; + } else { + *ypos += 1; + } + return result; +} - /* - * If we're trying to add a character at the lower-right corner more - * than once, fail. (Moving the cursor will clear the flag). - */ -#if 0 /* Solaris 2.6 allows updating the corner more than once */ - if (win->_flags & _WRAPPED) { - if (x >= win->_maxx) +/* + * The _WRAPPED flag is useful only for telling an application that we've just + * wrapped the cursor. We don't do anything with this flag except set it when + * wrapping, and clear it whenever we move the cursor. If we try to wrap at + * the lower-right corner of a window, we cannot move the cursor (since that + * wouldn't be legal). So we return an error (which is what SVr4 does). + * Unlike SVr4, we can successfully add a character to the lower-right corner + * (Solaris 2.6 does this also, however). + */ +static int +wrap_to_next_line(WINDOW *win) +{ + win->_flags |= _WRAPPED; + if (newline_forces_scroll(win, &(win->_cury))) { + win->_curx = win->_maxx; + if (!win->_scroll) return (ERR); - win->_flags &= ~_WRAPPED; + scroll(win); + } + win->_curx = 0; + return (OK); +} + +#if USE_WIDEC_SUPPORT +static int waddch_literal(WINDOW *, NCURSES_CH_T); +/* + * Fill the given number of cells with blanks using the current background + * rendition. This saves/restores the current x-position. + */ +static void +fill_cells(WINDOW *win, int count) +{ + NCURSES_CH_T blank = blankchar; + int save_x = win->_curx; + int save_y = win->_cury; + + while (count-- > 0) { + if (waddch_literal(win, blank) == ERR) + break; } + win->_curx = save_x; + win->_cury = save_y; +} +#endif + +/* + * Build up the bytes for a multibyte character, returning the length when + * complete (a positive number), -1 for error and -2 for incomplete. + */ +#if USE_WIDEC_SUPPORT +NCURSES_EXPORT(int) +_nc_build_wch(WINDOW *win, ARG_CH_T ch) +{ + char *buffer = WINDOW_EXT(win, addch_work); + int len; + int x = win->_curx; + int y = win->_cury; + mbstate_t state; + wchar_t result; + + if ((WINDOW_EXT(win, addch_used) != 0) && + (WINDOW_EXT(win, addch_x) != x || + WINDOW_EXT(win, addch_y) != y)) { + /* discard the incomplete multibyte character */ + WINDOW_EXT(win, addch_used) = 0; + TR(TRACE_VIRTPUT, + ("Alert discarded multibyte on move (%d,%d) -> (%d,%d)", + WINDOW_EXT(win, addch_y), WINDOW_EXT(win, addch_x), + y, x)); + } + WINDOW_EXT(win, addch_x) = x; + WINDOW_EXT(win, addch_y) = y; + + init_mb(state); + buffer[WINDOW_EXT(win, addch_used)] = CharOf(CHDEREF(ch)); + WINDOW_EXT(win, addch_used) += 1; + buffer[WINDOW_EXT(win, addch_used)] = '\0'; + if ((len = mbrtowc(&result, + buffer, + WINDOW_EXT(win, addch_used), &state)) > 0) { + attr_t attrs = AttrOf(CHDEREF(ch)); + if_EXT_COLORS(int pair = GetPair(CHDEREF(ch))); + SetChar(CHDEREF(ch), result, attrs); + if_EXT_COLORS(SetPair(CHDEREF(ch), pair)); + WINDOW_EXT(win, addch_used) = 0; + } else if (len == -1) { + /* + * An error occurred. We could either discard everything, + * or assume that the error was in the previous input. + * Try the latter. + */ + TR(TRACE_VIRTPUT, ("Alert! mbrtowc returns error")); + /* handle this with unctrl() */ + WINDOW_EXT(win, addch_used) = 0; + } + return len; +} +#endif /* USE_WIDEC_SUPPORT */ + +static +#if !USE_WIDEC_SUPPORT /* cannot be inline if it is recursive */ +NCURSES_INLINE #endif +int +waddch_literal(WINDOW *win, NCURSES_CH_T ch) +{ + int x; + int y; + struct ldat *line; + + x = win->_curx; + y = win->_cury; + + CHECK_POSITION(win, x, y); ch = render_char(win, ch); - TR(TRACE_VIRTPUT, ("win attr = %s", _traceattr(win->_attrs))); - line = win->_line + win->_cury; + line = win->_line + y; CHANGED_CELL(line, x); + /* + * Build up multibyte characters until we have a wide-character. + */ + if_WIDEC({ + if (WINDOW_EXT(win, addch_used) != 0 || !Charable(ch)) { + int len = _nc_build_wch(win, CHREF(ch)); + + if (len >= -1) { + /* handle EILSEQ */ + if (is8bits(CharOf(ch))) { + const char *s = unctrl((chtype) CharOf(ch)); + if (s[1] != 0) { + return waddstr(win, s); + } + } + if (len == -1) + return waddch(win, ' '); + } else { + return OK; + } + } + }); + + /* + * Non-spacing characters are added to the current cell. + * + * Spacing characters that are wider than one column require some display + * adjustments. + */ + if_WIDEC({ + int len = wcwidth(CharOf(ch)); + int i; + int j; + wchar_t *chars; + + if (len == 0) { /* non-spacing */ + if ((x > 0 && y >= 0) + || (win->_maxx >= 0 && win->_cury >= 1)) { + if (x > 0 && y >= 0) + chars = (win->_line[y].text[x - 1].chars); + else + chars = (win->_line[y - 1].text[win->_maxx].chars); + for (i = 0; i < CCHARW_MAX; ++i) { + if (chars[i] == 0) { + TR(TRACE_VIRTPUT, + ("added non-spacing %d: %x", + x, (int) CharOf(ch))); + chars[i] = CharOf(ch); + break; + } + } + } + goto testwrapping; + } else if (len > 1) { /* multi-column characters */ + /* + * Check if the character will fit on the current line. If it does + * not fit, fill in the remainder of the line with blanks. and + * move to the next line. + */ + if (len > win->_maxx + 1) { + TR(TRACE_VIRTPUT, ("character will not fit")); + return ERR; + } else if (x + len > win->_maxx + 1) { + int count = win->_maxx + 1 - x; + TR(TRACE_VIRTPUT, ("fill %d remaining cells", count)); + fill_cells(win, count); + if (wrap_to_next_line(win) == ERR) + return ERR; + x = win->_curx; + y = win->_cury; + } + /* + * Check for cells which are orphaned by adding this character, set + * those to blanks. + * + * FIXME: this actually could fill j-i cells, more complicated to + * setup though. + */ + for (i = 0; i < len; ++i) { + if (isWidecBase(win->_line[y].text[x + i])) { + break; + } else if (isWidecExt(win->_line[y].text[x + i])) { + for (j = i; x + j <= win->_maxx; ++j) { + if (!isWidecExt(win->_line[y].text[x + j])) { + TR(TRACE_VIRTPUT, ("fill %d orphan cells", j)); + fill_cells(win, j); + break; + } + } + break; + } + } + /* + * Finally, add the cells for this character. + */ + for (i = 0; i < len; ++i) { + NCURSES_CH_T value = ch; + SetWidecExt(value, i); + TR(TRACE_VIRTPUT, ("multicolumn %d:%d (%d,%d)", + i + 1, len, + win->_begy + y, win->_begx + x)); + line->text[x] = value; + CHANGED_CELL(line, x); + ++x; + } + goto testwrapping; + } + }); + + /* + * Single-column characters. + */ line->text[x++] = ch; + /* + * This label is used only for wide-characters. + */ + if_WIDEC( + testwrapping: + ); + + TR(TRACE_VIRTPUT, ("cell (%ld, %ld..%d) = %s", + (long) win->_cury, (long) win->_curx, x - 1, + _tracech_t(CHREF(ch)))); - TR(TRACE_VIRTPUT, ("(%d, %d) = %s", win->_cury, x, _tracechtype(ch))); if (x > win->_maxx) { - /* - * The _WRAPPED flag is useful only for telling an application that - * we've just wrapped the cursor. We don't do anything with this flag - * except set it when wrapping, and clear it whenever we move the - * cursor. If we try to wrap at the lower-right corner of a window, we - * cannot move the cursor (since that wouldn't be legal). So we return - * an error (which is what SVr4 does). Unlike SVr4, we can - * successfully add a character to the lower-right corner (Solaris 2.6 - * does this also, however). - */ - win->_flags |= _WRAPPED; - if (++win->_cury > win->_regbottom) { - win->_cury = win->_regbottom; - win->_curx = win->_maxx; - if (!win->_scroll) - return (ERR); - scroll(win); - } - win->_curx = 0; - return (OK); + return wrap_to_next_line(win); } win->_curx = x; return OK; } -static inline int -waddch_nosync(WINDOW *win, const chtype ch) +static NCURSES_INLINE int +waddch_nosync(WINDOW *win, const NCURSES_CH_T ch) /* the workhorse function -- add a character to the given window */ { - int x, y; - int t = 0; - const char *s = 0; + NCURSES_SIZE_T x, y; + chtype t = CharOf(ch); + const char *s = unctrl(t); - if ((ch & A_ALTCHARSET) - || ((t = TextOf(ch)) > 127) - || ((s = unctrl(t))[1] == 0)) + /* + * If we are using the alternate character set, forget about locale. + * Otherwise, if unctrl() returns a single-character or the locale + * claims the code is printable, treat it that way. + */ + if ((AttrOf(ch) & A_ALTCHARSET) + || ( +#if USE_WIDEC_SUPPORT + (SP != 0 && SP->_legacy_coding) && +#endif + s[1] == 0 + ) + || ( + isprint(t) +#if USE_WIDEC_SUPPORT + || ((SP == 0 || !SP->_legacy_coding) && + (WINDOW_EXT(win, addch_used) + || !_nc_is_charable(CharOf(ch)))) +#endif + )) return waddch_literal(win, ch); + /* + * Handle carriage control and other codes that are not printable, or are + * known to expand to more than one character according to unctrl(). + */ x = win->_curx; y = win->_cury; @@ -192,7 +435,8 @@ waddch_nosync(WINDOW *win, const chtype ch) */ if ((!win->_scroll && (y == win->_regbottom)) || (x <= win->_maxx)) { - chtype blank = (' ' | AttrOf(ch)); + NCURSES_CH_T blank = blankchar; + AddAttr(blank, AttrOf(ch)); while (win->_curx < x) { if (waddch_literal(win, blank) == ERR) return (ERR); @@ -201,9 +445,8 @@ waddch_nosync(WINDOW *win, const chtype ch) } else { wclrtoeol(win); win->_flags |= _WRAPPED; - if (++y > win->_regbottom) { + if (newline_forces_scroll(win, &y)) { x = win->_maxx; - y--; if (win->_scroll) { scroll(win); x = 0; @@ -215,8 +458,7 @@ waddch_nosync(WINDOW *win, const chtype ch) break; case '\n': wclrtoeol(win); - if (++y > win->_regbottom) { - y--; + if (newline_forces_scroll(win, &y)) { if (win->_scroll) scroll(win); else @@ -234,9 +476,13 @@ waddch_nosync(WINDOW *win, const chtype ch) win->_flags &= ~_WRAPPED; break; default: - while (*s) - if (waddch_literal(win, (*s++) | AttrOf(ch)) == ERR) + while (*s) { + NCURSES_CH_T sch; + SetChar(sch, *s++, AttrOf(ch)); + if_EXT_COLORS(SetPair(sch, GetPair(ch))); + if (waddch_literal(win, sch) == ERR) return ERR; + } return (OK); } @@ -246,30 +492,32 @@ waddch_nosync(WINDOW *win, const chtype ch) return (OK); } -int -_nc_waddch_nosync(WINDOW *win, const chtype c) +NCURSES_EXPORT(int) +_nc_waddch_nosync(WINDOW *win, const NCURSES_CH_T c) /* export copy of waddch_nosync() so the string-put functions can use it */ { return (waddch_nosync(win, c)); } /* - * The versions below call _nc_synhook(). We wanted to avoid this in the + * The versions below call _nc_synchook(). We wanted to avoid this in the * version exported for string puts; they'll call _nc_synchook once at end * of run. */ /* These are actual entry points */ -int +NCURSES_EXPORT(int) waddch(WINDOW *win, const chtype ch) { int code = ERR; + NCURSES_CH_T wch; + SetChar2(wch, ch); TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), win, - _tracechtype(ch))); + _tracechtype(ch))); - if (win && (waddch_nosync(win, ch) != ERR)) { + if (win && (waddch_nosync(win, wch) != ERR)) { _nc_synchook(win); code = OK; } @@ -278,15 +526,17 @@ waddch(WINDOW *win, const chtype ch) return (code); } -int +NCURSES_EXPORT(int) wechochar(WINDOW *win, const chtype ch) { int code = ERR; + NCURSES_CH_T wch; + SetChar2(wch, ch); TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win, - _tracechtype(ch))); + _tracechtype(ch))); - if (win && (waddch_nosync(win, ch) != ERR)) { + if (win && (waddch_nosync(win, wch) != ERR)) { bool save_immed = win->_immed; win->_immed = TRUE; _nc_synchook(win);