Esempio n. 1
0
_tracecchar_t2(int bufnum, const cchar_t *ch)
{
    char *result = _nc_trace_buf(bufnum, BUFSIZ);
    attr_t attr;
    const char *found;

    if (result != 0) {
	strcpy(result, l_brace);
	if (ch != 0) {
	    attr = AttrOfD(ch);
	    if ((found = _nc_altcharset_name(attr, (chtype) CharOfD(ch))) != 0) {
		(void) _nc_trace_bufcat(bufnum, found);
		attr &= ~A_ALTCHARSET;
	    } else if (isWidecExt(CHDEREF(ch))) {
		(void) _nc_trace_bufcat(bufnum, "{NAC}");
		attr &= ~A_CHARTEXT;
	    } else {
		PUTC_DATA;
		int n;

		PUTC_INIT;
		(void) _nc_trace_bufcat(bufnum, "{ ");
		for (PUTC_i = 0; PUTC_i < CCHARW_MAX; ++PUTC_i) {
		    PUTC_ch = ch->chars[PUTC_i];
		    if (PUTC_ch == L'\0') {
			if (PUTC_i == 0)
			    result = _nc_trace_bufcat(bufnum, "\\000");
			break;
		    }
		    PUTC_n = wcrtomb(PUTC_buf, ch->chars[PUTC_i], &PUT_st);
		    if (PUTC_n <= 0) {
			if (PUTC_ch != L'\0') {
			    /* it could not be a multibyte sequence */
			    (void) _nc_trace_bufcat(bufnum,
						    _nc_tracechar(CURRENT_SCREEN,
								  UChar(ch->chars[PUTC_i])));
			}
			break;
		    }
		    for (n = 0; n < PUTC_n; n++) {
			if (n)
			    (void) _nc_trace_bufcat(bufnum, ", ");
			(void) _nc_trace_bufcat(bufnum,
						_nc_tracechar(CURRENT_SCREEN,
							      UChar(PUTC_buf[n])));
		    }
		}
		(void) _nc_trace_bufcat(bufnum, " }");
	    }
	    if (attr != A_NORMAL) {
		(void) _nc_trace_bufcat(bufnum, " | ");
		(void) _nc_trace_bufcat(bufnum, _traceattr2(bufnum + 20, attr));
	    }
	}

	result = _nc_trace_bufcat(bufnum, r_brace);
    }
    return result;
}
Esempio n. 2
0
_tracecchar_t2 (int bufnum, const cchar_t *ch)
{
    char *buf = _nc_trace_buf(bufnum, BUFSIZ);
    attr_t attr;
    const char *found;

    strcpy(buf, l_brace);
    if (ch != 0) {
	attr = AttrOfD(ch);
	if ((found = _nc_altcharset_name(attr, CharOfD(ch))) != 0) {
	    (void) _nc_trace_bufcat(bufnum, found);
	    attr &= ~A_ALTCHARSET;
	} else if (isWidecExt(CHDEREF(ch))) {
	    (void) _nc_trace_bufcat(bufnum, "{NAC}");
	    attr &= ~A_CHARTEXT;
	} else {
	    PUTC_DATA;
	    int n;

	    PUTC_INIT;
	    (void) _nc_trace_bufcat(bufnum, "{ ");
	    do {
		PUTC_ch = PUTC_i < CCHARW_MAX ? ch->chars[PUTC_i] : L'\0';
		PUTC_n = wcrtomb(PUTC_buf, ch->chars[PUTC_i], &PUT_st);
		if (PUTC_ch == L'\0')
		    --PUTC_n;
		if (PUTC_n <= 0) {
		    if (PUTC_ch != L'\0') {
			/* it could not be a multibyte sequence */
			(void) _nc_trace_bufcat(bufnum, _tracechar(UChar(ch->chars[PUTC_i])));
		    }
		    break;
		}
		for (n = 0; n < PUTC_n; n++) {
		    if (n)
			(void) _nc_trace_bufcat(bufnum, ", ");
		    (void) _nc_trace_bufcat(bufnum, _tracechar(UChar(PUTC_buf[n])));
		}
		++PUTC_i;
	    } while (PUTC_ch != L'\0');
	    (void) _nc_trace_bufcat(bufnum, " }");
	}
	if (attr != A_NORMAL) {
	    (void) _nc_trace_bufcat(bufnum, " | ");
	    (void) _nc_trace_bufcat(bufnum, _traceattr2(bufnum + 20, attr));
	}
    }

    return (_nc_trace_bufcat(bufnum, r_brace));
}
Esempio n. 3
0
werase(WINDOW *win)
{
    int code = ERR;
    NCURSES_CH_T blank;
    NCURSES_CH_T *start;

    T((T_CALLED("werase(%p)"), (void *) win));

    if (win) {
	NCURSES_CH_T *sp;
	int y;

	blank = win->_nc_bkgd;
	for (y = 0; y <= win->_maxy; y++) {
	    NCURSES_CH_T *end;

	    start = win->_line[y].text;
	    end = &start[win->_maxx];

	    /*
	     * If this is a derived window, we have to handle the case where
	     * a multicolumn character extends into the window that we are
	     * erasing.
	     */
	    if_WIDEC({
		if (isWidecExt(start[0])) {
		    int x = (win->_parent != 0) ? (win->_begx) : 0;
		    while (x-- > 0) {
			if (isWidecBase(start[-1])) {
			    --start;
			    break;
			}
			--start;
		    }
		}
	    });

	    for (sp = start; sp <= end; sp++)
		*sp = blank;

	    win->_line[y].firstchar = 0;
	    win->_line[y].lastchar = win->_maxx;
	}
	win->_curx = win->_cury = 0;
	win->_flags &= ~_WRAPPED;
	_nc_synchook(win);
	code = OK;
    }
Esempio n. 4
0
winnwstr(WINDOW *win, wchar_t *wstr, int n)
{
    int count = 0;
    cchar_t *text;

    T((T_CALLED("winnwstr(%p,%p,%d)"), (void *) win, (void *) wstr, n));
    if (wstr != 0) {
	if (win) {
	    int row, col;
	    int last = 0;

	    getyx(win, row, col);

	    text = win->_line[row].text;
	    while (count < n && count != ERR) {

		if (!isWidecExt(text[col])) {
		    int inx;
		    wchar_t wch;

		    for (inx = 0; (inx < CCHARW_MAX)
			 && ((wch = text[col].chars[inx]) != 0);
			 ++inx) {
			if (count + 1 > n) {
			    if ((count = last) == 0) {
				count = ERR;	/* error if we store nothing */
			    }
			    break;
			}
			wstr[count++] = wch;
		    }
		}
		last = count;
		if (++col > win->_maxx) {
		    break;
		}
	    }
	}
	if (count > 0) {
	    wstr[count] = '\0';
	    T(("winnwstr returns %s", _nc_viswbuf(wstr)));
	}
    }
    returnCode(count);
}
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;
	    }
	    /*
	     * 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) {
		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;
	}
    }

    /*
     * 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;
}
Esempio n. 6
0
pnoutrefresh(WINDOW *win,
	     int pminrow,
	     int pmincol,
	     int sminrow,
	     int smincol,
	     int smaxrow,
	     int smaxcol)
{
    NCURSES_SIZE_T i, j;
    NCURSES_SIZE_T m, n;
    NCURSES_SIZE_T pmaxrow;
    NCURSES_SIZE_T pmaxcol;
    SCREEN *sp;

#if USE_SCROLL_HINTS
    const int my_len = 2;	/* parameterize the threshold for hardscroll */
    NCURSES_SIZE_T displaced;
    bool wide;
#endif

    T((T_CALLED("pnoutrefresh(%p, %d, %d, %d, %d, %d, %d)"),
       (void *) win, pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol));

    if (win == 0)
	returnCode(ERR);

    if (!(win->_flags & _ISPAD))
	returnCode(ERR);

    sp = _nc_screen_of(win);

    /* negative values are interpreted as zero */
    if (pminrow < 0)
	pminrow = 0;
    if (pmincol < 0)
	pmincol = 0;
    if (sminrow < 0)
	sminrow = 0;
    if (smincol < 0)
	smincol = 0;

    pmaxrow = pminrow + smaxrow - sminrow;
    pmaxcol = pmincol + smaxcol - smincol;

    T((" pminrow + smaxrow - sminrow %ld, win->_maxy %ld",
       (long) pmaxrow, (long) win->_maxy));
    T((" pmincol + smaxcol - smincol %ld, win->_maxx %ld",
       (long) pmaxcol, (long) win->_maxx));

    /*
     * Trim the caller's screen size back to the actual limits.
     */
    if (pmaxrow > win->_maxy) {
	smaxrow -= (pmaxrow - win->_maxy);
	pmaxrow = pminrow + smaxrow - sminrow;
    }
    if (pmaxcol > win->_maxx) {
	smaxcol -= (pmaxcol - win->_maxx);
	pmaxcol = pmincol + smaxcol - smincol;
    }

    if (smaxrow >= screen_lines(sp)
	|| smaxcol >= screen_columns(sp)
	|| sminrow > smaxrow
	|| smincol > smaxcol)
	returnCode(ERR);

    T(("pad being refreshed"));

#if USE_SCROLL_HINTS
    if (win->_pad._pad_y >= 0) {
	displaced = pminrow - win->_pad._pad_y
	    - (sminrow - win->_pad._pad_top);
	T(("pad being shifted by %d line(s)", displaced));
    } else
	displaced = 0;
#endif

    /*
     * For pure efficiency, we'd want to transfer scrolling information
     * from the pad to newscr whenever the window is wide enough that
     * its update will dominate the cost of the update for the horizontal
     * band of newscr that it occupies.  Unfortunately, this threshold
     * tends to be complex to estimate, and in any case scrolling the
     * whole band and rewriting the parts outside win's image would look
     * really ugly.  So.  What we do is consider the pad "wide" if it
     * either (a) occupies the whole width of newscr, or (b) occupies
     * all but at most one column on either vertical edge of the screen
     * (this caters to fussy people who put boxes around full-screen
     * windows).  Note that changing this formula will not break any code,
     * merely change the costs of various update cases.
     */
#if USE_SCROLL_HINTS
    wide = (smincol < my_len && smaxcol > (NewScreen(sp)->_maxx - my_len));
#endif

    for (i = pminrow, m = sminrow + win->_yoffset;
	 i <= pmaxrow && m <= NewScreen(sp)->_maxy;
	 i++, m++) {
	register struct ldat *nline = &NewScreen(sp)->_line[m];
	register struct ldat *oline = &win->_line[i];
	for (j = pmincol, n = smincol; j <= pmaxcol; j++, n++) {
	    NCURSES_CH_T ch = oline->text[j];
#if USE_WIDEC_SUPPORT
	    /*
	     * Special case for leftmost character of the displayed area.
	     * Only half of a double-width character may be visible.
	     */
	    if (j == pmincol
		&& j > 0
		&& isWidecExt(ch)) {
		SetChar(ch, L(' '), AttrOf(oline->text[j - 1]));
	    }
#endif
	    if (!CharEq(ch, nline->text[n])) {
		nline->text[n] = ch;
		CHANGED_CELL(nline, n);
	    }
	}

#if USE_SCROLL_HINTS
	if (wide) {
	    int nind = m + displaced;
	    if (oline->oldindex < 0
		|| nind < sminrow
		|| nind > smaxrow) {
		nind = _NEWINDEX;
	    } else if (displaced) {
		register struct ldat *pline = &CurScreen(sp)->_line[nind];
		for (j = 0; j <= my_len; j++) {
		    int k = NewScreen(sp)->_maxx - j;
		    if (pline->text[j] != nline->text[j]
			|| pline->text[k] != nline->text[k]) {
			nind = _NEWINDEX;
			break;
		    }
		}
	    }

	    nline->oldindex = nind;
	}
#endif /* USE_SCROLL_HINTS */
	oline->firstchar = oline->lastchar = _NOCHANGE;
	if_USE_SCROLL_HINTS(oline->oldindex = i);
    }

    /*
     * Clean up debris from scrolling or resizing the pad, so we do not
     * accidentally pick up the index value during the next call to this
     * procedure.  The only rows that should have an index value are those
     * that are displayed during this cycle.
     */
#if USE_SCROLL_HINTS
    for (i = pminrow - 1; (i >= 0) && (win->_line[i].oldindex >= 0); i--)
	win->_line[i].oldindex = _NEWINDEX;
    for (i = pmaxrow + 1; (i <= win->_maxy)
	 && (win->_line[i].oldindex >= 0); i++)
	win->_line[i].oldindex = _NEWINDEX;
#endif

    win->_begx = smincol;
    win->_begy = sminrow;

    if (win->_clear) {
	win->_clear = FALSE;
	NewScreen(sp)->_clear = TRUE;
    }

    /*
     * Use the pad's current position, if it will be visible.
     * If not, don't do anything; it's not an error.
     */
    if (win->_leaveok == FALSE
	&& win->_cury >= pminrow
	&& win->_curx >= pmincol
	&& win->_cury <= pmaxrow
	&& win->_curx <= pmaxcol) {
	NewScreen(sp)->_cury = win->_cury - pminrow + win->_begy + win->_yoffset;
	NewScreen(sp)->_curx = win->_curx - pmincol + win->_begx;
    }
    NewScreen(sp)->_leaveok = win->_leaveok;
    win->_flags &= ~_HASMOVED;

    /*
     * Update our cache of the line-numbers that we displayed from the pad.
     * We will use this on subsequent calls to this function to derive
     * values to stuff into 'oldindex[]' -- for scrolling optimization.
     */
    win->_pad._pad_y = pminrow;
    win->_pad._pad_x = pmincol;
    win->_pad._pad_top = sminrow;
    win->_pad._pad_left = smincol;
    win->_pad._pad_bottom = smaxrow;
    win->_pad._pad_right = smaxcol;

    returnCode(OK);
}
Esempio n. 7
0
wadd_wchnstr(WINDOW *win, const cchar_t *astr, int n)
{
    NCURSES_CH_T blank = NewChar(BLANK_TEXT);
    NCURSES_SIZE_T y = win->_cury;
    NCURSES_SIZE_T x = win->_curx;
    int code = OK;
    struct ldat *line;
    int i, j, start, len, end;

    T((T_CALLED("wadd_wchnstr(%p,%s,%d)"), win, _nc_viscbuf(astr, n), n));

    if (!win)
	returnCode(ERR);

    if (n < 0) {
	n = _nc_wchstrlen(astr);
    }
    if (n > win->_maxx - x + 1)
	n = win->_maxx - x + 1;
    if (n == 0)
	returnCode(code);

    line = &(win->_line[y]);
    start = x;
    end = x + n - 1;

    /*
     * Reset orphaned cells of multi-column characters that extend up to the
     * new string's location to blanks.
     */
    if (x > 0 && isWidecExt(line->text[x])) {
	for (i = 0; i <= x; ++i) {
	    if (!isWidecExt(line->text[x - i])) {
		/* must be isWidecBase() */
		start -= i;
		while (i > 0) {
		    line->text[x - i--] = _nc_render(win, blank);
		}
		break;
	    }
	}
    }

    /*
     * Copy the new string to the window.
     */
    for (i = 0; i < n && x <= win->_maxx; ++i) {
	if (isWidecExt(astr[i]))
	    continue;

	len = wcwidth(CharOf(astr[i]));

	if (x + len - 1 <= win->_maxx) {
	    line->text[x] = _nc_render(win, astr[i]);
	    if (len > 1) {
		for (j = 0; j < len; ++j) {
		    if (j != 0) {
			line->text[x + j] = line->text[x];
		    }
		    SetWidecExt(line->text[x + j], j);
		}
	    }
	    x += len;
	    end += len - 1;
	} else {
	    break;
	}
    }

    /*
     * Set orphaned cells of multi-column characters which lie after the new
     * string to blanks.
     */
    while (x <= win->_maxx && isWidecExt(line->text[x])) {
	line->text[x] = _nc_render(win, blank);
	++end;
	++x;
    }
    CHANGED_RANGE(line, start, end);

    _nc_synchook(win);
    returnCode(code);
}
Esempio n. 8
0
wnoutrefresh(WINDOW *win)
{
    int limit_x;
    int src_row, src_col;
    int begx;
    int begy;
    int dst_row, dst_col;
#if USE_SCROLL_HINTS
    bool wide;
#endif
#if NCURSES_SP_FUNCS
    SCREEN *SP_PARM = _nc_screen_of(win);
#endif

    T((T_CALLED("wnoutrefresh(%p)"), (void *) win));
#ifdef TRACE
    if (USE_TRACEF(TRACE_UPDATE)) {
	_tracedump("...win", win);
	_nc_unlock_global(tracef);
    }
#endif /* TRACE */

    /*
     * This function will break badly if we try to refresh a pad.
     */
    if ((win == 0)
	|| (win->_flags & _ISPAD))
	returnCode(ERR);

    /* put them here so "win == 0" won't break our code */
    begx = win->_begx;
    begy = win->_begy;

    NewScreen(SP_PARM)->_nc_bkgd = win->_nc_bkgd;
    WINDOW_ATTRS(NewScreen(SP_PARM)) = WINDOW_ATTRS(win);

    /* merge in change information from all subwindows of this window */
    wsyncdown(win);

#if USE_SCROLL_HINTS
    /*
     * For pure efficiency, we'd want to transfer scrolling information
     * from the window to newscr whenever the window is wide enough that
     * its update will dominate the cost of the update for the horizontal
     * band of newscr that it occupies.  Unfortunately, this threshold
     * tends to be complex to estimate, and in any case scrolling the
     * whole band and rewriting the parts outside win's image would look
     * really ugly.  So.  What we do is consider the window "wide" if it
     * either (a) occupies the whole width of newscr, or (b) occupies
     * all but at most one column on either vertical edge of the screen
     * (this caters to fussy people who put boxes around full-screen
     * windows).  Note that changing this formula will not break any code,
     * merely change the costs of various update cases.
     */
    wide = (begx <= 1 && win->_maxx >= (NewScreen(SP_PARM)->_maxx - 1));
#endif

    win->_flags &= ~_HASMOVED;

    /*
     * Microtweaking alert!  This double loop is one of the genuine
     * hot spots in the code.  Even gcc doesn't seem to do enough
     * common-subexpression chunking to make it really tense,
     * so we'll force the issue.
     */

    /* limit(dst_col) */
    limit_x = win->_maxx;
    /* limit(src_col) */
    if (limit_x > NewScreen(SP_PARM)->_maxx - begx)
	limit_x = NewScreen(SP_PARM)->_maxx - begx;

    for (src_row = 0, dst_row = begy + win->_yoffset;
	 src_row <= win->_maxy && dst_row <= NewScreen(SP_PARM)->_maxy;
	 src_row++, dst_row++) {
	struct ldat *nline = &(NewScreen(SP_PARM)->_line[dst_row]);
	struct ldat *oline = &win->_line[src_row];

	if (oline->firstchar != _NOCHANGE) {
	    int last_src = oline->lastchar;

	    if (last_src > limit_x)
		last_src = limit_x;

	    src_col = oline->firstchar;
	    dst_col = src_col + begx;

	    if_WIDEC({
		int j;

		/*
		 * Ensure that we will copy complete multi-column characters
		 * on the left-boundary.
		 */
		if (isWidecExt(oline->text[src_col])) {
		    j = 1 + dst_col - WidecExt(oline->text[src_col]);
		    if (j < 0)
			j = 0;
		    if (dst_col > j) {
			src_col -= (dst_col - j);
			dst_col = j;
		    }
		}

		/*
		 * Ensure that we will copy complete multi-column characters
		 * on the right-boundary.
		 */
		j = last_src;
		if (WidecExt(oline->text[j])) {
		    ++j;
		    while (j <= limit_x) {
			if (isWidecBase(oline->text[j])) {
			    break;
			} else {
			    last_src = j;
			}
			++j;
		    }
		}
	    });

	    if_WIDEC({
		static cchar_t blank = BLANK;
		int last_dst = begx + ((last_src < win->_maxx)
				       ? last_src
				       : win->_maxx);
		int fix_left = dst_col;
		int fix_right = last_dst;
		int j;

		/*
		 * Check for boundary cases where we may overwrite part of a
		 * multi-column character.  For those, wipe the remainder of
		 * the character to blanks.
		 */
		j = dst_col;
		if (isWidecExt(nline->text[j])) {
		    /*
		     * On the left, we only care about multi-column characters
		     * that extend into the changed region.
		     */
		    fix_left = 1 + j - WidecExt(nline->text[j]);
		    if (fix_left < 0)
			fix_left = 0;	/* only if cell is corrupt */
		}

		j = last_dst;
		if (WidecExt(nline->text[j]) != 0) {
		    /*
		     * On the right, any multi-column character is a problem,
		     * unless it happens to be contained in the change, and
		     * ending at the right boundary of the change.  The
		     * computation for 'fix_left' accounts for the left-side of
		     * this character.  Find the end of the character.
		     */
		    ++j;
		    while (j <= NewScreen(SP_PARM)->_maxx &&
			   isWidecExt(nline->text[j])) {
			fix_right = j++;
		    }
		}

		/*
		 * The analysis is simpler if we do the clearing afterwards.
		 * Do that now.
		 */
		if (fix_left < dst_col || fix_right > last_dst) {
		    for (j = fix_left; j <= fix_right; ++j) {
			nline->text[j] = blank;
			CHANGED_CELL(nline, j);
		    }
		}
	    });
_tracecchar_t2(int bufnum, const cchar_t *ch)
{
    char *result = _nc_trace_buf(bufnum, (size_t) BUFSIZ);

    if (result != 0) {
	_nc_STRCPY(result, l_brace, TRACE_BUF_SIZE(bufnum));
	if (ch != 0) {
	    const char *found;
	    attr_t attr = AttrOfD(ch);

	    if ((found = _nc_altcharset_name(attr, (chtype) CharOfD(ch))) != 0) {
		(void) _nc_trace_bufcat(bufnum, found);
		attr &= ~A_ALTCHARSET;
	    } else if (isWidecExt(CHDEREF(ch))) {
		(void) _nc_trace_bufcat(bufnum, "{NAC}");
		attr &= ~A_CHARTEXT;
	    } else {
		PUTC_DATA;
		int n;

		(void) _nc_trace_bufcat(bufnum, "{ ");
		for (PUTC_i = 0; PUTC_i < CCHARW_MAX; ++PUTC_i) {
		    PUTC_ch = ch->chars[PUTC_i];
		    if (PUTC_ch == L'\0') {
			if (PUTC_i == 0)
			    (void) _nc_trace_bufcat(bufnum, "\\000");
			break;
		    }
		    PUTC_INIT;
		    PUTC_n = (int) wcrtomb(PUTC_buf, ch->chars[PUTC_i], &PUT_st);
		    if (PUTC_n <= 0) {
			if (PUTC_ch != L'\0') {
			    /* it could not be a multibyte sequence */
			    (void) _nc_trace_bufcat(bufnum,
						    _nc_tracechar(CURRENT_SCREEN,
								  UChar(ch->chars[PUTC_i])));
			}
			break;
		    } else if (ch->chars[PUTC_i] > 255) {
			char temp[80];
			_nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
				    "{%d:\\u%lx}",
				    wcwidth(ch->chars[PUTC_i]),
				    (unsigned long) ch->chars[PUTC_i]);
			(void) _nc_trace_bufcat(bufnum, temp);
			break;
		    }
		    for (n = 0; n < PUTC_n; n++) {
			if (n)
			    (void) _nc_trace_bufcat(bufnum, ", ");
			(void) _nc_trace_bufcat(bufnum,
						_nc_tracechar(CURRENT_SCREEN,
							      UChar(PUTC_buf[n])));
		    }
		}
		(void) _nc_trace_bufcat(bufnum, " }");
	    }
	    if (attr != A_NORMAL) {
		(void) _nc_trace_bufcat(bufnum, " | ");
		(void) _nc_trace_bufcat(bufnum, _traceattr2(bufnum + 20, attr));
	    }
#if NCURSES_EXT_COLORS
	    /*
	     * Just in case the extended color is different from the chtype
	     * value, trace both.
	     */
	    if (ch->ext_color != PairNumber(attr)) {
		char temp[80];
		_nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
			    " X_COLOR{%d:%d}", ch->ext_color, PairNumber(attr));
		(void) _nc_trace_bufcat(bufnum, temp);
	    }
#endif
	}

	result = _nc_trace_bufcat(bufnum, r_brace);
    }
    return result;
}