mirror of
https://github.com/Aigor44/ncursesw-morphos.git
synced 2024-12-27 07:49:06 +08:00
11dead3981
+ fix some strict compiler warnings in ncurses library. + modify configure macro CF_HEADER_PATH to not look for variations in the predefined include directories. + improve configure macros CF_GCC_VERSION and CF_GCC_WARNINGS to work with gcc 4.x's c89 alias, which gives warning messages for cases where older versions would produce an error.
569 lines
15 KiB
C
569 lines
15 KiB
C
/****************************************************************************
|
|
* Copyright (c) 1998-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_addch.c
|
|
**
|
|
** The routine waddch().
|
|
**
|
|
*/
|
|
|
|
#include <curses.priv.h>
|
|
#include <ctype.h>
|
|
|
|
MODULE_ID("$Id: lib_addch.c,v 1.124 2010/04/24 22:41:05 tom Exp $")
|
|
|
|
static const NCURSES_CH_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
|
|
* waddch(). 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 NCURSES_CH_T
|
|
render_char(WINDOW *win, NCURSES_CH_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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
#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 = (NCURSES_SIZE_T) save_x;
|
|
win->_cury = (NCURSES_SIZE_T) 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)] = (char) CharOf(CHDEREF(ch));
|
|
WINDOW_EXT(win, addch_used) += 1;
|
|
buffer[WINDOW_EXT(win, addch_used)] = '\0';
|
|
if ((len = (int) 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);
|
|
|
|
line = win->_line + y;
|
|
|
|
CHANGED_CELL(line, x);
|
|
|
|
/*
|
|
* Build up multibyte characters until we have a wide-character.
|
|
*/
|
|
#if NCURSES_SP_FUNCS
|
|
#define DeriveSP() SCREEN *sp = _nc_screen_of(win);
|
|
#else
|
|
#define DeriveSP() /*nothing */
|
|
#endif
|
|
if_WIDEC({
|
|
DeriveSP();
|
|
if (WINDOW_EXT(win, addch_used) != 0 || !Charable(ch)) {
|
|
int len = _nc_build_wch(win, CHREF(ch));
|
|
|
|
if (len >= -1) {
|
|
attr_t attr = AttrOf(ch);
|
|
|
|
/* handle EILSEQ (i.e., when len >= -1) */
|
|
if (len == -1 && is8bits(CharOf(ch))) {
|
|
int rc = OK;
|
|
const char *s = NCURSES_SP_NAME(unctrl)
|
|
(NCURSES_SP_ARGx (chtype) CharOf(ch));
|
|
|
|
if (s[1] != '\0') {
|
|
while (*s != '\0') {
|
|
rc = waddch(win, UChar(*s) | attr);
|
|
if (rc != OK)
|
|
break;
|
|
++s;
|
|
}
|
|
return rc;
|
|
}
|
|
}
|
|
if (len == -1)
|
|
return waddch(win, ' ' | attr);
|
|
} 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;
|
|
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) {
|
|
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))));
|
|
|
|
if (x > win->_maxx) {
|
|
return wrap_to_next_line(win);
|
|
}
|
|
win->_curx = (NCURSES_SIZE_T) x;
|
|
return OK;
|
|
}
|
|
|
|
static NCURSES_INLINE int
|
|
waddch_nosync(WINDOW *win, const NCURSES_CH_T ch)
|
|
/* the workhorse function -- add a character to the given window */
|
|
{
|
|
NCURSES_SIZE_T x, y;
|
|
chtype t = (chtype) CharOf(ch);
|
|
#if USE_WIDEC_SUPPORT || NCURSES_SP_FUNCS || USE_REENTRANT
|
|
SCREEN *sp = _nc_screen_of(win);
|
|
#endif
|
|
const char *s = NCURSES_SP_NAME(unctrl) (NCURSES_SP_ARGx t);
|
|
int tabsize = 8;
|
|
/*
|
|
* 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;
|
|
|
|
switch (t) {
|
|
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)) {
|
|
NCURSES_CH_T blank = blankchar;
|
|
AddAttr(blank, AttrOf(ch));
|
|
while (win->_curx < x) {
|
|
if (waddch_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:
|
|
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);
|
|
}
|
|
|
|
win->_curx = x;
|
|
win->_cury = y;
|
|
|
|
return (OK);
|
|
}
|
|
|
|
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_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)
|
|
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)"), (void *) win,
|
|
_tracechtype(ch)));
|
|
|
|
if (win && (waddch_nosync(win, wch) != ERR)) {
|
|
_nc_synchook(win);
|
|
code = OK;
|
|
}
|
|
|
|
TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
|
|
return (code);
|
|
}
|
|
|
|
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)"),
|
|
(void *) win,
|
|
_tracechtype(ch)));
|
|
|
|
if (win && (waddch_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);
|
|
}
|