X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Fwidechar%2Flib_add_wch.c;h=38d3130a54c422550e5686edc2eda0d7ab94e4e6;hp=93b41bb4930cdd38bf8f0339ee683716c7791e33;hb=cc387b11f421650091d84640e05825ea5161ac66;hpb=027ae42953e3186daed8f3882da73de48291b606 diff --git a/ncurses/widechar/lib_add_wch.c b/ncurses/widechar/lib_add_wch.c index 93b41bb4..38d3130a 100644 --- a/ncurses/widechar/lib_add_wch.c +++ b/ncurses/widechar/lib_add_wch.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 2004,2006 Free Software Foundation, Inc. * + * Copyright (c) 2004-2010,2011 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 * @@ -35,79 +35,409 @@ #include -MODULE_ID("$Id: lib_add_wch.c,v 1.6 2006/12/02 21:19:17 tom Exp $") +#if HAVE_WCTYPE_H +#include +#endif -NCURSES_EXPORT(int) -wadd_wch(WINDOW *win, const cchar_t *wch) +MODULE_ID("$Id: lib_add_wch.c,v 1.12 2011/03/22 09:31:15 Petr.Pavlu Exp $") + +/* clone/adapt lib_addch.c */ +static const cchar_t blankchar = NewChar(BLANK_TEXT); + +/* + * Ugly microtweaking alert. Everything from here to end of module is + * likely to be speed-critical -- profiling data sure says it is! + * Most of the important screen-painting functions are shells around + * wadd_wch(). So we make every effort to reduce function-call overhead + * by inlining stuff, even at the cost of making wrapped copies for + * export. Also we supply some internal versions that don't call the + * window sync hook, for use by string-put functions. + */ + +/* Return bit mask for clearing color pair number if given ch has color */ +#define COLOR_MASK(ch) (~(attr_t)((ch) & A_COLOR ? A_COLOR : 0)) + +static NCURSES_INLINE cchar_t +render_char(WINDOW *win, cchar_t ch) +/* compute a rendition of the given char correct for the current context */ { - PUTC_DATA; - int n; - int code = ERR; + 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 bkgrnd */ + a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a); + /* color in ch has precedence */ + if (pair == 0) { + if ((pair = GET_WINDOW_PAIR(win)) == 0) + pair = GetPair(win->_nc_bkgd); + } + AddAttr(ch, (a & COLOR_MASK(AttrOf(ch)))); + SetPair(ch, pair); + } + + 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); +} + +/* check if position is legal; if not, return error */ +#ifndef NDEBUG /* treat this like an assertion */ +#define CHECK_POSITION(win, x, y) \ + if (y > win->_maxy \ + || x > win->_maxx \ + || y < 0 \ + || x < 0) { \ + TR(TRACE_VIRTPUT, ("Alert! Win=%p _curx = %d, _cury = %d " \ + "(_maxx = %d, _maxy = %d)", win, x, y, \ + win->_maxx, win->_maxy)); \ + return(ERR); \ + } +#else +#define CHECK_POSITION(win, x, y) /* nothing */ +#endif + +static bool +newline_forces_scroll(WINDOW *win, NCURSES_SIZE_T * ypos) +{ + bool result = FALSE; + + if (*ypos >= win->_regtop && *ypos == win->_regbottom) { + *ypos = win->_regbottom; + result = TRUE; + } else { + *ypos = (NCURSES_SIZE_T) (*ypos + 1); + } + return result; +} - TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wadd_wch(%p, %s)"), win, - _tracech_t(wch))); - - if (win != 0) { - PUTC_INIT; - for (PUTC_i = 0; PUTC_i < CCHARW_MAX; ++PUTC_i) { - attr_t attrs = (wch->attr & A_ATTRIBUTES); - - if ((PUTC_ch = wch->chars[PUTC_i]) == L'\0') - break; - if ((PUTC_n = wcrtomb(PUTC_buf, PUTC_ch, &PUT_st)) <= 0) { - code = ERR; - if (is8bits(PUTC_ch)) - code = waddch(win, UChar(PUTC_ch) | attrs); - break; +/* + * 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); + scroll(win); + } + win->_curx = 0; + return (OK); +} + +static int wadd_wch_literal(WINDOW *, cchar_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) +{ + cchar_t blank = blankchar; + int save_x = win->_curx; + int save_y = win->_cury; + + while (count-- > 0) { + if (wadd_wch_literal(win, blank) == ERR) + break; + } + win->_curx = (NCURSES_SIZE_T) save_x; + win->_cury = (NCURSES_SIZE_T) save_y; +} + +static int +wadd_wch_literal(WINDOW *win, cchar_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); + + line = win->_line + y; + + CHANGED_CELL(line, x); + + /* + * Non-spacing characters are added to the current cell. + * + * Spacing characters that are wider than one column require some display + * adjustments. + */ + { + 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; + line = win->_line + y; } - for (n = 0; n < PUTC_n; n++) { - if ((code = waddch(win, UChar(PUTC_buf[n]) | attrs)) == ERR) { + /* + * 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; } } - if (code == ERR) - break; + /* + * Finally, add the cells for this character. + */ + for (i = 0; i < len; ++i) { + cchar_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; } } - TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code)); - return (code); + /* + * Single-column characters. + */ + line->text[x++] = ch; + /* + * This label is used only for wide-characters. + */ + testwrapping: + + TR(TRACE_VIRTPUT, ("cell (%ld, %ld..%d) = %s", + (long) win->_cury, (long) win->_curx, x - 1, + _tracech_t(CHREF(ch)))); + + if (x > win->_maxx) { + return wrap_to_next_line(win); + } + win->_curx = (NCURSES_SIZE_T) x; + return OK; } -NCURSES_EXPORT(int) -wecho_wchar(WINDOW *win, const cchar_t *wch) +static NCURSES_INLINE int +wadd_wch_nosync(WINDOW *win, cchar_t ch) +/* the workhorse function -- add a character to the given window */ { - PUTC_DATA; - int n; - int code = ERR; + NCURSES_SIZE_T x, y; + wchar_t *s; + int tabsize = 8; +#if USE_REENTRANT + SCREEN *sp = _nc_screen_of(win); +#endif + + /* + * If we are using the alternate character set, forget about locale. + * Otherwise, if the locale claims the code is printable, treat it that + * way. + */ + if ((AttrOf(ch) & A_ALTCHARSET) + || iswprint((wint_t) CharOf(ch))) + return wadd_wch_literal(win, ch); - TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wecho_wchar(%p, %s)"), win, - _tracech_t(wch))); - - if (win != 0) { - PUTC_INIT; - for (PUTC_i = 0; PUTC_i < CCHARW_MAX; ++PUTC_i) { - attr_t attrs = (wch->attr & A_ATTRIBUTES); - - if ((PUTC_ch = wch->chars[PUTC_i]) == L'\0') - break; - if ((PUTC_n = wcrtomb(PUTC_buf, PUTC_ch, &PUT_st)) <= 0) { - code = ERR; - if (is8bits(PUTC_ch)) - code = waddch(win, UChar(PUTC_ch) | attrs); - break; + /* + * 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; + + switch (CharOf(ch)) { + case '\t': +#if USE_REENTRANT + tabsize = *ptrTabsize(sp); +#else + tabsize = TABSIZE; +#endif + x = (NCURSES_SIZE_T) (x + (tabsize - (x % tabsize))); + /* + * Space-fill the tab on the bottom line so that we'll get the + * "correct" cursor position. + */ + if ((!win->_scroll && (y == win->_regbottom)) + || (x <= win->_maxx)) { + cchar_t blank = blankchar; + AddAttr(blank, AttrOf(ch)); + while (win->_curx < x) { + if (wadd_wch_literal(win, blank) == ERR) + return (ERR); } - for (n = 0; n < PUTC_n; n++) { - if ((code = waddch(win, UChar(PUTC_buf[n]) | attrs)) == ERR) { - break; + break; + } else { + wclrtoeol(win); + win->_flags |= _WRAPPED; + if (newline_forces_scroll(win, &y)) { + x = win->_maxx; + if (win->_scroll) { + scroll(win); + x = 0; } + } else { + x = 0; + } + } + break; + case '\n': + wclrtoeol(win); + if (newline_forces_scroll(win, &y)) { + if (win->_scroll) + scroll(win); + else + return (ERR); + } + /* FALLTHRU */ + case '\r': + x = 0; + win->_flags &= ~_WRAPPED; + break; + case '\b': + if (x == 0) + return (OK); + x--; + win->_flags &= ~_WRAPPED; + break; + default: + if ((s = wunctrl(&ch)) != 0) { + while (*s) { + cchar_t sch; + SetChar(sch, *s++, AttrOf(ch)); + if_EXT_COLORS(SetPair(sch, GetPair(ch))); + if (wadd_wch_literal(win, sch) == ERR) + return ERR; } - if (code == ERR) - break; + return OK; } - wrefresh(win); + return ERR; } + win->_curx = x; + win->_cury = y; + + return OK; +} + +/* + * 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 */ + +NCURSES_EXPORT(int) +wadd_wch(WINDOW *win, const cchar_t *wch) +{ + int code = ERR; + + TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wadd_wch(%p, %s)"), + (void *) win, + _tracecchar_t(wch))); + + if (win && (wadd_wch_nosync(win, *wch) != ERR)) { + _nc_synchook(win); + code = OK; + } + + TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code)); + return (code); +} + +NCURSES_EXPORT(int) +wecho_wchar(WINDOW *win, const cchar_t *wch) +{ + int code = ERR; + + TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), + (void *) win, + _tracecchar_t(wch))); + + if (win && (wadd_wch_nosync(win, *wch) != ERR)) { + bool save_immed = win->_immed; + win->_immed = TRUE; + _nc_synchook(win); + win->_immed = save_immed; + code = OK; + } TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code)); return (code); }