mirror of
https://github.com/Aigor44/ncursesw-morphos.git
synced 2024-12-27 07:49:06 +08:00
f79b52fddd
+ modify nc_tparm.h, adding guards against repeated inclusion, and allowing TPARM_ARG to be overridden. + fix some strict compiler warnings in ncurses library.
444 lines
12 KiB
C
444 lines
12 KiB
C
/****************************************************************************
|
|
* 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 *
|
|
* "Software"), to deal in the Software without restriction, including *
|
|
* without limitation the rights to use, copy, modify, merge, publish, *
|
|
* distribute, distribute with modifications, sublicense, and/or sell *
|
|
* copies of the Software, and to permit persons to whom the Software is *
|
|
* furnished to do so, subject to the following conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included *
|
|
* in all copies or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
|
|
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
|
|
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
* *
|
|
* Except as contained in this notice, the name(s) of the above copyright *
|
|
* holders shall not be used in advertising or otherwise to promote the *
|
|
* sale, use or other dealings in this Software without prior written *
|
|
* authorization. *
|
|
****************************************************************************/
|
|
|
|
/*
|
|
** lib_add_wch.c
|
|
**
|
|
** The routine wadd_wch().
|
|
**
|
|
*/
|
|
|
|
#include <curses.priv.h>
|
|
|
|
#if HAVE_WCTYPE_H
|
|
#include <wctype.h>
|
|
#endif
|
|
|
|
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 */
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
static NCURSES_INLINE int
|
|
wadd_wch_nosync(WINDOW *win, cchar_t ch)
|
|
/* the workhorse function -- add a character to the given window */
|
|
{
|
|
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);
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
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;
|
|
}
|
|
return OK;
|
|
}
|
|
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);
|
|
}
|