blob: 59a88c457402cc0a4ab43325e750246a0634ee56 [file] [log] [blame]
/****************************************************************************
* Copyright (c) 1998-2014,2015 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. *
****************************************************************************/
/****************************************************************************
NAME
ncurses.c --- ncurses library exerciser
SYNOPSIS
ncurses
DESCRIPTION
An interactive test module for the ncurses library.
AUTHOR
Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
Thomas E. Dickey (beginning revision 1.27 in 1996).
$Id: ncurses.c,v 1.420 2015/05/23 23:41:25 tom Exp $
***************************************************************************/
#include <test.priv.h>
#ifdef __hpux
#undef mvwdelch /* HPUX 11.23 macro will not compile */
#endif
#if HAVE_GETTIMEOFDAY
#if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
#include <sys/time.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif
#if USE_LIBPANEL
#include <panel.h>
#endif
#if USE_LIBMENU
#include <menu.h>
#endif
#if USE_LIBFORM
#include <form.h>
#endif
#ifdef NCURSES_VERSION
#define NCURSES_CONST_PARAM const void
#ifdef TRACE
static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
extern unsigned _nc_tracing;
#endif
#else
#define NCURSES_CONST_PARAM char
#define mmask_t chtype /* not specified in XSI */
#ifndef ACS_S3
#ifdef CURSES_ACS_ARRAY
#define ACS_S3 (CURSES_ACS_ARRAY['p']) /* scan line 3 */
#define ACS_S7 (CURSES_ACS_ARRAY['r']) /* scan line 7 */
#define ACS_LEQUAL (CURSES_ACS_ARRAY['y']) /* less/equal */
#define ACS_GEQUAL (CURSES_ACS_ARRAY['z']) /* greater/equal */
#define ACS_PI (CURSES_ACS_ARRAY['{']) /* Pi */
#define ACS_NEQUAL (CURSES_ACS_ARRAY['|']) /* not equal */
#define ACS_STERLING (CURSES_ACS_ARRAY['}']) /* UK pound sign */
#else
#define ACS_S3 (A_ALTCHARSET + 'p') /* scan line 3 */
#define ACS_S7 (A_ALTCHARSET + 'r') /* scan line 7 */
#define ACS_LEQUAL (A_ALTCHARSET + 'y') /* less/equal */
#define ACS_GEQUAL (A_ALTCHARSET + 'z') /* greater/equal */
#define ACS_PI (A_ALTCHARSET + '{') /* Pi */
#define ACS_NEQUAL (A_ALTCHARSET + '|') /* not equal */
#define ACS_STERLING (A_ALTCHARSET + '}') /* UK pound sign */
#endif
#endif /* ACS_S3 */
#ifndef WACS_S3
#ifdef CURSES_WACS_ARRAY
#define WACS_S3 (&(CURSES_WACS_ARRAY['p'])) /* scan line 3 */
#define WACS_S7 (&(CURSES_WACS_ARRAY['r'])) /* scan line 7 */
#define WACS_LEQUAL (&(CURSES_WACS_ARRAY['y'])) /* less/equal */
#define WACS_GEQUAL (&(CURSES_WACS_ARRAY['z'])) /* greater/equal */
#define WACS_PI (&(CURSES_WACS_ARRAY['{'])) /* Pi */
#define WACS_NEQUAL (&(CURSES_WACS_ARRAY['|'])) /* not equal */
#define WACS_STERLING (&(CURSES_WACS_ARRAY['}'])) /* UK pound sign */
#endif
#endif
#endif
#if HAVE_WCSRTOMBS
#define count_wchars(src, len, state) wcsrtombs(0, &src, len, state)
#define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
#define reset_wchars(state) init_mb(state)
#elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
#define count_wchars(src, len, state) wcstombs(0, src, len)
#define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
#define reset_wchars(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
#define state_unused
#endif
#if HAVE_MBSRTOWCS
#define count_mbytes(src, len, state) mbsrtowcs(0, &src, len, state)
#define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
#define reset_mbytes(state) init_mb(state)
#elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
#define count_mbytes(src, len, state) mbstowcs(0, src, len)
#define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
#define reset_mbytes(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
#define state_unused
#endif
#define ToggleAcs(temp,real) temp = ((temp == real) ? 0 : real)
#define P(string) printw("%s\n", string)
#define BLANK ' ' /* this is the background character */
#undef max_colors
static int max_colors; /* the actual number of colors we'll use */
static int min_colors; /* the minimum color code */
static bool use_colors; /* true if we use colors */
#undef max_pairs
static int max_pairs; /* ...and the number of color pairs */
typedef struct {
NCURSES_COLOR_T red;
NCURSES_COLOR_T green;
NCURSES_COLOR_T blue;
} RGB_DATA;
static RGB_DATA *all_colors;
static void main_menu(bool);
static void
failed(const char *s)
{
perror(s);
endwin();
ExitProgram(EXIT_FAILURE);
}
/* The behavior of mvhline, mvvline for negative/zero length is unspecified,
* though we can rely on negative x/y values to stop the macro.
*/
static void
do_h_line(int y, int x, chtype c, int to)
{
if ((to) > (x))
MvHLine(y, x, c, (to) - (x));
}
static void
do_v_line(int y, int x, chtype c, int to)
{
if ((to) > (y))
MvVLine(y, x, c, (to) - (y));
}
static void
Repaint(void)
{
touchwin(stdscr);
touchwin(curscr);
wrefresh(curscr);
}
static bool
isQuit(int c)
{
return ((c) == QUIT || (c) == ESCAPE);
}
#define case_QUIT QUIT: case ESCAPE
/* Common function to allow ^T to toggle trace-mode in the middle of a test
* so that trace-files can be made smaller.
*/
static int
wGetchar(WINDOW *win)
{
int c;
#ifdef TRACE
while ((c = wgetch(win)) == CTRL('T')) {
if (_nc_tracing) {
save_trace = _nc_tracing;
Trace(("TOGGLE-TRACING OFF"));
_nc_tracing = 0;
} else {
_nc_tracing = save_trace;
}
trace(_nc_tracing);
if (_nc_tracing)
Trace(("TOGGLE-TRACING ON"));
}
#else
c = wgetch(win);
#endif
return c;
}
#define Getchar() wGetchar(stdscr)
/* replaces wgetnstr(), since we want to be able to edit values */
static void
wGetstring(WINDOW *win, char *buffer, int limit)
{
int y0, x0, x, ch;
bool done = FALSE;
echo();
getyx(win, y0, x0);
(void) wattrset(win, A_REVERSE);
x = (int) strlen(buffer);
while (!done) {
if (x > (int) strlen(buffer))
x = (int) strlen(buffer);
wmove(win, y0, x0);
wprintw(win, "%-*s", limit, buffer);
wmove(win, y0, x0 + x);
switch (ch = wGetchar(win)) {
case '\n':
case KEY_ENTER:
done = TRUE;
break;
case CTRL('U'):
*buffer = '\0';
break;
case '\b':
case KEY_BACKSPACE:
case KEY_DC:
if (x > 0) {
int j;
for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
;
}
} else {
beep();
}
break;
case KEY_LEFT:
if (x > 0) {
--x;
} else {
flash();
}
break;
case KEY_RIGHT:
++x;
break;
default:
if (!isprint(ch) || ch >= KEY_MIN) {
beep();
} else if ((int) strlen(buffer) < limit) {
int j;
for (j = (int) strlen(buffer) + 1; j > x; --j) {
buffer[j] = buffer[j - 1];
}
buffer[x++] = (char) ch;
} else {
flash();
}
}
}
wattroff(win, A_REVERSE);
wmove(win, y0, x0);
noecho();
}
#if USE_WIDEC_SUPPORT
static wchar_t
fullwidth_digit(int ch)
{
return (wchar_t) (ch + 0xff10 - '0');
}
static void
make_fullwidth_text(wchar_t *target, const char *source)
{
int ch;
while ((ch = *source++) != 0) {
*target++ = fullwidth_digit(ch);
}
*target = 0;
}
static void
make_narrow_text(wchar_t *target, const char *source)
{
int ch;
while ((ch = *source++) != 0) {
*target++ = (wchar_t) ch;
}
*target = 0;
}
#if USE_LIBPANEL
static void
make_fullwidth_digit(cchar_t *target, int digit)
{
wchar_t source[2];
source[0] = fullwidth_digit(digit + '0');
source[1] = 0;
setcchar(target, source, A_NORMAL, 0, 0);
}
#endif
static int
wGet_wchar(WINDOW *win, wint_t *result)
{
int c;
#ifdef TRACE
while ((c = wget_wch(win, result)) == CTRL('T')) {
if (_nc_tracing) {
save_trace = _nc_tracing;
Trace(("TOGGLE-TRACING OFF"));
_nc_tracing = 0;
} else {
_nc_tracing = save_trace;
}
trace(_nc_tracing);
if (_nc_tracing)
Trace(("TOGGLE-TRACING ON"));
}
#else
c = wget_wch(win, result);
#endif
return c;
}
#define Get_wchar(result) wGet_wchar(stdscr, result)
/* replaces wgetn_wstr(), since we want to be able to edit values */
static void
wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
{
int y0, x0, x;
wint_t ch;
bool done = FALSE;
bool fkey = FALSE;
echo();
getyx(win, y0, x0);
(void) wattrset(win, A_REVERSE);
x = (int) wcslen(buffer);
while (!done) {
if (x > (int) wcslen(buffer))
x = (int) wcslen(buffer);
/* clear the "window' */
wmove(win, y0, x0);
wprintw(win, "%*s", limit, " ");
/* write the existing buffer contents */
wmove(win, y0, x0);
waddnwstr(win, buffer, limit);
/* positions the cursor past character 'x' */
wmove(win, y0, x0);
waddnwstr(win, buffer, x);
switch (wGet_wchar(win, &ch)) {
case KEY_CODE_YES:
fkey = TRUE;
switch (ch) {
case KEY_ENTER:
ch = '\n';
fkey = FALSE;
break;
case KEY_BACKSPACE:
case KEY_DC:
ch = '\b';
fkey = FALSE;
break;
case KEY_LEFT:
case KEY_RIGHT:
break;
default:
ch = (wint_t) -1;
break;
}
break;
case OK:
fkey = FALSE;
break;
default:
ch = (wint_t) -1;
fkey = TRUE;
break;
}
switch (ch) {
case '\n':
done = TRUE;
break;
case CTRL('U'):
*buffer = '\0';
break;
case '\b':
if (x > 0) {
int j;
for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
;
}
} else {
beep();
}
break;
case KEY_LEFT:
if (x > 0) {
--x;
} else {
beep();
}
break;
case KEY_RIGHT:
++x;
break;
default:
if (fkey) {
beep();
} else if ((int) wcslen(buffer) < limit) {
int j;
for (j = (int) wcslen(buffer) + 1; j > x; --j) {
buffer[j] = buffer[j - 1];
}
buffer[x++] = (wchar_t) ch;
} else {
beep();
}
}
}
wattroff(win, A_REVERSE);
wmove(win, y0, x0);
noecho();
}
#endif
static void
Pause(void)
{
move(LINES - 1, 0);
addstr("Press any key to continue... ");
(void) Getchar();
}
static void
Cannot(const char *what)
{
printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
Pause();
}
static void
ShellOut(bool message)
{
if (message)
addstr("Shelling out...");
def_prog_mode();
endwin();
#ifdef __MINGW32__
system("cmd.exe");
#else
IGNORE_RC(system("sh"));
#endif
if (message)
addstr("returned from shellout.\n");
refresh();
}
#ifdef NCURSES_MOUSE_VERSION
/*
* This function is the same as _tracemouse(), but we cannot count on that
* being available in the non-debug library.
*/
static const char *
mouse_decode(MEVENT const *ep)
{
static char buf[80 + (5 * 10) + (32 * 15)];
(void) sprintf(buf, "id %2d at (%2d, %2d, %d) state %4lx = {",
ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
#define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
SHOW(BUTTON1_RELEASED, "release-1");
SHOW(BUTTON1_PRESSED, "press-1");
SHOW(BUTTON1_CLICKED, "click-1");
SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
#endif
SHOW(BUTTON2_RELEASED, "release-2");
SHOW(BUTTON2_PRESSED, "press-2");
SHOW(BUTTON2_CLICKED, "click-2");
SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
#endif
SHOW(BUTTON3_RELEASED, "release-3");
SHOW(BUTTON3_PRESSED, "press-3");
SHOW(BUTTON3_CLICKED, "click-3");
SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
#endif
SHOW(BUTTON4_RELEASED, "release-4");
SHOW(BUTTON4_PRESSED, "press-4");
SHOW(BUTTON4_CLICKED, "click-4");
SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
#endif
#if NCURSES_MOUSE_VERSION == 2
SHOW(BUTTON5_RELEASED, "release-5");
SHOW(BUTTON5_PRESSED, "press-5");
SHOW(BUTTON5_CLICKED, "click-5");
SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
#endif
SHOW(BUTTON_CTRL, "ctrl");
SHOW(BUTTON_SHIFT, "shift");
SHOW(BUTTON_ALT, "alt");
SHOW(ALL_MOUSE_EVENTS, "all-events");
SHOW(REPORT_MOUSE_POSITION, "position");
#undef SHOW
if (buf[strlen(buf) - 1] == ' ')
buf[strlen(buf) - 2] = '\0';
(void) strcat(buf, "}");
return (buf);
}
static void
show_mouse(WINDOW *win)
{
int y, x;
MEVENT event;
bool outside;
bool show_loc;
getmouse(&event);
outside = !wenclose(win, event.y, event.x);
if (outside) {
(void) wstandout(win);
waddstr(win, "KEY_MOUSE");
(void) wstandend(win);
} else {
waddstr(win, "KEY_MOUSE");
}
wprintw(win, ", %s", mouse_decode(&event));
if (outside)
win = stdscr;
show_loc = wmouse_trafo(win, &event.y, &event.x, FALSE);
if (show_loc) {
getyx(win, y, x);
wmove(win, event.y, event.x);
waddch(win, '*');
wmove(win, y, x);
}
if (outside)
wnoutrefresh(win);
}
#endif /* NCURSES_MOUSE_VERSION */
/****************************************************************************
*
* Character input test
*
****************************************************************************/
#define NUM_GETCH_FLAGS 256
typedef bool GetchFlags[NUM_GETCH_FLAGS];
static void
setup_getch(WINDOW *win, GetchFlags flags)
{
keypad(win, flags['k']); /* should be redundant, but for testing */
meta(win, flags['m']); /* force this to a known state */
if (flags['e'])
echo();
else
noecho();
}
static void
init_getch(WINDOW *win, GetchFlags flags)
{
memset(flags, FALSE, NUM_GETCH_FLAGS);
flags[UChar('k')] = (win == stdscr);
flags[UChar('m')] = TRUE;
setup_getch(win, flags);
}
static void
wgetch_help(WINDOW *win, GetchFlags flags)
{
static const char *help[] =
{
"e -- toggle echo mode"
,"g -- triggers a getstr test"
,"k -- toggle keypad/literal mode"
,"m -- toggle meta (7-bit/8-bit) mode"
,"^q -- quit"
,"s -- shell out\n"
,"w -- create a new window"
#ifdef SIGTSTP
,"z -- suspend this process"
#endif
};
int y, x;
unsigned chk = ((SIZEOF(help) + 1) / 2);
unsigned n;
getyx(win, y, x);
move(0, 0);
printw("Type any key to see its %s value. Also:\n",
flags['k'] ? "keypad" : "literal");
for (n = 0; n < SIZEOF(help); ++n) {
int row = 1 + (int) (n % chk);
int col = (n >= chk) ? COLS / 2 : 0;
int flg = ((strstr(help[n], "toggle") != 0)
&& (flags[UChar(*help[n])] != FALSE));
if (flg)
(void) standout();
MvPrintw(row, col, "%s", help[n]);
if (col == 0)
clrtoeol();
if (flg)
(void) standend();
}
wrefresh(stdscr);
wmove(win, y, x);
}
static void
wgetch_wrap(WINDOW *win, int first_y)
{
int last_y = getmaxy(win) - 1;
int y = getcury(win) + 1;
if (y >= last_y)
y = first_y;
wmove(win, y, 0);
wclrtoeol(win);
}
#if defined(KEY_RESIZE) && HAVE_WRESIZE
typedef struct {
WINDOW *text;
WINDOW *frame;
} WINSTACK;
static WINSTACK *winstack = 0;
static unsigned len_winstack = 0;
static void
forget_boxes(void)
{
if (winstack != 0) {
free(winstack);
}
winstack = 0;
len_winstack = 0;
}
static void
remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
{
unsigned need = (level + 1) * 2;
assert(level < (unsigned) COLS);
if (winstack == 0) {
len_winstack = 20;
winstack = typeMalloc(WINSTACK, len_winstack);
} else if (need >= len_winstack) {
len_winstack = need;
winstack = typeRealloc(WINSTACK, len_winstack, winstack);
}
if (!winstack)
failed("remember_boxes");
winstack[level].text = txt_win;
winstack[level].frame = box_win;
}
#if USE_SOFTKEYS && (defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
static void
slk_repaint(void)
{
/* this chunk is now done in resize_term() */
slk_touch();
slk_clear();
slk_noutrefresh();
}
#else
#define slk_repaint() /* nothing */
#endif
#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
/*
* For wgetch_test(), we create pairs of windows - one for a box, one for text.
* Resize both and paint the box in the parent.
*/
static void
resize_boxes(unsigned level, WINDOW *win)
{
unsigned n;
int base = 5;
int high = LINES - base;
int wide = COLS;
touchwin(stdscr);
wnoutrefresh(stdscr);
slk_repaint();
for (n = 0; n < level; ++n) {
wresize(winstack[n].frame, high, wide);
wresize(winstack[n].text, high - 2, wide - 2);
high -= 2;
wide -= 2;
werase(winstack[n].text);
box(winstack[n].frame, 0, 0);
wnoutrefresh(winstack[n].frame);
wprintw(winstack[n].text,
"size %dx%d\n",
getmaxy(winstack[n].text),
getmaxx(winstack[n].text));
wnoutrefresh(winstack[n].text);
if (winstack[n].text == win)
break;
}
doupdate();
}
#endif /* resize_boxes */
#else
#define forget_boxes() /* nothing */
#define remember_boxes(level,text,frame) /* nothing */
#endif
/*
* Return-code is OK/ERR or a keyname.
*/
static const char *
ok_keyname(int code)
{
return ((code == OK) ? "OK" : ((code == ERR) ? "ERR" : keyname(code)));
}
static void
wgetch_test(unsigned level, WINDOW *win, int delay)
{
char buf[BUFSIZ];
int first_y, first_x;
int c;
int incount = 0;
GetchFlags flags;
bool blocking = (delay < 0);
init_getch(win, flags);
wtimeout(win, delay);
getyx(win, first_y, first_x);
wgetch_help(win, flags);
wsetscrreg(win, first_y, getmaxy(win) - 1);
scrollok(win, TRUE);
for (;;) {
while ((c = wGetchar(win)) == ERR) {
incount++;
if (blocking) {
(void) wprintw(win, "%05d: input error", incount);
break;
} else {
(void) wprintw(win, "%05d: input timed out", incount);
}
wgetch_wrap(win, first_y);
}
if (c == ERR && blocking) {
wprintw(win, "ERR");
wgetch_wrap(win, first_y);
} else if (isQuit(c)) {
break;
} else if (c == 'e') {
flags[UChar('e')] = !flags[UChar('e')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'g') {
waddstr(win, "getstr test: ");
echo();
c = wgetnstr(win, buf, sizeof(buf) - 1);
noecho();
wprintw(win, "I saw %d characters:\n\t`%s' (%s).",
(int) strlen(buf), buf,
ok_keyname(c));
wclrtoeol(win);
wgetch_wrap(win, first_y);
} else if (c == 'k') {
flags[UChar('k')] = !flags[UChar('k')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'm') {
flags[UChar('m')] = !flags[UChar('m')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 's') {
ShellOut(TRUE);
} else if (c == 'w') {
int high = getmaxy(win) - 1 - first_y + 1;
int wide = getmaxx(win) - first_x;
int old_y, old_x;
int new_y = first_y + getbegy(win);
int new_x = first_x + getbegx(win);
getyx(win, old_y, old_x);
if (high > 2 && wide > 2) {
WINDOW *wb = newwin(high, wide, new_y, new_x);
WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
box(wb, 0, 0);
wrefresh(wb);
wmove(wi, 0, 0);
remember_boxes(level, wi, wb);
wgetch_test(level + 1, wi, delay);
delwin(wi);
delwin(wb);
wgetch_help(win, flags);
wmove(win, old_y, old_x);
touchwin(win);
wrefresh(win);
doupdate();
}
#ifdef SIGTSTP
} else if (c == 'z') {
kill(getpid(), SIGTSTP);
#endif
} else {
wprintw(win, "Key pressed: %04o ", c);
#ifdef NCURSES_MOUSE_VERSION
if (c == KEY_MOUSE) {
show_mouse(win);
} else
#endif /* NCURSES_MOUSE_VERSION */
if (c >= KEY_MIN) {
#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
if (c == KEY_RESIZE) {
resize_boxes(level, win);
}
#endif
(void) waddstr(win, keyname(c));
} else if (c >= 0x80) {
unsigned c2 = (unsigned) c;
#if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
/* at least Solaris SVR4 curses breaks unctrl(128), etc. */
c2 &= 0x7f;
#endif
if (isprint(c))
(void) wprintw(win, "%c", UChar(c));
else if (c2 != UChar(c))
(void) wprintw(win, "M-%s", unctrl(c2));
else
(void) wprintw(win, "%s", unctrl(c2));
waddstr(win, " (high-half character)");
} else {
if (isprint(c))
(void) wprintw(win, "%c (ASCII printable character)", c);
else
(void) wprintw(win, "%s (ASCII control character)",
unctrl(UChar(c)));
}
wgetch_wrap(win, first_y);
}
}
wtimeout(win, -1);
if (!level)
init_getch(win, flags);
}
static int
begin_getch_test(void)
{
char buf[BUFSIZ];
int delay;
refresh();
#ifdef NCURSES_MOUSE_VERSION
mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, (mmask_t *) 0);
#endif
(void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
echo();
getnstr(buf, sizeof(buf) - 1);
noecho();
nonl();
if (isdigit(UChar(buf[0]))) {
delay = atoi(buf) * 100;
} else {
delay = -1;
}
raw();
move(5, 0);
return delay;
}
static void
finish_getch_test(void)
{
#ifdef NCURSES_MOUSE_VERSION
mousemask(0, (mmask_t *) 0);
#endif
erase();
noraw();
nl();
endwin();
}
static void
getch_test(void)
{
int delay = begin_getch_test();
slk_restore();
wgetch_test(0, stdscr, delay);
forget_boxes();
finish_getch_test();
slk_clear();
}
#if USE_WIDEC_SUPPORT
/*
* For wget_wch_test(), we create pairs of windows - one for a box, one for text.
* Resize both and paint the box in the parent.
*/
#if defined(KEY_RESIZE) && HAVE_WRESIZE
static void
resize_wide_boxes(unsigned level, WINDOW *win)
{
unsigned n;
int base = 5;
int high = LINES - base;
int wide = COLS;
touchwin(stdscr);
wnoutrefresh(stdscr);
slk_repaint();
for (n = 0; n < level; ++n) {
wresize(winstack[n].frame, high, wide);
wresize(winstack[n].text, high - 2, wide - 2);
high -= 2;
wide -= 2;
werase(winstack[n].text);
box_set(winstack[n].frame, 0, 0);
wnoutrefresh(winstack[n].frame);
wprintw(winstack[n].text,
"size %dx%d\n",
getmaxy(winstack[n].text),
getmaxx(winstack[n].text));
wnoutrefresh(winstack[n].text);
if (winstack[n].text == win)
break;
}
doupdate();
}
#endif /* KEY_RESIZE */
static char *
wcstos(const wchar_t *src)
{
int need;
char *result = 0;
const wchar_t *tmp = src;
#ifndef state_unused
mbstate_t state;
#endif
reset_wchars(state);
if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
unsigned have = (unsigned) need;
if ((result = typeCalloc(char, have + 1)) != 0) {
tmp = src;
if (trans_wchars(result, tmp, have, &state) != have) {
free(result);
result = 0;
}
} else {
failed("wcstos");
}
}
return result;
}
static void
wget_wch_test(unsigned level, WINDOW *win, int delay)
{
wchar_t wchar_buf[BUFSIZ];
wint_t wint_buf[BUFSIZ];
int first_y, first_x;
wint_t c;
int incount = 0;
GetchFlags flags;
bool blocking = (delay < 0);
int code;
char *temp;
init_getch(win, flags);
wtimeout(win, delay);
getyx(win, first_y, first_x);
wgetch_help(win, flags);
wsetscrreg(win, first_y, getmaxy(win) - 1);
scrollok(win, TRUE);
for (;;) {
while ((code = wGet_wchar(win, &c)) == ERR) {
incount++;
if (blocking) {
(void) wprintw(win, "%05d: input error", incount);
break;
} else {
(void) wprintw(win, "%05d: input timed out", incount);
}
wgetch_wrap(win, first_y);
}
if (code == ERR && blocking) {
wprintw(win, "ERR");
wgetch_wrap(win, first_y);
} else if (isQuit((int) c)) {
break;
} else if (c == 'e') {
flags[UChar('e')] = !flags[UChar('e')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'g') {
waddstr(win, "getstr test: ");
echo();
code = wgetn_wstr(win, wint_buf, BUFSIZ - 1);
noecho();
if (code == ERR) {
wprintw(win, "wgetn_wstr returns an error.");
} else {
int n;
for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
;
}
if ((temp = wcstos(wchar_buf)) != 0) {
wprintw(win, "I saw %d characters:\n\t`%s'.",
(int) wcslen(wchar_buf), temp);
free(temp);
} else {
wprintw(win, "I saw %d characters (cannot convert).",
(int) wcslen(wchar_buf));
}
}
wclrtoeol(win);
wgetch_wrap(win, first_y);
} else if (c == 'k') {
flags[UChar('k')] = !flags[UChar('k')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'm') {
flags[UChar('m')] = !flags[UChar('m')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 's') {
ShellOut(TRUE);
} else if (c == 'w') {
int high = getmaxy(win) - 1 - first_y + 1;
int wide = getmaxx(win) - first_x;
int old_y, old_x;
int new_y = first_y + getbegy(win);
int new_x = first_x + getbegx(win);
getyx(win, old_y, old_x);
if (high > 2 && wide > 2) {
WINDOW *wb = newwin(high, wide, new_y, new_x);
WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
box_set(wb, 0, 0);
wrefresh(wb);
wmove(wi, 0, 0);
remember_boxes(level, wi, wb);
wget_wch_test(level + 1, wi, delay);
delwin(wi);
delwin(wb);
wgetch_help(win, flags);
wmove(win, old_y, old_x);
touchwin(win);
wrefresh(win);
}
#ifdef SIGTSTP
} else if (c == 'z') {
kill(getpid(), SIGTSTP);
#endif
} else {
wprintw(win, "Key pressed: %04o ", (int) c);
#ifdef NCURSES_MOUSE_VERSION
if (c == KEY_MOUSE) {
show_mouse(win);
} else
#endif /* NCURSES_MOUSE_VERSION */
if (code == KEY_CODE_YES) {
#if defined(KEY_RESIZE) && HAVE_WRESIZE
if (c == KEY_RESIZE) {
resize_wide_boxes(level, win);
}
#endif
(void) waddstr(win, keyname((wchar_t) c));
} else {
(void) waddstr(win, key_name((wchar_t) c));
if (c < 256 && iscntrl(c)) {
(void) wprintw(win, " (control character)");
} else {
(void) wprintw(win, " = %#x (printable character)",
(unsigned) c);
}
}
wgetch_wrap(win, first_y);
}
}
wtimeout(win, -1);
if (!level)
init_getch(win, flags);
}
static void
get_wch_test(void)
{
int delay = begin_getch_test();
slk_restore();
wget_wch_test(0, stdscr, delay);
forget_boxes();
finish_getch_test();
slk_clear();
}
#endif
/****************************************************************************
*
* Character attributes test
*
****************************************************************************/
#if HAVE_SETUPTERM || HAVE_TGETENT
#define get_ncv() TIGETNUM("ncv","NC")
#define get_xmc() TIGETNUM("xmc","sg")
#else
#define get_ncv() -1
#define get_xmc() -1
#endif
#if !HAVE_TERMATTRS
static chtype
my_termattrs(void)
{
static int first = TRUE;
static chtype result = 0;
if (first) {
#if !HAVE_TIGETSTR
char buffer[4096];
char parsed[4096];
char *area_pointer = parsed;
tgetent(buffer, getenv("TERM"));
#endif
if (TIGETSTR("smso", "so"))
result |= A_STANDOUT;
if (TIGETSTR("smul", "us"))
result |= A_UNDERLINE;
if (TIGETSTR("rev", "mr"))
result |= A_REVERSE;
if (TIGETSTR("blink", "mb"))
result |= A_BLINK;
if (TIGETSTR("dim", "mh"))
result |= A_DIM;
if (TIGETSTR("bold", "md"))
result |= A_BOLD;
if (TIGETSTR("smacs", "ac"))
result |= A_ALTCHARSET;
first = FALSE;
}
return result;
}
#define termattrs() my_termattrs()
#endif
#define ATTRSTRING_1ST 32 /* ' ' */
#define ATTRSTRING_END 126 /* '~' */
#define COL_ATTRSTRING 25
#define MARGIN_4_ATTRS (COL_ATTRSTRING + 8)
#define LEN_ATTRSTRING (COLS - MARGIN_4_ATTRS)
#define MAX_ATTRSTRING (ATTRSTRING_END + 1 - ATTRSTRING_1ST)
static char attr_test_string[MAX_ATTRSTRING + 1];
static void
attr_legend(WINDOW *helpwin)
{
int row = 1;
int col = 1;
MvWPrintw(helpwin, row++, col,
"ESC to exit.");
MvWPrintw(helpwin, row++, col,
"^L repaints.");
++row;
MvWPrintw(helpwin, row++, col,
"Modify the test strings:");
MvWPrintw(helpwin, row++, col,
" A digit sets gaps on each side of displayed attributes");
MvWPrintw(helpwin, row++, col,
" </> shifts the text left/right. ");
++row;
MvWPrintw(helpwin, row++, col,
"Toggles:");
if (use_colors) {
MvWPrintw(helpwin, row++, col,
" f/F/b/F toggle foreground/background background color");
MvWPrintw(helpwin, row++, col,
" t/T toggle text/background color attribute");
}
MvWPrintw(helpwin, row++, col,
" a/A toggle ACS (alternate character set) mapping");
MvWPrintw(helpwin, row, col,
" v/V toggle video attribute to combine with each line");
#if USE_WIDEC_SUPPORT
MvWPrintw(helpwin, row, col,
" w/W toggle normal/wide (double-width) test-characters");
#endif
}
static void
show_color_attr(int fg, int bg, int tx)
{
if (use_colors) {
printw(" Colors (fg %d, bg %d", fg, bg);
if (tx >= 0)
printw(", text %d", tx);
printw("),");
}
}
static bool
cycle_color_attr(int ch, NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg, NCURSES_COLOR_T *tx)
{
bool error = FALSE;
if (use_colors) {
switch (ch) {
case 'f':
*fg = (NCURSES_COLOR_T) (*fg + 1);
break;
case 'F':
*fg = (NCURSES_COLOR_T) (*fg - 1);
break;
case 'b':
*bg = (NCURSES_COLOR_T) (*bg + 1);
break;
case 'B':
*bg = (NCURSES_COLOR_T) (*bg - 1);
break;
case 't':
*tx = (NCURSES_COLOR_T) (*tx + 1);
break;
case 'T':
*tx = (NCURSES_COLOR_T) (*tx - 1);
break;
default:
beep();
error = TRUE;
break;
}
if (*fg >= COLORS)
*fg = (NCURSES_COLOR_T) min_colors;
if (*fg < min_colors)
*fg = (NCURSES_COLOR_T) (COLORS - 1);
if (*bg >= COLORS)
*bg = (NCURSES_COLOR_T) min_colors;
if (*bg < min_colors)
*bg = (NCURSES_COLOR_T) (COLORS - 1);
if (*tx >= COLORS)
*tx = -1;
if (*tx < -1)
*tx = (NCURSES_COLOR_T) (COLORS - 1);
} else {
beep();
error = TRUE;
}
return error;
}
static void
adjust_attr_string(int adjust)
{
char save = attr_test_string[0];
int first = ((int) UChar(save)) + adjust;
int j, k;
if (first >= ATTRSTRING_1ST) {
for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
if (k > ATTRSTRING_END)
break;
attr_test_string[j] = (char) k;
if (((k + 1 - first) % 5) == 0) {
if (++j >= MAX_ATTRSTRING)
break;
attr_test_string[j] = ' ';
}
}
if ((LEN_ATTRSTRING - j) > 5) {
attr_test_string[0] = save;
adjust_attr_string(adjust - 1);
} else {
while (j < MAX_ATTRSTRING)
attr_test_string[j++] = ' ';
attr_test_string[j] = '\0';
}
}
}
/*
* Prefer the right-end of the string for starting, since that maps to the
* VT100 line-drawing.
*/
static int
default_attr_string(void)
{
int result = (ATTRSTRING_END - LEN_ATTRSTRING);
result += (LEN_ATTRSTRING / 5);
if (result < ATTRSTRING_1ST)
result = ATTRSTRING_1ST;
return result;
}
static void
init_attr_string(void)
{
attr_test_string[0] = (char) default_attr_string();
adjust_attr_string(0);
}
static int
show_attr(WINDOW *win, int row, int skip, bool arrow, chtype attr, const char *name)
{
int ncv = get_ncv();
chtype test = attr & (chtype) (~A_ALTCHARSET);
if (arrow)
MvPrintw(row, 5, "-->");
MvPrintw(row, 8, "%s mode:", name);
MvPrintw(row, COL_ATTRSTRING - 1, "|");
if (skip)
printw("%*s", skip, " ");
/*
* Just for testing, write text using the alternate character set one
* character at a time (to pass its rendition directly), and use the
* string operation for the other attributes.
*/
wmove(win, 0, 0);
werase(win);
if (attr & A_ALTCHARSET) {
const char *s;
chtype ch;
for (s = attr_test_string; *s != '\0'; ++s) {
ch = UChar(*s);
(void) waddch(win, ch | attr);
}
} else {
(void) wattrset(win, AttrArg(attr, 0));
(void) waddstr(win, attr_test_string);
(void) wattroff(win, (int) attr);
}
if (skip)
printw("%*s", skip, " ");
MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
if (test != A_NORMAL) {
if (!(termattrs() & test)) {
printw(" (N/A)");
} else {
if (ncv > 0 && stdscr && (getbkgd(stdscr) & A_COLOR)) {
static const chtype table[] =
{
A_STANDOUT,
A_UNDERLINE,
A_REVERSE,
A_BLINK,
A_DIM,
A_BOLD,
#ifdef A_INVIS
A_INVIS,
#endif
#ifdef A_ITALIC
A_ITALIC,
#endif
A_PROTECT,
A_ALTCHARSET
};
unsigned n;
bool found = FALSE;
for (n = 0; n < SIZEOF(table); n++) {
if ((table[n] & attr) != 0
&& ((1 << n) & ncv) != 0) {
found = TRUE;
break;
}
}
if (found)
printw(" (NCV)");
}
if ((termattrs() & test) != test)
printw(" (Part)");
}
}
return row + 2;
}
typedef struct {
attr_t attr;
NCURSES_CONST char *name;
} ATTR_TBL;
/* *INDENT-OFF* */
static const ATTR_TBL attrs_to_test[] = {
{ A_STANDOUT, "STANDOUT" },
{ A_REVERSE, "REVERSE" },
{ A_BOLD, "BOLD" },
{ A_UNDERLINE, "UNDERLINE" },
{ A_DIM, "DIM" },
{ A_BLINK, "BLINK" },
{ A_PROTECT, "PROTECT" },
#ifdef A_INVIS
{ A_INVIS, "INVISIBLE" },
#endif
#ifdef A_ITALIC
{ A_ITALIC, "ITALIC" },
#endif
{ A_NORMAL, "NORMAL" },
};
/* *INDENT-ON* */
static unsigned
init_attr_list(ATTR_TBL * target, attr_t attrs)
{
unsigned result = 0;
size_t n;
for (n = 0; n < SIZEOF(attrs_to_test); ++n) {
attr_t test = attrs_to_test[n].attr;
if (test == A_NORMAL || (test & attrs) != 0) {
target[result++] = attrs_to_test[n];
}
}
return result;
}
static bool
attr_getc(int *skip,
NCURSES_COLOR_T *fg,
NCURSES_COLOR_T *bg,
NCURSES_COLOR_T *tx,
int *ac,
unsigned *kc,
unsigned limit)
{
bool result = TRUE;
bool error = FALSE;
WINDOW *helpwin;
do {
int ch = Getchar();
error = FALSE;
if (ch < 256 && isdigit(ch)) {
*skip = (ch - '0');
} else {
switch (ch) {
case CTRL('L'):
Repaint();
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box(helpwin, 0, 0);
attr_legend(helpwin);
wGetchar(helpwin);
delwin(helpwin);
}
break;
case 'a':
*ac = 0;
break;
case 'A':
*ac = A_ALTCHARSET;
break;
case 'v':
if (*kc == 0)
*kc = limit - 1;
else
*kc -= 1;
break;
case 'V':
*kc += 1;
if (*kc >= limit)
*kc = 0;
break;
case '<':
adjust_attr_string(-1);
break;
case '>':
adjust_attr_string(1);
break;
case case_QUIT:
result = FALSE;
break;
default:
error = cycle_color_attr(ch, fg, bg, tx);
break;
}
}
} while (error);
return result;
}
static void
attr_test(void)
/* test text attributes */
{
int n;
int skip = get_xmc();
NCURSES_COLOR_T fg = COLOR_BLACK; /* color pair 0 is special */
NCURSES_COLOR_T bg = COLOR_BLACK;
NCURSES_COLOR_T tx = -1;
int ac = 0;
unsigned j, k;
WINDOW *my_wins[SIZEOF(attrs_to_test)];
ATTR_TBL my_list[SIZEOF(attrs_to_test)];
unsigned my_size = init_attr_list(my_list, termattrs());
if (my_size > 1) {
for (j = 0; j < my_size; ++j) {
my_wins[j] = subwin(stdscr,
1, LEN_ATTRSTRING,
2 + (int) (2 * j), COL_ATTRSTRING);
scrollok(my_wins[j], FALSE);
}
if (skip < 0)
skip = 0;
n = skip; /* make it easy */
k = my_size - 1;
init_attr_string();
do {
int row = 2;
chtype normal = A_NORMAL | BLANK;
chtype extras = (chtype) ac;
if (use_colors) {
NCURSES_PAIRS_T pair = 0;
if ((fg != COLOR_BLACK) || (bg != COLOR_BLACK)) {
pair = 1;
if (init_pair(pair, fg, bg) == ERR) {
beep();
} else {
normal |= (chtype) COLOR_PAIR(pair);
}
}
if (tx >= 0) {
pair = 2;
if (init_pair(pair, tx, bg) == ERR) {
beep();
} else {
extras |= (chtype) COLOR_PAIR(pair);
normal &= ~A_COLOR;
}
}
}
bkgd(normal);
bkgdset(normal);
erase();
box(stdscr, 0, 0);
MvAddStr(0, 20, "Character attribute test display");
for (j = 0; j < my_size; ++j) {
bool arrow = (j == k);
row = show_attr(my_wins[j], row, n, arrow,
normal |
extras |
my_list[j].attr |
my_list[k].attr,
my_list[j].name);
}
MvPrintw(row, 8,
"This terminal does %shave the magic-cookie glitch",
get_xmc() > -1 ? "" : "not ");
MvPrintw(row + 1, 8, "Enter '?' for help.");
show_color_attr(fg, bg, tx);
printw(" ACS (%d)", ac != 0);
refresh();
} while (attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
bkgdset(A_NORMAL | BLANK);
erase();
endwin();
} else {
Cannot("does not support video attributes.");
}
}
#if USE_WIDEC_SUPPORT
static bool use_fullwidth;
static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
#define FULL_LO 0xff00
#define FULL_HI 0xff5e
#define HALF_LO 0x20
#define isFullWidth(ch) ((int)(ch) >= FULL_LO && (int)(ch) <= FULL_HI)
#define ToNormalWidth(ch) (wchar_t) (((int)(ch) - FULL_LO) + HALF_LO)
#define ToFullWidth(ch) (wchar_t) (((int)(ch) - HALF_LO) + FULL_LO)
/*
* Returns an ASCII code in [32..126]
*/
static wchar_t
normal_wchar(int ch)
{
wchar_t result = (wchar_t) ch;
if (isFullWidth(ch))
result = ToNormalWidth(ch);
return result;
}
/*
* Returns either an ASCII code in in [32..126] or full-width in
* [0xff00..0xff5e], according to use_fullwidth setting.
*/
static wchar_t
target_wchar(int ch)
{
wchar_t result = (wchar_t) ch;
if (use_fullwidth) {
if (!isFullWidth(ch))
result = ToFullWidth(ch);
} else {
if (isFullWidth(ch))
result = ToNormalWidth(ch);
}
return result;
}
static void
wide_adjust_attr_string(int adjust)
{
wchar_t save = wide_attr_test_string[0];
int first = ((int) normal_wchar(save)) + adjust;
int j, k;
if (first >= ATTRSTRING_1ST) {
for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
if (k > ATTRSTRING_END)
break;
wide_attr_test_string[j] = target_wchar(k);
if (((k + 1 - first) % 5) == 0) {
if (++j >= MAX_ATTRSTRING)
break;
wide_attr_test_string[j] = ' ';
}
}
if ((LEN_ATTRSTRING - j) > 5) {
wide_attr_test_string[0] = save;
wide_adjust_attr_string(adjust - 1);
} else {
while (j < MAX_ATTRSTRING)
wide_attr_test_string[j++] = ' ';
wide_attr_test_string[j] = '\0';
}
}
}
static void
wide_init_attr_string(void)
{
use_fullwidth = FALSE;
wide_attr_test_string[0] = (wchar_t) default_attr_string();
wide_adjust_attr_string(0);
}
static void
set_wide_background(NCURSES_PAIRS_T pair)
{
cchar_t normal;
wchar_t blank[2];
blank[0] = ' ';
blank[1] = 0;
setcchar(&normal, blank, A_NORMAL, pair, 0);
bkgrnd(&normal);
bkgrndset(&normal);
}
static attr_t
get_wide_background(void)
{
attr_t result = A_NORMAL;
attr_t attr;
cchar_t ch;
NCURSES_PAIRS_T pair;
wchar_t wch[10];
memset(&ch, 0, sizeof(ch));
if (getbkgrnd(&ch) != ERR) {
if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
result = attr;
}
}
return result;
}
static int
wide_show_attr(WINDOW *win,
int row,
int skip,
bool arrow,
chtype attr,
NCURSES_PAIRS_T pair,
const char *name)
{
int ncv = get_ncv();
chtype test = attr & ~WA_ALTCHARSET;
if (arrow)
MvPrintw(row, 5, "-->");
MvPrintw(row, 8, "%s mode:", name);
MvPrintw(row, COL_ATTRSTRING - 1, "|");
if (skip)
printw("%*s", skip, " ");
/*
* Just for testing, write text using the alternate character set one
* character at a time (to pass its rendition directly), and use the
* string operation for the other attributes.
*/
wmove(win, 0, 0);
werase(win);
if (attr & WA_ALTCHARSET) {
const wchar_t *s;
cchar_t ch;
for (s = wide_attr_test_string; *s != L'\0'; ++s) {
wchar_t fill[2];
fill[0] = *s;
fill[1] = L'\0';
setcchar(&ch, fill, attr, pair, 0);
(void) wadd_wch(win, &ch);
}
} else {
attr_t old_attr = 0;
NCURSES_PAIRS_T old_pair = 0;
(void) (wattr_get) (win, &old_attr, &old_pair, 0);
(void) wattr_set(win, attr, pair, 0);
(void) waddwstr(win, wide_attr_test_string);
(void) wattr_set(win, old_attr, old_pair, 0);
}
if (skip)
printw("%*s", skip, " ");
MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
if (test != A_NORMAL) {
if (!(term_attrs() & test)) {
printw(" (N/A)");
} else {
if (ncv > 0 && (get_wide_background() & A_COLOR)) {
static const attr_t table[] =
{
WA_STANDOUT,
WA_UNDERLINE,
WA_REVERSE,
WA_BLINK,
WA_DIM,
WA_BOLD,
WA_INVIS,
WA_PROTECT,
WA_ALTCHARSET
};
unsigned n;
bool found = FALSE;
for (n = 0; n < SIZEOF(table); n++) {
if ((table[n] & attr) != 0
&& ((1 << n) & ncv) != 0) {
found = TRUE;
break;
}
}
if (found)
printw(" (NCV)");
}
if ((term_attrs() & test) != test)
printw(" (Part)");
}
}
return row + 2;
}
static bool
wide_attr_getc(int *skip,
NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg,
NCURSES_COLOR_T *tx, int *ac,
unsigned *kc, unsigned limit)
{
bool result = TRUE;
bool error = FALSE;
WINDOW *helpwin;
do {
int ch = Getchar();
error = FALSE;
if (ch < 256 && isdigit(ch)) {
*skip = (ch - '0');
} else {
switch (ch) {
case CTRL('L'):
Repaint();
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box_set(helpwin, 0, 0);
attr_legend(helpwin);
wGetchar(helpwin);
delwin(helpwin);
}
break;
case 'a':
*ac = 0;
break;
case 'A':
*ac = A_ALTCHARSET;
break;
case 'v':
if (*kc == 0)
*kc = limit - 1;
else
*kc -= 1;
break;
case 'V':
*kc += 1;
if (*kc >= limit)
*kc = 0;
break;
case 'w':
use_fullwidth = FALSE;
wide_adjust_attr_string(0);
break;
case 'W':
use_fullwidth = TRUE;
wide_adjust_attr_string(0);
break;
case '<':
wide_adjust_attr_string(-1);
break;
case '>':
wide_adjust_attr_string(1);
break;
case case_QUIT:
result = FALSE;
break;
default:
error = cycle_color_attr(ch, fg, bg, tx);
break;
}
}
} while (error);
return result;
}
static void
wide_attr_test(void)
/* test text attributes using wide-character calls */
{
int n;
int skip = get_xmc();
NCURSES_COLOR_T fg = COLOR_BLACK; /* color pair 0 is special */
NCURSES_COLOR_T bg = COLOR_BLACK;
NCURSES_COLOR_T tx = -1;
int ac = 0;
unsigned j, k;
ATTR_TBL my_list[SIZEOF(attrs_to_test)];
WINDOW *my_wins[SIZEOF(attrs_to_test)];
unsigned my_size = init_attr_list(my_list, term_attrs());
if (my_size > 1) {
for (j = 0; j < my_size; ++j) {
my_wins[j] = subwin(stdscr,
1, LEN_ATTRSTRING,
2 + (int) (2 * j), COL_ATTRSTRING);
scrollok(my_wins[j], FALSE);
}
if (skip < 0)
skip = 0;
n = skip; /* make it easy */
k = my_size - 1;
wide_init_attr_string();
do {
int row = 2;
NCURSES_PAIRS_T pair = 0;
NCURSES_PAIRS_T extras = 0;
if (use_colors) {
pair = (NCURSES_PAIRS_T) (fg != COLOR_BLACK || bg != COLOR_BLACK);
if (pair != 0) {
pair = 1;
if (init_pair(pair, fg, bg) == ERR) {
beep();
}
}
extras = pair;
if (tx >= 0) {
extras = 2;
if (init_pair(extras, tx, bg) == ERR) {
beep();
}
}
}
set_wide_background(pair);
erase();
box_set(stdscr, 0, 0);
MvAddStr(0, 20, "Character attribute test display");
for (j = 0; j < my_size; ++j) {
row = wide_show_attr(my_wins[j], row, n, (j == k),
((attr_t) ac |
my_list[j].attr |
my_list[k].attr),
extras,
my_list[j].name);
}
MvPrintw(row, 8,
"This terminal does %shave the magic-cookie glitch",
get_xmc() > -1 ? "" : "not ");
MvPrintw(row + 1, 8, "Enter '?' for help.");
show_color_attr(fg, bg, tx);
printw(" ACS (%d)", ac != 0);
refresh();
} while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
set_wide_background(0);
erase();
endwin();
} else {
Cannot("does not support extended video attributes.");
}
}
#endif
/****************************************************************************
*
* Color support tests
*
****************************************************************************/
static NCURSES_CONST char *the_color_names[] =
{
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"BLACK",
"RED",
"GREEN",
"YELLOW",
"BLUE",
"MAGENTA",
"CYAN",
"WHITE"
};
static void
show_color_name(int y, int x, int color, bool wide)
{
if (move(y, x) != ERR) {
char temp[80];
int width = 8;
if (wide) {
sprintf(temp, "%02d", color);
width = 4;
} else if (color >= 8) {
sprintf(temp, "[%02d]", color);
} else if (color < 0) {
strcpy(temp, "default");
} else {
sprintf(temp, "%.*s", 16, the_color_names[color]);
}
printw("%-*.*s", width, width, temp);
}
}
static void
color_legend(WINDOW *helpwin, bool wide)
{
int row = 1;
int col = 1;
MvWPrintw(helpwin, row++, col,
"ESC to exit.");
++row;
MvWPrintw(helpwin, row++, col,
"Use up/down arrow to scroll through the display if it is");
MvWPrintw(helpwin, row++, col,
"longer than one screen. Control/N and Control/P can be used");
MvWPrintw(helpwin, row++, col,
"in place of up/down arrow. Use pageup/pagedown to scroll a");
MvWPrintw(helpwin, row++, col,
"full screen; control/B and control/F can be used here.");
++row;
MvWPrintw(helpwin, row++, col,
"Toggles:");
MvWPrintw(helpwin, row++, col,
" a/A toggle altcharset off/on");
MvWPrintw(helpwin, row++, col,
" b/B toggle bold off/on");
if (has_colors()) {
MvWPrintw(helpwin, row++, col,
" c/C cycle used-colors through 8,16,...,COLORS");
}
MvWPrintw(helpwin, row++, col,
" n/N toggle text/number on/off");
MvWPrintw(helpwin, row++, col,
" r/R toggle reverse on/off");
MvWPrintw(helpwin, row++, col,
" w/W toggle width between 8/16 colors");
#if USE_WIDEC_SUPPORT
if (wide) {
MvWPrintw(helpwin, row++, col,
"Wide characters:");
MvWPrintw(helpwin, row, col,
" x/X toggle text between ASCII and wide-character");
}
#else
(void) wide;
#endif
}
#define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
static int
color_cycle(int current, int step)
{
int result = current;
if (step < 0) {
if (current <= 8) {
result = COLORS;
} else {
result = 8;
if ((result * 2) > COLORS) {
result = COLORS;
} else {
while ((result * 2) < current) {
result *= 2;
}
}
}
} else {
if (current >= COLORS) {
result = 8;
} else {
result *= 2;
}
if (result > COLORS)
result = COLORS;
}
return result;
}
/* generate a color test pattern */
static void
color_test(void)
{
NCURSES_PAIRS_T i;
int top = 0, width;
int base_row = 0;
int grid_top = top + 3;
int page_size = (LINES - grid_top);
int pairs_max;
int colors_max = COLORS;
int row_limit;
int per_row;
char numbered[80];
const char *hello;
bool done = FALSE;
bool opt_acsc = FALSE;
bool opt_bold = FALSE;
bool opt_revs = FALSE;
bool opt_nums = FALSE;
bool opt_wide = FALSE;
WINDOW *helpwin;
while (!done) {
int shown = 0;
pairs_max = PAIR_NUMBER(A_COLOR) + 1;
if (colors_max * colors_max <= COLOR_PAIRS) {
int limit = (colors_max - min_colors) * (colors_max - min_colors);
if (pairs_max > limit)
pairs_max = limit;
} else {
if (pairs_max > COLOR_PAIRS)
pairs_max = COLOR_PAIRS;
}
/* this assumes an 80-column line */
if (opt_wide) {
width = 4;
hello = "Test";
per_row = (colors_max > 8) ? 16 : 8;
} else {
width = 8;
hello = "Hello";
per_row = 8;
}
per_row -= min_colors;
row_limit = (pairs_max + per_row - 1) / per_row;
move(0, 0);
(void) printw("There are %d color pairs and %d colors",
pairs_max, COLORS);
if (colors_max != COLORS)
(void) printw(" (using %d colors)", colors_max);
if (min_colors)
(void) addstr(" besides 'default'");
clrtobot();
MvPrintw(top + 1, 0,
"%dx%d matrix of foreground/background colors, bold *%s*\n",
row_limit,
per_row,
opt_bold ? "on" : "off");
/* show color names/numbers across the top */
for (i = 0; i < per_row; i++)
show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
/* show a grid of colors, with color names/ numbers on the left */
for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
int row = grid_top + (i / per_row) - base_row;
int col = (i % per_row + 1) * width;
NCURSES_PAIRS_T pair = i;
#define InxToFG(i) (NCURSES_COLOR_T) ((i % (colors_max - min_colors)) + min_colors)
#define InxToBG(i) (NCURSES_COLOR_T) ((i / (colors_max - min_colors)) + min_colors)
if (row >= 0 && move(row, col) != ERR) {
NCURSES_COLOR_T fg = InxToFG(i);
NCURSES_COLOR_T bg = InxToBG(i);
init_pair(pair, fg, bg);
attron(COLOR_PAIR(pair));
if (opt_acsc)
attron(A_ALTCHARSET);
if (opt_bold)
attron(A_BOLD);
if (opt_revs)
attron(A_REVERSE);
if (opt_nums) {
sprintf(numbered, "{%02X}", (int) i);
hello = numbered;
}
printw("%-*.*s", width, width, hello);
(void) attrset(A_NORMAL);
if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
show_color_name(row, 0, InxToBG(i), opt_wide);
}
++shown;
} else if (shown) {
break;
}
}
switch (wGetchar(stdscr)) {
case 'a':
opt_acsc = FALSE;
break;
case 'A':
opt_acsc = TRUE;
break;
case 'b':
opt_bold = FALSE;
break;
case 'B':
opt_bold = TRUE;
break;
case 'c':
colors_max = color_cycle(colors_max, -1);
break;
case 'C':
colors_max = color_cycle(colors_max, 1);
break;
case 'n':
opt_nums = FALSE;
break;
case 'N':
opt_nums = TRUE;
break;
case 'r':
opt_revs = FALSE;
break;
case 'R':
opt_revs = TRUE;
break;
case case_QUIT:
done = TRUE;
continue;
case 'w':
set_color_test(opt_wide, FALSE);
break;
case 'W':
set_color_test(opt_wide, TRUE);
break;
case CTRL('p'):
case KEY_UP:
if (base_row <= 0) {
beep();
} else {
base_row -= 1;
}
break;
case CTRL('n'):
case KEY_DOWN:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += 1;
}
break;
case CTRL('b'):
case KEY_PREVIOUS:
case KEY_PPAGE:
if (base_row <= 0) {
beep();
} else {
base_row -= (page_size - 1);
if (base_row < 0)
base_row = 0;
}
break;
case CTRL('f'):
case KEY_NEXT:
case KEY_NPAGE:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += page_size - 1;
if (base_row + page_size >= row_limit) {
base_row = row_limit - page_size - 1;
}
}
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box(helpwin, 0, 0);
color_legend(helpwin, FALSE);
wGetchar(helpwin);
delwin(helpwin);
}
break;
default:
beep();
continue;
}
}
erase();
endwin();
}
#if USE_WIDEC_SUPPORT
/* generate a color test pattern */
static void
wide_color_test(void)
{
int i;
int top = 0, width;
int base_row = 0;
int grid_top = top + 3;
int page_size = (LINES - grid_top);
int pairs_max = (unsigned short) (-1);
int colors_max = COLORS;
int row_limit;
int per_row;
char numbered[80];
const char *hello;
bool done = FALSE;
bool opt_acsc = FALSE;
bool opt_bold = FALSE;
bool opt_revs = FALSE;
bool opt_wide = FALSE;
bool opt_nums = FALSE;
bool opt_xchr = FALSE;
wchar_t buffer[80];
WINDOW *helpwin;
while (!done) {
int shown = 0;
pairs_max = (unsigned short) (-1);
if (colors_max * colors_max <= COLOR_PAIRS) {
int limit = (colors_max - min_colors) * (colors_max - min_colors);
if (pairs_max > limit)
pairs_max = limit;
} else {
if (pairs_max > COLOR_PAIRS)
pairs_max = COLOR_PAIRS;
}
/* this assumes an 80-column line */
if (opt_wide) {
width = 4;
hello = "Test";
per_row = (colors_max > 8) ? 16 : 8;
} else {
width = 8;
hello = "Hello";
per_row = 8;
}
per_row -= min_colors;
if (opt_xchr) {
make_fullwidth_text(buffer, hello);
width *= 2;
per_row /= 2;
} else {
make_narrow_text(buffer, hello);
}
row_limit = (pairs_max + per_row - 1) / per_row;
move(0, 0);
(void) printw("There are %d color pairs and %d colors",
pairs_max, COLORS);
if (colors_max != COLORS)
(void) printw(" (using %d colors)", colors_max);
if (min_colors)
(void) addstr(" besides 'default'");
clrtobot();
MvPrintw(top + 1, 0,
"%dx%d matrix of foreground/background colors, bold *%s*\n",
row_limit,
per_row,
opt_bold ? "on" : "off");
/* show color names/numbers across the top */
for (i = 0; i < per_row; i++)
show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
/* show a grid of colors, with color names/ numbers on the left */
for (i = (base_row * per_row); i < pairs_max; i++) {
int row = grid_top + (i / per_row) - base_row;
int col = (i % per_row + 1) * width;
NCURSES_PAIRS_T pair = (NCURSES_PAIRS_T) i;
if (row >= 0 && move(row, col) != ERR) {
init_pair(pair, InxToFG(i), InxToBG(i));
(void) color_set(pair, NULL);
if (opt_acsc)
attr_on(A_ALTCHARSET, NULL);
if (opt_bold)
attr_on(A_BOLD, NULL);
if (opt_revs)
attr_on(A_REVERSE, NULL);
if (opt_nums) {
sprintf(numbered, "{%02X}", i);
if (opt_xchr) {
make_fullwidth_text(buffer, numbered);
} else {
make_narrow_text(buffer, numbered);
}
}
addnwstr(buffer, width);
(void) attr_set(A_NORMAL, 0, NULL);
if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
show_color_name(row, 0, InxToBG(i), opt_wide);
}
++shown;
} else if (shown) {
break;
}
}
switch (wGetchar(stdscr)) {
case 'a':
opt_acsc = FALSE;
break;
case 'A':
opt_acsc = TRUE;
break;
case 'b':
opt_bold = FALSE;
break;
case 'B':
opt_bold = TRUE;
break;
case 'c':
colors_max = color_cycle(colors_max, -1);
break;
case 'C':
colors_max = color_cycle(colors_max, 1);
break;
case 'n':
opt_nums = FALSE;
break;
case 'N':
opt_nums = TRUE;
break;
case 'r':
opt_revs = FALSE;
break;
case 'R':
opt_revs = TRUE;
break;
case case_QUIT:
done = TRUE;
continue;
case 'w':
set_color_test(opt_wide, FALSE);
break;
case 'W':
set_color_test(opt_wide, TRUE);
break;
case 'x':
opt_xchr = FALSE;
break;
case 'X':
opt_xchr = TRUE;
break;
case CTRL('p'):
case KEY_UP:
if (base_row <= 0) {
beep();
} else {
base_row -= 1;
}
break;
case CTRL('n'):
case KEY_DOWN:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += 1;
}
break;
case CTRL('b'):
case KEY_PREVIOUS:
case KEY_PPAGE:
if (base_row <= 0) {
beep();
} else {
base_row -= (page_size - 1);
if (base_row < 0)
base_row = 0;
}
break;
case CTRL('f'):
case KEY_NEXT:
case KEY_NPAGE:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += page_size - 1;
if (base_row + page_size >= row_limit) {
base_row = row_limit - page_size - 1;
}
}
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box(helpwin, 0, 0);
color_legend(helpwin, TRUE);
wGetchar(helpwin);
delwin(helpwin);
}
break;
default:
beep();
continue;
}
}
erase();
endwin();
}
#endif /* USE_WIDEC_SUPPORT */
static void
change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
{
NCURSES_COLOR_T red, green, blue;
color_content(current, &red, &green, &blue);
switch (field) {
case 0:
red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
break;
case 1:
green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
break;
case 2:
blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
break;
}
if (init_color(current, red, green, blue) == ERR)
beep();
}
static void
init_all_colors(void)
{
NCURSES_PAIRS_T c;
for (c = 0; c < COLORS; ++c)
init_color(c,
all_colors[c].red,
all_colors[c].green,
all_colors[c].blue);
}
#define scaled_rgb(n) ((255 * (n)) / 1000)
static void
color_edit(void)
/* display the color test pattern, without trying to edit colors */
{
int i;
int current = 0;
int this_c = 0, value = 0, field = 0;
int last_c;
int top_color = 0;
int page_size = (LINES - 6);
init_all_colors();
refresh();
for (i = 0; i < max_colors; i++)
init_pair((NCURSES_PAIRS_T) i,
(NCURSES_COLOR_T) COLOR_WHITE,
(NCURSES_COLOR_T) i);
MvPrintw(LINES - 2, 0, "Number: %d", value);
do {
NCURSES_COLOR_T red, green, blue;
attron(A_BOLD);
MvAddStr(0, 20, "Color RGB Value Editing");
attroff(A_BOLD);
for (i = (NCURSES_COLOR_T) top_color;
(i - top_color < page_size)
&& (i < max_colors); i++) {
char numeric[80];
sprintf(numeric, "[%d]", i);
MvPrintw(2 + i - top_color, 0, "%c %-8s:",
(i == current ? '>' : ' '),
(i < (int) SIZEOF(the_color_names)
? the_color_names[i] : numeric));
(void) attrset(AttrArg(COLOR_PAIR(i), 0));
addstr(" ");
(void) attrset(A_NORMAL);
color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
addstr(" R = ");
if (current == i && field == 0)
attron(A_STANDOUT);
printw("%04d", (int) red);
if (current == i && field == 0)
(void) attrset(A_NORMAL);
addstr(", G = ");
if (current == i && field == 1)
attron(A_STANDOUT);
printw("%04d", (int) green);
if (current == i && field == 1)
(void) attrset(A_NORMAL);
addstr(", B = ");
if (current == i && field == 2)
attron(A_STANDOUT);
printw("%04d", (int) blue);
if (current == i && field == 2)
(void) attrset(A_NORMAL);
(void) attrset(A_NORMAL);
printw(" ( %3d %3d %3d )",
(int) scaled_rgb(red),
(int) scaled_rgb(green),
(int)