summaryrefslogtreecommitdiff
path: root/ncurses/widechar/lib_add_wch.c
diff options
context:
space:
mode:
authorEd Schouten <ed@FreeBSD.org>2011-04-30 10:55:14 +0000
committerEd Schouten <ed@FreeBSD.org>2011-04-30 10:55:14 +0000
commit0294a182a1629b1d854b84906e73487d6cb75fba (patch)
treee563e21bcc0164ba11452983316bda26eebb1d3c /ncurses/widechar/lib_add_wch.c
parent22b11c4db16996bf8d4878fab98762c348676df3 (diff)
downloadsrc-test2-vendor/ncurses/5.8-20110226.tar.gz
src-test2-vendor/ncurses/5.8-20110226.zip
Import a stock copy of ncurses 5.8 into the vendor space.vendor/ncurses/5.8-20110226
It seems both local changes we made to 5.7 have already been fixed upstream properly, so there is no need to preserve the changes. Also, with SVN we import full source trees. Unlike CVS, where we removed unneeded cruft.
Diffstat (limited to 'ncurses/widechar/lib_add_wch.c')
-rw-r--r--ncurses/widechar/lib_add_wch.c438
1 files changed, 384 insertions, 54 deletions
diff --git a/ncurses/widechar/lib_add_wch.c b/ncurses/widechar/lib_add_wch.c
index 93b41bb4930c..a8491fcbf57f 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-2009,2010 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 <curses.priv.h>
-MODULE_ID("$Id: lib_add_wch.c,v 1.6 2006/12/02 21:19:17 tom Exp $")
+#if HAVE_WCTYPE_H
+#include <wctype.h>
+#endif
-NCURSES_EXPORT(int)
-wadd_wch(WINDOW *win, const cchar_t *wch)
+MODULE_ID("$Id: lib_add_wch.c,v 1.11 2010/12/19 01:32:55 tom 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);
}