aboutsummaryrefslogtreecommitdiff
path: root/contrib/ncurses/test/ncurses.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ncurses/test/ncurses.c')
-rw-r--r--contrib/ncurses/test/ncurses.c3270
1 files changed, 3270 insertions, 0 deletions
diff --git a/contrib/ncurses/test/ncurses.c b/contrib/ncurses/test/ncurses.c
new file mode 100644
index 000000000000..667d2ef6ffc9
--- /dev/null
+++ b/contrib/ncurses/test/ncurses.c
@@ -0,0 +1,3270 @@
+/****************************************************************************
+ * Copyright (c) 1998 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
+
+$Id: ncurses.c,v 1.118 1999/08/10 00:37:30 tom Exp $
+
+***************************************************************************/
+
+#include <test.priv.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+
+#if HAVE_LOCALE_H
+#include <locale.h>
+#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 HAVE_PANEL_H
+#define USE_LIBPANEL 1
+#include <panel.h>
+#else
+#define USE_LIBPANEL 0
+#endif
+
+#if HAVE_MENU_H && HAVE_LIBMENU
+#define USE_LIBMENU 1
+#include <menu.h>
+#else
+#define USE_LIBMENU 0
+#endif
+
+#if HAVE_FORM_H && HAVE_LIBFORM
+#define USE_LIBFORM 1
+#include <form.h>
+#else
+#define USE_LIBFORM 0
+#endif
+
+#ifdef NCURSES_VERSION
+
+#ifdef TRACE
+static int save_trace = TRACE_ORDINARY|TRACE_CALLS;
+extern int _nc_tracing;
+#endif
+
+#if !HAVE_NAPMS
+#define HAVE_NAPMS 1
+#endif
+
+#else
+
+#define mmask_t chtype /* not specified in XSI */
+#define attr_t chtype /* not specified in XSI */
+#define ACS_S3 (acs_map['p']) /* scan line 3 */
+#define ACS_S7 (acs_map['r']) /* scan line 7 */
+#define ACS_LEQUAL (acs_map['y']) /* less/equal */
+#define ACS_GEQUAL (acs_map['z']) /* greater/equal */
+#define ACS_PI (acs_map['{']) /* Pi */
+#define ACS_NEQUAL (acs_map['|']) /* not equal */
+#define ACS_STERLING (acs_map['}']) /* UK pound sign */
+
+#endif
+
+#define P(string) printw("%s\n", string)
+#ifndef CTRL
+#define CTRL(x) ((x) & 0x1f)
+#endif
+
+#define QUIT CTRL('Q')
+#define ESCAPE CTRL('[')
+#define BLANK ' ' /* this is the background character */
+
+/* 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));
+}
+
+/* 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;
+ _tracef("TOGGLE-TRACING OFF");
+ _nc_tracing = 0;
+ } else {
+ _nc_tracing = save_trace;
+ }
+ trace(_nc_tracing);
+ if (_nc_tracing)
+ _tracef("TOGGLE-TRACING ON");
+ }
+#else
+ c = wgetch(win);
+#endif
+ return c;
+}
+#define Getchar() wGetchar(stdscr)
+
+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();
+ system("sh");
+ if (message)
+ addstr("returned from shellout.\n");
+ refresh();
+}
+
+#ifdef NCURSES_MOUSE_VERSION
+static const char *mouse_decode(MEVENT const *ep)
+{
+ static char buf[80];
+
+ (void) sprintf(buf, "id %2d at (%2d, %2d, %2d) state %4lx = {",
+ ep->id, ep->x, ep->y, ep->z, 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")
+ SHOW(BUTTON1_RESERVED_EVENT, "reserved-1")
+ 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")
+ SHOW(BUTTON2_RESERVED_EVENT, "reserved-2")
+ 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")
+ SHOW(BUTTON3_RESERVED_EVENT, "reserved-3")
+ 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")
+ SHOW(BUTTON4_RESERVED_EVENT, "reserved-4")
+ 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);
+}
+#endif /* NCURSES_MOUSE_VERSION */
+
+/****************************************************************************
+ *
+ * Character input test
+ *
+ ****************************************************************************/
+
+static void getch_test(void)
+/* test the keypad feature */
+{
+char buf[BUFSIZ];
+int c;
+int incount = 0, firsttime = 0;
+bool blocking = TRUE;
+int y, x;
+
+ refresh();
+
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(ALL_MOUSE_EVENTS, (mmask_t *)0);
+#endif
+
+ (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
+ echo();
+ getstr(buf);
+ noecho();
+ nonl();
+
+ if (isdigit(buf[0]))
+ {
+ timeout(atoi(buf) * 100);
+ blocking = FALSE;
+ }
+
+ c = '?';
+ raw();
+ for (;;)
+ {
+ if (firsttime++)
+ {
+ printw("Key pressed: %04o ", c);
+#ifdef NCURSES_MOUSE_VERSION
+ if (c == KEY_MOUSE)
+ {
+ MEVENT event;
+
+ getmouse(&event);
+ printw("KEY_MOUSE, %s\n", mouse_decode(&event));
+ }
+ else
+#endif /* NCURSES_MOUSE_VERSION */
+ if (c >= KEY_MIN)
+ {
+ (void) addstr(keyname(c));
+ addch('\n');
+ }
+ else if (c > 0x80)
+ {
+ int c2 = (c & 0x7f);
+ if (isprint(c2))
+ (void) printw("M-%c", c2);
+ else
+ (void) printw("M-%s", unctrl(c2));
+ addstr(" (high-half character)\n");
+ }
+ else
+ {
+ if (isprint(c))
+ (void) printw("%c (ASCII printable character)\n", c);
+ else
+ (void) printw("%s (ASCII control character)\n", unctrl(c));
+ }
+ getyx(stdscr, y, x);
+ if (y >= LINES-1)
+ move(0,0);
+ clrtoeol();
+ }
+
+ if (c == 'g')
+ {
+ addstr("getstr test: ");
+ echo(); getstr(buf); noecho();
+ printw("I saw `%s'.\n", buf);
+ }
+ if (c == 's')
+ {
+ ShellOut(TRUE);
+ }
+ if (c == 'x' || c == 'q' || (c == ERR && blocking))
+ break;
+ if (c == '?')
+ {
+ addstr("Type any key to see its keypad value. Also:\n");
+ addstr("g -- triggers a getstr test\n");
+ addstr("s -- shell out\n");
+ addstr("q -- quit\n");
+ addstr("? -- repeats this help message\n");
+ }
+
+ while ((c = Getchar()) == ERR)
+ if (!blocking)
+ (void) printw("%05d: input timed out\n", incount++);
+ else {
+ (void) printw("%05d: input error\n", incount++);
+ break;
+ }
+ }
+
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(0, (mmask_t *)0);
+#endif
+ timeout(-1);
+ erase();
+ noraw();
+ nl();
+ endwin();
+}
+
+static int show_attr(int row, int skip, chtype attr, const char *name, bool once)
+{
+ int ncv = tigetnum("ncv");
+
+ mvprintw(row, 8, "%s mode:", name);
+ mvprintw(row, 24, "|");
+ if (skip) printw("%*s", skip, " ");
+ if (once)
+ attron(attr);
+ else
+ attrset(attr);
+ addstr("abcde fghij klmno pqrst uvwxy z");
+ if (once)
+ attroff(attr);
+ if (skip) printw("%*s", skip, " ");
+ printw("|");
+ if (attr != A_NORMAL) {
+ if (!(termattrs() & attr)) {
+ printw(" (N/A)");
+ } else if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
+ static const attr_t table[] = {
+ A_STANDOUT,
+ A_UNDERLINE,
+ A_REVERSE,
+ A_BLINK,
+ A_DIM,
+ A_BOLD,
+ A_INVIS,
+ A_PROTECT,
+ A_ALTCHARSET
+ };
+ unsigned n;
+ bool found = FALSE;
+ for (n = 0; n < sizeof(table)/sizeof(table[0]); n++) {
+ if ((table[n] & attr) != 0
+ && ((1 << n) & ncv) != 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found)
+ printw(" (NCV)");
+ }
+ }
+ return row + 2;
+}
+
+static bool attr_getc(int *skip, int *fg, int *bg)
+{
+ int ch = Getchar();
+
+ if (isdigit(ch)) {
+ *skip = (ch - '0');
+ return TRUE;
+ } else if (ch == CTRL('L')) {
+ touchwin(stdscr);
+ touchwin(curscr);
+ return TRUE;
+ } else if (has_colors()) {
+ switch (ch) {
+ case 'f': *fg = (*fg + 1); break;
+ case 'F': *fg = (*fg - 1); break;
+ case 'b': *bg = (*bg + 1); break;
+ case 'B': *bg = (*bg - 1); break;
+ default:
+ return FALSE;
+ }
+ if (*fg >= COLORS) *fg = 0;
+ if (*fg < 0) *fg = COLORS - 1;
+ if (*bg >= COLORS) *bg = 0;
+ if (*bg < 0) *bg = COLORS - 1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void attr_test(void)
+/* test text attributes */
+{
+ int n;
+ int skip = tigetnum("xmc");
+ int fg = COLOR_BLACK; /* color pair 0 is special */
+ int bg = COLOR_BLACK;
+ bool *pairs = (bool *)calloc(COLOR_PAIRS, sizeof(bool));
+ pairs[0] = TRUE;
+
+ if (skip < 0)
+ skip = 0;
+
+ n = skip; /* make it easy */
+
+ do {
+ int row = 2;
+ int normal = A_NORMAL | BLANK;
+
+ if (has_colors()) {
+ int pair = (fg * COLORS) + bg;
+ if (!pairs[pair]) {
+ init_pair(pair, fg, bg);
+ pairs[pair] = TRUE;
+ }
+ normal |= COLOR_PAIR(pair);
+ }
+ bkgdset(normal);
+ erase();
+
+ mvaddstr(0, 20, "Character attribute test display");
+
+ row = show_attr(row, n, A_STANDOUT, "STANDOUT", TRUE);
+ row = show_attr(row, n, A_REVERSE, "REVERSE", TRUE);
+ row = show_attr(row, n, A_BOLD, "BOLD", TRUE);
+ row = show_attr(row, n, A_UNDERLINE, "UNDERLINE", TRUE);
+ row = show_attr(row, n, A_DIM, "DIM", TRUE);
+ row = show_attr(row, n, A_BLINK, "BLINK", TRUE);
+ row = show_attr(row, n, A_PROTECT, "PROTECT", TRUE);
+ row = show_attr(row, n, A_INVIS, "INVISIBLE", TRUE);
+ row = show_attr(row, n, A_NORMAL, "NORMAL", FALSE);
+
+ mvprintw(row, 8,
+ "This terminal does %shave the magic-cookie glitch",
+ tigetnum("xmc") > -1 ? "" : "not ");
+ mvprintw(row+1, 8,
+ "Enter a digit to set gaps on each side of displayed attributes");
+ mvprintw(row+2, 8,
+ "^L = repaint");
+ if (has_colors())
+ printw(". f/F/b/F toggle colors (now %d/%d)", fg, bg);
+
+ refresh();
+ } while (attr_getc(&n, &fg, &bg));
+
+ free((char *)pairs);
+ bkgdset(A_NORMAL | BLANK);
+ erase();
+ endwin();
+}
+
+/****************************************************************************
+ *
+ * Color support tests
+ *
+ ****************************************************************************/
+
+static NCURSES_CONST char *color_names[] =
+{
+ "black",
+ "red",
+ "green",
+ "yellow",
+ "blue",
+ "magenta",
+ "cyan",
+ "white"
+};
+
+static void show_color_name(int y, int x, int color)
+{
+ if (COLORS > 8)
+ mvprintw(y, x, "%02d ", color);
+ else
+ mvaddstr(y, x, color_names[color]);
+}
+
+static void color_test(void)
+/* generate a color test pattern */
+{
+ int i;
+ int base, top, width;
+ NCURSES_CONST char *hello;
+
+ refresh();
+ (void) printw("There are %d color pairs\n", COLOR_PAIRS);
+
+ width = (COLORS > 8) ? 4 : 8;
+ hello = (COLORS > 8) ? "Test" : "Hello";
+
+ for (base = 0; base < 2; base++)
+ {
+ top = (COLORS > 8) ? 0 : base * (COLORS+3);
+ clrtobot();
+ (void) mvprintw(top + 1, 0,
+ "%dx%d matrix of foreground/background colors, bright *%s*\n",
+ COLORS, COLORS,
+ base ? "on" : "off");
+ for (i = 0; i < COLORS; i++)
+ show_color_name(top + 2, (i+1) * width, i);
+ for (i = 0; i < COLORS; i++)
+ show_color_name(top + 3 + i, 0, i);
+ for (i = 1; i < COLOR_PAIRS; i++)
+ {
+ init_pair(i, i % COLORS, i / COLORS);
+ attron((attr_t)COLOR_PAIR(i));
+ if (base)
+ attron((attr_t)A_BOLD);
+ mvaddstr(top + 3 + (i / COLORS), (i % COLORS + 1) * width, hello);
+ attrset(A_NORMAL);
+ }
+ if ((COLORS > 8) || base)
+ Pause();
+ }
+
+ erase();
+ endwin();
+}
+
+static void change_color(int current, int field, int value, int usebase)
+{
+ short red, green, blue;
+
+ if (usebase)
+ color_content(current, &red, &green, &blue);
+ else
+ red = green = blue = 0;
+
+ switch (field) {
+ case 0:
+ red += value;
+ break;
+ case 1:
+ green += value;
+ break;
+ case 2:
+ blue += value;
+ break;
+ }
+
+ if (init_color(current, red, green, blue) == ERR)
+ beep();
+}
+
+static void color_edit(void)
+/* display the color test pattern, without trying to edit colors */
+{
+ int i, this_c = 0, value = 0, current = 0, field = 0;
+ int last_c;
+
+ refresh();
+
+ for (i = 0; i < COLORS; i++)
+ init_pair(i, COLOR_WHITE, i);
+
+ mvprintw(LINES-2, 0, "Number: %d", value);
+
+ do {
+ short red, green, blue;
+
+ attron(A_BOLD);
+ mvaddstr(0, 20, "Color RGB Value Editing");
+ attroff(A_BOLD);
+
+ for (i = 0; i < COLORS; i++)
+ {
+ mvprintw(2 + i, 0, "%c %-8s:",
+ (i == current ? '>' : ' '),
+ (i < (int) SIZEOF(color_names)
+ ? color_names[i] : ""));
+ attrset(COLOR_PAIR(i));
+ addstr(" ");
+ attrset(A_NORMAL);
+
+ /*
+ * Note: this refresh should *not* be necessary! It works around
+ * a bug in attribute handling that apparently causes the A_NORMAL
+ * attribute sets to interfere with the actual emission of the
+ * color setting somehow. This needs to be fixed.
+ */
+ refresh();
+
+ color_content(i, &red, &green, &blue);
+ addstr(" R = ");
+ if (current == i && field == 0) attron(A_STANDOUT);
+ printw("%04d", red);
+ if (current == i && field == 0) attrset(A_NORMAL);
+ addstr(", G = ");
+ if (current == i && field == 1) attron(A_STANDOUT);
+ printw("%04d", green);
+ if (current == i && field == 1) attrset(A_NORMAL);
+ addstr(", B = ");
+ if (current == i && field == 2) attron(A_STANDOUT);
+ printw("%04d", blue);
+ if (current == i && field == 2) attrset(A_NORMAL);
+ attrset(A_NORMAL);
+ addstr(")");
+ }
+
+ mvaddstr(COLORS + 3, 0,
+ "Use up/down to select a color, left/right to change fields.");
+ mvaddstr(COLORS + 4, 0,
+ "Modify field by typing nnn=, nnn-, or nnn+. ? for help.");
+
+ move(2 + current, 0);
+
+ last_c = this_c;
+ this_c = Getchar();
+ if (isdigit(this_c) && !isdigit(last_c))
+ value = 0;
+
+ switch (this_c)
+ {
+ case KEY_UP:
+ current = (current == 0 ? (COLORS - 1) : current - 1);
+ break;
+
+ case KEY_DOWN:
+ current = (current == (COLORS - 1) ? 0 : current + 1);
+ break;
+
+ case KEY_RIGHT:
+ field = (field == 2 ? 0 : field + 1);
+ break;
+
+ case KEY_LEFT:
+ field = (field == 0 ? 2 : field - 1);
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ value = value * 10 + (this_c - '0');
+ break;
+
+ case '+':
+ change_color(current, field, value, 1);
+ break;
+
+ case '-':
+ change_color(current, field, -value, 1);
+ break;
+
+ case '=':
+ change_color(current, field, value, 0);
+ break;
+
+ case '?':
+ erase();
+ P(" RGB Value Editing Help");
+ P("");
+ P("You are in the RGB value editor. Use the arrow keys to select one of");
+ P("the fields in one of the RGB triples of the current colors; the one");
+ P("currently selected will be reverse-video highlighted.");
+ P("");
+ P("To change a field, enter the digits of the new value; they are echoed");
+ P("as entered. Finish by typing `='. The change will take effect instantly.");
+ P("To increment or decrement a value, use the same procedure, but finish");
+ P("with a `+' or `-'.");
+ P("");
+ P("To quit, do `x' or 'q'");
+
+ Pause();
+ erase();
+ break;
+
+ case 'x':
+ case 'q':
+ break;
+
+ default:
+ beep();
+ break;
+ }
+ mvprintw(LINES-2, 0, "Number: %d", value);
+ clrtoeol();
+ } while
+ (this_c != 'x' && this_c != 'q');
+
+ erase();
+ endwin();
+}
+
+/****************************************************************************
+ *
+ * Soft-key label test
+ *
+ ****************************************************************************/
+
+static void slk_test(void)
+/* exercise the soft keys */
+{
+ int c, fmt = 1;
+ char buf[9];
+
+ c = CTRL('l');
+ do {
+ move(0, 0);
+ switch(c)
+ {
+ case CTRL('l'):
+ erase();
+ attron(A_BOLD);
+ mvaddstr(0, 20, "Soft Key Exerciser");
+ attroff(A_BOLD);
+
+ move(2, 0);
+ P("Available commands are:");
+ P("");
+ P("^L -- refresh screen");
+ P("a -- activate or restore soft keys");
+ P("d -- disable soft keys");
+ P("c -- set centered format for labels");
+ P("l -- set left-justified format for labels");
+ P("r -- set right-justified format for labels");
+ P("[12345678] -- set label; labels are numbered 1 through 8");
+ P("e -- erase stdscr (should not erase labels)");
+ P("s -- test scrolling of shortened screen");
+ P("x, q -- return to main menu");
+ P("");
+ P("Note: if activating the soft keys causes your terminal to");
+ P("scroll up one line, your terminal auto-scrolls when anything");
+ P("is written to the last screen position. The ncurses code");
+ P("does not yet handle this gracefully.");
+ refresh();
+ /* fall through */
+
+ case 'a':
+ slk_restore();
+ break;
+
+ case 'e':
+ wclear(stdscr);
+ break;
+
+ case 's':
+ mvprintw(20, 0, "Press Q to stop the scrolling-test: ");
+ while ((c = Getchar()) != 'Q' && (c != ERR))
+ addch((chtype)c);
+ break;
+
+ case 'd':
+ slk_clear();
+ break;
+
+ case 'l':
+ fmt = 0;
+ break;
+
+ case 'c':
+ fmt = 1;
+ break;
+
+ case 'r':
+ fmt = 2;
+ break;
+
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ (void) mvaddstr(20, 0, "Please enter the label value: ");
+ echo();
+ wgetnstr(stdscr, buf, 8);
+ noecho();
+ slk_set((c - '0'), buf, fmt);
+ slk_refresh();
+ move(20, 0); clrtoeol();
+ break;
+
+ case 'x':
+ case 'q':
+ goto done;
+
+ default:
+ beep();
+ }
+ } while
+ ((c = Getchar()) != EOF);
+
+ done:
+ erase();
+ endwin();
+}
+
+/****************************************************************************
+ *
+ * Alternate character-set stuff
+ *
+ ****************************************************************************/
+
+/* ISO 6429: codes 0x80 to 0x9f may be control characters that cause the
+ * terminal to perform functions. The remaining codes can be graphic.
+ */
+static void show_upper_chars(int first)
+{
+ bool C1 = (first == 128);
+ int code;
+ int last = first + 31;
+ int reply;
+
+ erase();
+ attron(A_BOLD);
+ mvprintw(0, 20, "Display of %s Character Codes %d to %d",
+ C1 ? "C1" : "GR", first, last);
+ attroff(A_BOLD);
+ refresh();
+
+ for (code = first; code <= last; code++) {
+ int row = 4 + ((code - first) % 16);
+ int col = ((code - first) / 16) * COLS / 2;
+ char tmp[80];
+ sprintf(tmp, "%3d (0x%x)", code, code);
+ mvprintw(row, col, "%*s: ", COLS/4, tmp);
+ if (C1)
+ nodelay(stdscr, TRUE);
+ echochar(code);
+ if (C1) {
+ /* (yes, this _is_ crude) */
+ while ((reply = Getchar()) != ERR) {
+ addch(reply);
+ napms(10);
+ }
+ nodelay(stdscr, FALSE);
+ }
+ }
+}
+
+static int show_1_acs(int n, const char *name, chtype code)
+{
+ const int height = 16;
+ int row = 4 + (n % height);
+ int col = (n / height) * COLS / 2;
+ mvprintw(row, col, "%*s : ", COLS/4, name);
+ addch(code);
+ return n + 1;
+}
+
+static void show_acs_chars(void)
+/* display the ACS character set */
+{
+ int n;
+
+#define BOTH(name) #name, name
+
+ erase();
+ attron(A_BOLD);
+ mvaddstr(0, 20, "Display of the ACS Character Set");
+ attroff(A_BOLD);
+ refresh();
+
+ n = show_1_acs(0, BOTH(ACS_ULCORNER));
+ n = show_1_acs(n, BOTH(ACS_LLCORNER));
+ n = show_1_acs(n, BOTH(ACS_URCORNER));
+ n = show_1_acs(n, BOTH(ACS_LRCORNER));
+ n = show_1_acs(n, BOTH(ACS_RTEE));
+ n = show_1_acs(n, BOTH(ACS_LTEE));
+ n = show_1_acs(n, BOTH(ACS_BTEE));
+ n = show_1_acs(n, BOTH(ACS_TTEE));
+ n = show_1_acs(n, BOTH(ACS_HLINE));
+ n = show_1_acs(n, BOTH(ACS_VLINE));
+ n = show_1_acs(n, BOTH(ACS_PLUS));
+ n = show_1_acs(n, BOTH(ACS_S1));
+ n = show_1_acs(n, BOTH(ACS_S9));
+ n = show_1_acs(n, BOTH(ACS_DIAMOND));
+ n = show_1_acs(n, BOTH(ACS_CKBOARD));
+ n = show_1_acs(n, BOTH(ACS_DEGREE));
+ n = show_1_acs(n, BOTH(ACS_PLMINUS));
+ n = show_1_acs(n, BOTH(ACS_BULLET));
+ n = show_1_acs(n, BOTH(ACS_LARROW));
+ n = show_1_acs(n, BOTH(ACS_RARROW));
+ n = show_1_acs(n, BOTH(ACS_DARROW));
+ n = show_1_acs(n, BOTH(ACS_UARROW));
+ n = show_1_acs(n, BOTH(ACS_BOARD));
+ n = show_1_acs(n, BOTH(ACS_LANTERN));
+ n = show_1_acs(n, BOTH(ACS_BLOCK));
+ n = show_1_acs(n, BOTH(ACS_S3));
+ n = show_1_acs(n, BOTH(ACS_S7));
+ n = show_1_acs(n, BOTH(ACS_LEQUAL));
+ n = show_1_acs(n, BOTH(ACS_GEQUAL));
+ n = show_1_acs(n, BOTH(ACS_PI));
+ n = show_1_acs(n, BOTH(ACS_NEQUAL));
+ n = show_1_acs(n, BOTH(ACS_STERLING));
+}
+
+static void acs_display(void)
+{
+ int c = 'a';
+
+ do {
+ switch (c) {
+ case 'a':
+ show_acs_chars();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ show_upper_chars((c - '0') * 32 + 128);
+ break;
+ }
+ mvprintw(LINES-3,0, "Note: ANSI terminals may not display C1 characters.");
+ mvprintw(LINES-2,0, "Select: a=ACS, 0=C1, 1,2,3=GR characters, q=quit");
+ refresh();
+ } while ((c = Getchar()) != 'x' && c != 'q');
+
+ Pause();
+ erase();
+ endwin();
+}
+
+/*
+ * Graphic-rendition test (adapted from vttest)
+ */
+static void
+test_sgr_attributes(void)
+{
+ int pass;
+
+ for (pass = 0; pass < 2; pass++) {
+ int normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
+
+ /* Use non-default colors if possible to exercise bce a little */
+ if (has_colors()) {
+ init_pair(1, COLOR_WHITE, COLOR_BLUE);
+ normal |= COLOR_PAIR(1);
+ }
+ bkgdset(normal);
+ erase();
+ mvprintw( 1,20, "Graphic rendition test pattern:");
+
+ mvprintw( 4, 1, "vanilla");
+
+#define set_sgr(mask) bkgdset((normal^(mask)));
+ set_sgr(A_BOLD);
+ mvprintw( 4,40, "bold");
+
+ set_sgr(A_UNDERLINE);
+ mvprintw( 6, 6, "underline");
+
+ set_sgr(A_BOLD|A_UNDERLINE);
+ mvprintw( 6,45, "bold underline");
+
+ set_sgr(A_BLINK);
+ mvprintw( 8, 1, "blink");
+
+ set_sgr(A_BLINK|A_BOLD);
+ mvprintw( 8,40, "bold blink");
+
+ set_sgr(A_UNDERLINE|A_BLINK);
+ mvprintw(10, 6, "underline blink");
+
+ set_sgr(A_BOLD|A_UNDERLINE|A_BLINK);
+ mvprintw(10,45, "bold underline blink");
+
+ set_sgr(A_REVERSE);
+ mvprintw(12, 1, "negative");
+
+ set_sgr(A_BOLD|A_REVERSE);
+ mvprintw(12,40, "bold negative");
+
+ set_sgr(A_UNDERLINE|A_REVERSE);
+ mvprintw(14, 6, "underline negative");
+
+ set_sgr(A_BOLD|A_UNDERLINE|A_REVERSE);
+ mvprintw(14,45, "bold underline negative");
+
+ set_sgr(A_BLINK|A_REVERSE);
+ mvprintw(16, 1, "blink negative");
+
+ set_sgr(A_BOLD|A_BLINK|A_REVERSE);
+ mvprintw(16,40, "bold blink negative");
+
+ set_sgr(A_UNDERLINE|A_BLINK|A_REVERSE);
+ mvprintw(18, 6, "underline blink negative");
+
+ set_sgr(A_BOLD|A_UNDERLINE|A_BLINK|A_REVERSE);
+ mvprintw(18,45, "bold underline blink negative");
+
+ bkgdset(normal);
+ mvprintw(LINES-2,1, "%s background. ", pass == 0 ? "Dark" : "Light");
+ clrtoeol();
+ Pause();
+ }
+
+ bkgdset(A_NORMAL | BLANK);
+ erase();
+ endwin();
+}
+
+/****************************************************************************
+ *
+ * Windows and scrolling tester.
+ *
+ ****************************************************************************/
+
+#define BOTLINES 4 /* number of line stolen from screen bottom */
+
+typedef struct
+{
+ int y, x;
+}
+pair;
+
+#define FRAME struct frame
+FRAME
+{
+ FRAME *next, *last;
+ bool do_scroll;
+ bool do_keypad;
+ WINDOW *wind;
+};
+
+/* We need to know if these flags are actually set, so don't look in FRAME.
+ * These names are known to work with SVr4 curses as well as ncurses.
+ */
+static bool HaveKeypad(FRAME *curp)
+{
+ WINDOW *win = (curp ? curp->wind : stdscr);
+ return win->_use_keypad;
+}
+
+static bool HaveScroll(FRAME *curp)
+{
+ WINDOW *win = (curp ? curp->wind : stdscr);
+ return win->_scroll;
+}
+
+static void newwin_legend(FRAME *curp)
+{
+ static const struct {
+ const char *msg;
+ int code;
+ } legend[] = {
+ { "^C = create window", 0 },
+ { "^N = next window", 0 },
+ { "^P = previous window", 0 },
+ { "^F = scroll forward", 0 },
+ { "^B = scroll backward", 0 },
+ { "^K = keypad(%s)", 1 },
+ { "^S = scrollok(%s)", 2 },
+ { "^W = save window to file", 0 },
+ { "^R = restore window", 0 },
+#ifdef NCURSES_VERSION
+ { "^X = resize", 0 },
+#endif
+ { "^Q%s = exit", 3 }
+ };
+ size_t n;
+ int y, x;
+ bool do_keypad = HaveKeypad(curp);
+ bool do_scroll = HaveScroll(curp);
+ char buf[BUFSIZ];
+
+ move(LINES-4, 0);
+ for (n = 0; n < SIZEOF(legend); n++) {
+ switch (legend[n].code) {
+ default:
+ strcpy(buf, legend[n].msg);
+ break;
+ case 1:
+ sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
+ break;
+ case 2:
+ sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
+ break;
+ case 3:
+ sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
+ break;
+ }
+ getyx(stdscr, y, x);
+ addstr((COLS < (x + 3 + (int)strlen(buf))) ? "\n" : (n ? ", " : ""));
+ addstr(buf);
+ }
+ clrtoeol();
+}
+
+static void transient(FRAME *curp, NCURSES_CONST char *msg)
+{
+ newwin_legend(curp);
+ if (msg)
+ {
+ mvaddstr(LINES - 1, 0, msg);
+ refresh();
+ napms(1000);
+ }
+
+ move(LINES-1, 0);
+ printw("%s characters are echoed, window should %sscroll.",
+ HaveKeypad(curp) ? "Non-arrow" : "All other",
+ HaveScroll(curp) ? "" : "not " );
+ clrtoeol();
+}
+
+static void newwin_report(FRAME *curp)
+/* report on the cursor's current position, then restore it */
+{
+ WINDOW *win = (curp != 0) ? curp->wind : stdscr;
+ int y, x;
+
+ if (win != stdscr)
+ transient(curp, (char *)0);
+ getyx(win, y, x);
+ move(LINES - 1, COLS - 17);
+ printw("Y = %2d X = %2d", y, x);
+ if (win != stdscr)
+ refresh();
+ else
+ wmove(win, y, x);
+}
+
+static pair *selectcell(int uli, int ulj, int lri, int lrj)
+/* arrows keys move cursor, return location at current on non-arrow key */
+{
+ static pair res; /* result cell */
+ int si = lri - uli + 1; /* depth of the select area */
+ int sj = lrj - ulj + 1; /* width of the select area */
+ int i = 0, j = 0; /* offsets into the select area */
+
+ res.y = uli;
+ res.x = ulj;
+ for (;;)
+ {
+ move(uli + i, ulj + j);
+ newwin_report((FRAME *)0);
+
+ switch(Getchar())
+ {
+ case KEY_UP: i += si - 1; break;
+ case KEY_DOWN: i++; break;
+ case KEY_LEFT: j += sj - 1; break;
+ case KEY_RIGHT: j++; break;
+ case QUIT:
+ case ESCAPE: return((pair *)0);
+#ifdef NCURSES_MOUSE_VERSION
+ case KEY_MOUSE:
+ {
+ MEVENT event;
+
+ getmouse(&event);
+ if (event.y > uli && event.x > ulj) {
+ i = event.y - uli;
+ j = event.x - ulj;
+ } else {
+ beep();
+ break;
+ }
+ }
+ /* FALLTHRU */
+#endif
+ default: res.y = uli + i; res.x = ulj + j; return(&res);
+ }
+ i %= si;
+ j %= sj;
+ }
+}
+
+static void outerbox(pair ul, pair lr, bool onoff)
+/* draw or erase a box *outside* the given pair of corners */
+{
+ mvaddch(ul.y-1, lr.x-1, onoff ? ACS_ULCORNER : ' ');
+ mvaddch(ul.y-1, lr.x+1, onoff ? ACS_URCORNER : ' ');
+ mvaddch(lr.y+1, lr.x+1, onoff ? ACS_LRCORNER : ' ');
+ mvaddch(lr.y+1, ul.x-1, onoff ? ACS_LLCORNER : ' ');
+ move(ul.y-1, ul.x); hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
+ move(ul.y, ul.x-1); vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
+ move(lr.y+1, ul.x); hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
+ move(ul.y, lr.x+1); vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
+}
+
+static WINDOW *getwindow(void)
+/* Ask user for a window definition */
+{
+ WINDOW *rwindow;
+ pair ul, lr, *tmp;
+
+ move(0, 0); clrtoeol();
+ addstr("Use arrows to move cursor, anything else to mark corner 1");
+ refresh();
+ if ((tmp = selectcell(2, 1, LINES-BOTLINES-2, COLS-2)) == (pair *)0)
+ return((WINDOW *)0);
+ memcpy(&ul, tmp, sizeof(pair));
+ mvaddch(ul.y-1, ul.x-1, ACS_ULCORNER);
+ move(0, 0); clrtoeol();
+ addstr("Use arrows to move cursor, anything else to mark corner 2");
+ refresh();
+ if ((tmp = selectcell(ul.y, ul.x, LINES-BOTLINES-2, COLS-2)) == (pair *)0)
+ return((WINDOW *)0);
+ memcpy(&lr, tmp, sizeof(pair));
+
+ rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
+
+ outerbox(ul, lr, TRUE);
+ refresh();
+
+ wrefresh(rwindow);
+
+ move(0, 0); clrtoeol();
+ return(rwindow);
+}
+
+static void newwin_move(FRAME *curp, int dy, int dx)
+{
+ WINDOW *win = (curp != 0) ? curp->wind : stdscr;
+ int cur_y, cur_x;
+ int max_y, max_x;
+
+ getyx(win, cur_y, cur_x);
+ getmaxyx(win, max_y, max_x);
+ if ((cur_x += dx) < 0)
+ cur_x = 0;
+ else if (cur_x >= max_x)
+ cur_x = max_x - 1;
+ if ((cur_y += dy) < 0)
+ cur_y = 0;
+ else if (cur_y >= max_y)
+ cur_y = max_y - 1;
+ wmove(win, cur_y, cur_x);
+}
+
+static FRAME *delete_framed(FRAME *fp, bool showit)
+{
+ FRAME *np;
+
+ fp->last->next = fp->next;
+ fp->next->last = fp->last;
+
+ if (showit) {
+ werase(fp->wind);
+ wrefresh(fp->wind);
+ }
+ delwin(fp->wind);
+
+ np = (fp == fp->next) ? 0 : fp->next;
+ free(fp);
+ return np;
+}
+
+static void acs_and_scroll(void)
+/* Demonstrate windows */
+{
+ int c, i;
+ FILE *fp;
+ FRAME *current = (FRAME *)0, *neww;
+ WINDOW *usescr = stdscr;
+
+#define DUMPFILE "screendump"
+
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(BUTTON1_CLICKED, (mmask_t *)0);
+#endif
+ c = CTRL('C');
+ raw();
+ do {
+ transient((FRAME *)0, (char *)0);
+ switch(c)
+ {
+ case CTRL('C'):
+ neww = (FRAME *) calloc(1, sizeof(FRAME));
+ if ((neww->wind = getwindow()) == (WINDOW *)0)
+ goto breakout;
+
+ if (current == 0) /* First element, */
+ {
+ neww->next = neww; /* so point it at itself */
+ neww->last = neww;
+ }
+ else
+ {
+ neww->next = current->next;
+ neww->last = current;
+ neww->last->next = neww;
+ neww->next->last = neww;
+ }
+ current = neww;
+ /* SVr4 curses sets the keypad on all newly-created windows to
+ * false. Someone reported that PDCurses makes new windows inherit
+ * this flag. Remove the following 'keypad()' call to test this
+ */
+ keypad(current->wind, TRUE);
+ current->do_keypad = HaveKeypad(current);
+ current->do_scroll = HaveScroll(current);
+ break;
+
+ case CTRL('N'): /* go to next window */
+ if (current)
+ current = current->next;
+ break;
+
+ case CTRL('P'): /* go to previous window */
+ if (current)
+ current = current->last;
+ break;
+
+ case CTRL('F'): /* scroll current window forward */
+ if (current)
+ wscrl(current->wind, 1);
+ break;
+
+ case CTRL('B'): /* scroll current window backwards */
+ if (current)
+ wscrl(current->wind, -1);
+ break;
+
+ case CTRL('K'): /* toggle keypad mode for current */
+ if (current) {
+ current->do_keypad = !current->do_keypad;
+ keypad(current->wind, current->do_keypad);
+ }
+ break;
+
+ case CTRL('S'):
+ if (current) {
+ current->do_scroll = !current->do_scroll;
+ scrollok(current->wind, current->do_scroll);
+ }
+ break;
+
+ case CTRL('W'): /* save and delete window */
+ if (current == current->next)
+ break;
+ if ((fp = fopen(DUMPFILE, "w")) == (FILE *)0)
+ transient(current, "Can't open screen dump file");
+ else
+ {
+ (void) putwin(current->wind, fp);
+ (void) fclose(fp);
+
+ current = delete_framed(current, TRUE);
+ }
+ break;
+
+ case CTRL('R'): /* restore window */
+ if ((fp = fopen(DUMPFILE, "r")) == (FILE *)0)
+ transient(current, "Can't open screen dump file");
+ else
+ {
+ neww = (FRAME *) calloc(1, sizeof(FRAME));
+
+ neww->next = current->next;
+ neww->last = current;
+ neww->last->next = neww;
+ neww->next->last = neww;
+
+ neww->wind = getwin(fp);
+ (void) fclose(fp);
+
+ wrefresh(neww->wind);
+ }
+ break;
+
+#ifdef NCURSES_VERSION
+ case CTRL('X'): /* resize window */
+ if (current)
+ {
+ pair *tmp, ul, lr;
+ int mx, my;
+
+ move(0, 0); clrtoeol();
+ addstr("Use arrows to move cursor, anything else to mark new corner");
+ refresh();
+
+ getbegyx(current->wind, ul.y, ul.x);
+
+ tmp = selectcell(ul.y, ul.x, LINES-BOTLINES-2, COLS-2);
+ if (tmp == (pair *)0)
+ {
+ beep();
+ break;
+ }
+
+ getmaxyx(current->wind, lr.y, lr.x);
+ lr.y += (ul.y - 1);
+ lr.x += (ul.x - 1);
+ outerbox(ul, lr, FALSE);
+ wnoutrefresh(stdscr);
+
+ /* strictly cosmetic hack for the test */
+ getmaxyx(current->wind, my, mx);
+ if (my > tmp->y - ul.y)
+ {
+ getyx(current->wind, lr.y, lr.x);
+ wmove(current->wind, tmp->y - ul.y + 1, 0);
+ wclrtobot(current->wind);
+ wmove(current->wind, lr.y, lr.x);
+ }
+ if (mx > tmp->x - ul.x)
+ for (i = 0; i < my; i++)
+ {
+ wmove(current->wind, i, tmp->x - ul.x + 1);
+ wclrtoeol(current->wind);
+ }
+ wnoutrefresh(current->wind);
+
+ memcpy(&lr, tmp, sizeof(pair));
+ (void) wresize(current->wind, lr.y-ul.y+0, lr.x-ul.x+0);
+
+ getbegyx(current->wind, ul.y, ul.x);
+ getmaxyx(current->wind, lr.y, lr.x);
+ lr.y += (ul.y - 1);
+ lr.x += (ul.x - 1);
+ outerbox(ul, lr, TRUE);
+ wnoutrefresh(stdscr);
+
+ wnoutrefresh(current->wind);
+ move(0, 0); clrtoeol();
+ doupdate();
+ }
+ break;
+#endif /* NCURSES_VERSION */
+
+ case KEY_F(10): /* undocumented --- use this to test area clears */
+ selectcell(0, 0, LINES - 1, COLS - 1);
+ clrtobot();
+ refresh();
+ break;
+
+ case KEY_UP:
+ newwin_move(current, -1, 0);
+ break;
+ case KEY_DOWN:
+ newwin_move(current, 1, 0);
+ break;
+ case KEY_LEFT:
+ newwin_move(current, 0, -1);
+ break;
+ case KEY_RIGHT:
+ newwin_move(current, 0, 1);
+ break;
+
+ case KEY_BACKSPACE:
+ /* FALLTHROUGH */
+ case KEY_DC:
+ {
+ int y, x;
+ getyx(current->wind, y, x);
+ if (--x < 0) {
+ if (--y < 0)
+ break;
+ x = getmaxx(current->wind) - 1;
+ }
+ mvwdelch(current->wind, y, x);
+ }
+ break;
+
+ case '\r':
+ c = '\n';
+ /* FALLTHROUGH */
+
+ default:
+ if (current)
+ waddch(current->wind, (chtype)c);
+ else
+ beep();
+ break;
+ }
+ newwin_report(current);
+ usescr = (current ? current->wind : stdscr);
+ wrefresh(usescr);
+ } while
+ ((c = wGetchar(usescr)) != QUIT
+ && !((c == ESCAPE) && (usescr->_use_keypad))
+ && (c != ERR));
+
+ breakout:
+ while (current != 0)
+ current = delete_framed(current, FALSE);
+
+ scrollok(stdscr, TRUE); /* reset to driver's default */
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(0, (mmask_t *)0);
+#endif
+ noraw();
+ erase();
+ endwin();
+}
+
+/****************************************************************************
+ *
+ * Panels tester
+ *
+ ****************************************************************************/
+
+#if USE_LIBPANEL
+static unsigned long nap_msec = 1;
+
+static NCURSES_CONST char *mod[] =
+{
+ "test ",
+ "TEST ",
+ "(**) ",
+ "*()* ",
+ "<--> ",
+ "LAST "
+};
+
+/*+-------------------------------------------------------------------------
+ wait_a_while(msec)
+--------------------------------------------------------------------------*/
+static void
+wait_a_while(unsigned long msec GCC_UNUSED)
+{
+#if HAVE_NAPMS
+ if(nap_msec == 1)
+ wGetchar(stdscr);
+ else
+ napms(nap_msec);
+#else
+ if(nap_msec == 1)
+ wGetchar(stdscr);
+ else if(msec > 1000L)
+ sleep((int)msec/1000L);
+ else
+ sleep(1);
+#endif
+} /* end of wait_a_while */
+
+/*+-------------------------------------------------------------------------
+ saywhat(text)
+--------------------------------------------------------------------------*/
+static void
+saywhat(NCURSES_CONST char *text)
+{
+ wmove(stdscr,LINES - 1,0);
+ wclrtoeol(stdscr);
+ waddstr(stdscr, text);
+} /* end of saywhat */
+
+/*+-------------------------------------------------------------------------
+ mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
+--------------------------------------------------------------------------*/
+static PANEL *
+mkpanel(int color, int rows, int cols, int tly, int tlx)
+{
+WINDOW *win;
+PANEL *pan = 0;
+
+ if ((win = newwin(rows, cols, tly, tlx)) != 0) {
+ if ((pan = new_panel(win)) == 0) {
+ delwin(win);
+ } else if (has_colors()) {
+ int fg = (color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK;
+ int bg = color;
+ init_pair(color, fg, bg);
+ wbkgdset(win, COLOR_PAIR(color) | ' ');
+ } else {
+ wbkgdset(win, A_BOLD | ' ');
+ }
+ }
+ return pan;
+} /* end of mkpanel */
+
+/*+-------------------------------------------------------------------------
+ rmpanel(pan)
+--------------------------------------------------------------------------*/
+static void
+rmpanel(PANEL *pan)
+{
+WINDOW *win = panel_window(pan);
+ del_panel(pan);
+ delwin(win);
+} /* end of rmpanel */
+
+/*+-------------------------------------------------------------------------
+ pflush()
+--------------------------------------------------------------------------*/
+static void
+pflush(void)
+{
+ update_panels();
+ doupdate();
+} /* end of pflush */
+
+/*+-------------------------------------------------------------------------
+ fill_panel(win)
+--------------------------------------------------------------------------*/
+static void
+fill_panel(PANEL *pan)
+{
+WINDOW *win = panel_window(pan);
+int num = ((const char *)panel_userptr(pan))[1];
+int y,x;
+
+ wmove(win,1,1);
+ wprintw(win,"-pan%c-", num);
+ wclrtoeol(win);
+ box(win, 0, 0);
+ for(y = 2; y < getmaxy(win) - 1; y++)
+ {
+ for(x = 1; x < getmaxx(win) - 1; x++)
+ {
+ wmove(win,y,x);
+ waddch(win,num);
+ }
+ }
+} /* end of fill_panel */
+
+static void demo_panels(void)
+{
+int itmp;
+register int y,x;
+
+ refresh();
+
+ for(y = 0; y < LINES - 1; y++)
+ {
+ for(x = 0; x < COLS; x++)
+ wprintw(stdscr,"%d",(y + x) % 10);
+ }
+ for(y = 0; y < 5; y++)
+ {
+ PANEL *p1;
+ PANEL *p2;
+ PANEL *p3;
+ PANEL *p4;
+ PANEL *p5;
+
+ p1 = mkpanel(COLOR_RED, LINES/2 - 2, COLS/8 + 1, 0, 0);
+ set_panel_userptr(p1,"p1");
+
+ p2 = mkpanel(COLOR_GREEN, LINES/2 + 1, COLS/7, LINES/4, COLS/10);
+ set_panel_userptr(p2,"p2");
+
+ p3 = mkpanel(COLOR_YELLOW, LINES/4, COLS/10, LINES/2, COLS/9);
+ set_panel_userptr(p3,"p3");
+
+ p4 = mkpanel(COLOR_BLUE, LINES/2 - 2, COLS/8, LINES/2 - 2, COLS/3);
+ set_panel_userptr(p4,"p4");
+
+ p5 = mkpanel(COLOR_MAGENTA, LINES/2 - 2, COLS/8, LINES/2, COLS/2 - 2);
+ set_panel_userptr(p5,"p5");
+
+ fill_panel(p1);
+ fill_panel(p2);
+ fill_panel(p3);
+ fill_panel(p4);
+ fill_panel(p5);
+ hide_panel(p4);
+ hide_panel(p5);
+ pflush();
+ saywhat("press any key to continue");
+ wait_a_while(nap_msec);
+
+ saywhat("h3 s1 s2 s4 s5; press any key to continue");
+ move_panel(p1,0,0);
+ hide_panel(p3);
+ show_panel(p1);
+ show_panel(p2);
+ show_panel(p4);
+ show_panel(p5);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("s1; press any key to continue");
+ show_panel(p1);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("s2; press any key to continue");
+ show_panel(p2);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("m2; press any key to continue");
+ move_panel(p2, LINES/3 + 1, COLS / 8);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("s3;");
+ show_panel(p3);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("m3; press any key to continue");
+ move_panel(p3, LINES/4 + 1, COLS / 15);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("b3; press any key to continue");
+ bottom_panel(p3);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("s4; press any key to continue");
+ show_panel(p4);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("s5; press any key to continue");
+ show_panel(p5);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t3; press any key to continue");
+ top_panel(p3);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t1; press any key to continue");
+ top_panel(p1);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t2; press any key to continue");
+ top_panel(p2);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t3; press any key to continue");
+ top_panel(p3);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t4; press any key to continue");
+ top_panel(p4);
+ pflush();
+ wait_a_while(nap_msec);
+
+ for(itmp = 0; itmp < 6; itmp++)
+ {
+ WINDOW *w4 = panel_window(p4);
+ WINDOW *w5 = panel_window(p5);
+
+ saywhat("m4; press any key to continue");
+ wmove(w4, LINES/8, 1);
+ waddstr(w4,mod[itmp]);
+ move_panel(p4, LINES/6, itmp*(COLS/8));
+ wmove(w5, LINES/6, 1);
+ waddstr(w5,mod[itmp]);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("m5; press any key to continue");
+ wmove(w4, LINES/6, 1);
+ waddstr(w4,mod[itmp]);
+ move_panel(p5, LINES/3 - 1,(itmp*10) + 6);
+ wmove(w5, LINES/8, 1);
+ waddstr(w5,mod[itmp]);
+ pflush();
+ wait_a_while(nap_msec);
+ }
+
+ saywhat("m4; press any key to continue");
+ move_panel(p4, LINES/6, itmp*(COLS/8));
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t5; press any key to continue");
+ top_panel(p5);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t2; press any key to continue");
+ top_panel(p2);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("t1; press any key to continue");
+ top_panel(p1);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("d2; press any key to continue");
+ rmpanel(p2);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("h3; press any key to continue");
+ hide_panel(p3);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("d1; press any key to continue");
+ rmpanel(p1);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("d4; press any key to continue");
+ rmpanel(p4);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("d5; press any key to continue");
+ rmpanel(p5);
+ pflush();
+ wait_a_while(nap_msec);
+ if(nap_msec == 1)
+ break;
+ nap_msec = 100L;
+ }
+
+ erase();
+ endwin();
+}
+
+/****************************************************************************
+ *
+ * Pad tester
+ *
+ ****************************************************************************/
+
+#define GRIDSIZE 3
+
+static bool show_panner_legend = TRUE;
+
+static int panner_legend(int line)
+{
+ static const char *const legend[] = {
+ "Use arrow keys (or U,D,L,R) to pan, q to quit (?,t,s flags)",
+ "Use ! to shell-out. Toggle legend:?, timer:t, scroll mark:s.",
+ "Use +,- (or j,k) to grow/shrink the panner vertically.",
+ "Use <,> (or h,l) to grow/shrink the panner horizontally."
+ };
+ int n = (SIZEOF(legend) - (LINES - line));
+ if (line < LINES && (n >= 0)) {
+ move(line, 0);
+ if (show_panner_legend)
+ printw("%s", legend[n]);
+ clrtoeol();
+ return show_panner_legend;
+ }
+ return FALSE;
+}
+
+static void panner_h_cleanup(int from_y, int from_x, int to_x)
+{
+ if (!panner_legend(from_y))
+ do_h_line(from_y, from_x, ' ', to_x);
+}
+
+static void panner_v_cleanup(int from_y, int from_x, int to_y)
+{
+ if (!panner_legend(from_y))
+ do_v_line(from_y, from_x, ' ', to_y);
+}
+
+static void panner(WINDOW *pad,
+ int top_x, int top_y, int porty, int portx,
+ int (*pgetc)(WINDOW *))
+{
+#if HAVE_GETTIMEOFDAY
+ struct timeval before, after;
+ bool timing = TRUE;
+#endif
+ bool scrollers = TRUE;
+ int basex = 0;
+ int basey = 0;
+ int pxmax, pymax, lowend, highend, c;
+
+ getmaxyx(pad, pymax, pxmax);
+ scrollok(stdscr, FALSE); /* we don't want stdscr to scroll! */
+
+ c = KEY_REFRESH;
+ do {
+#ifdef NCURSES_VERSION
+ /*
+ * During shell-out, the user may have resized the window. Adjust
+ * the port size of the pad to accommodate this. Ncurses automatically
+ * resizes all of the normal windows to fit on the new screen.
+ */
+ if (top_x > COLS) top_x = COLS;
+ if (portx > COLS) portx = COLS;
+ if (top_y > LINES) top_y = LINES;
+ if (porty > LINES) porty = LINES;
+#endif
+ switch(c)
+ {
+ case KEY_REFRESH:
+ erase();
+
+ /* FALLTHRU */
+ case '?':
+ if (c == '?')
+ show_panner_legend = !show_panner_legend;
+ panner_legend(LINES - 4);
+ panner_legend(LINES - 3);
+ panner_legend(LINES - 2);
+ panner_legend(LINES - 1);
+ break;
+#if HAVE_GETTIMEOFDAY
+ case 't':
+ timing = !timing;
+ if (!timing)
+ panner_legend(LINES-1);
+ break;
+#endif
+ case 's':
+ scrollers = !scrollers;
+ break;
+
+ /* Move the top-left corner of the pad, keeping the bottom-right
+ * corner fixed.
+ */
+ case 'h': /* increase-columns: move left edge to left */
+ if (top_x <= 0)
+ beep();
+ else
+ {
+ panner_v_cleanup(top_y, top_x, porty);
+ top_x--;
+ }
+ break;
+
+ case 'j': /* decrease-lines: move top-edge down */
+ if (top_y >= porty)
+ beep();
+ else
+ {
+ panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
+ top_y++;
+ }
+ break;
+
+ case 'k': /* increase-lines: move top-edge up */
+ if (top_y <= 0)
+ beep();
+ else
+ {
+ top_y--;
+ panner_h_cleanup(top_y, top_x, portx);
+ }
+ break;
+
+ case 'l': /* decrease-columns: move left-edge to right */
+ if (top_x >= portx)
+ beep();
+ else
+ {
+ panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
+ top_x++;
+ }
+ break;
+
+ /* Move the bottom-right corner of the pad, keeping the top-left
+ * corner fixed.
+ */
+ case KEY_IC: /* increase-columns: move right-edge to right */
+ if (portx >= pxmax || portx >= COLS)
+ beep();
+ else
+ {
+ panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
+ ++portx;
+ }
+ break;
+
+ case KEY_IL: /* increase-lines: move bottom-edge down */
+ if (porty >= pymax || porty >= LINES)
+ beep();
+ else
+ {
+ panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
+ ++porty;
+ }
+ break;
+
+ case KEY_DC: /* decrease-columns: move bottom edge up */
+ if (portx <= top_x)
+ beep();
+ else
+ {
+ portx--;
+ panner_v_cleanup(top_y - (top_y > 0), portx, porty);
+ }
+ break;
+
+ case KEY_DL: /* decrease-lines */
+ if (porty <= top_y)
+ beep();
+ else
+ {
+ porty--;
+ panner_h_cleanup(porty, top_x - (top_x > 0), portx);
+ }
+ break;
+
+ case KEY_LEFT: /* pan leftwards */
+ if (basex > 0)
+ basex--;
+ else
+ beep();
+ break;
+
+ case KEY_RIGHT: /* pan rightwards */
+ if (basex + portx - (pymax > porty) < pxmax)
+ basex++;
+ else
+ beep();
+ break;
+
+ case KEY_UP: /* pan upwards */
+ if (basey > 0)
+ basey--;
+ else
+ beep();
+ break;
+
+ case KEY_DOWN: /* pan downwards */
+ if (basey + porty - (pxmax > portx) < pymax)
+ basey++;
+ else
+ beep();
+ break;
+
+ default:
+ beep();
+ break;
+ }
+
+ mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
+ do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
+ do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
+
+ if (scrollers && (pxmax > portx - 1)) {
+ int length = (portx - top_x - 1);
+ float ratio = ((float) length) / ((float) pxmax);
+
+ lowend = (int)(top_x + (basex * ratio));
+ highend = (int)(top_x + ((basex + length) * ratio));
+
+ do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
+ if (highend < portx) {
+ attron(A_REVERSE);
+ do_h_line(porty - 1, lowend, ' ', highend + 1);
+ attroff(A_REVERSE);
+ do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
+ }
+ } else
+ do_h_line(porty - 1, top_x, ACS_HLINE, portx);
+
+ if (scrollers && (pymax > porty - 1)) {
+ int length = (porty - top_y - 1);
+ float ratio = ((float) length) / ((float) pymax);
+
+ lowend = (int)(top_y + (basey * ratio));
+ highend = (int)(top_y + ((basey + length) * ratio));
+
+ do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
+ if (highend < porty) {
+ attron(A_REVERSE);
+ do_v_line(lowend, portx - 1, ' ', highend + 1);
+ attroff(A_REVERSE);
+ do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
+ }
+ } else
+ do_v_line(top_y, portx - 1, ACS_VLINE, porty);
+
+ mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
+ mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
+ mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
+
+#if HAVE_GETTIMEOFDAY
+ gettimeofday(&before, 0);
+#endif
+ wnoutrefresh(stdscr);
+
+ pnoutrefresh(pad,
+ basey, basex,
+ top_y, top_x,
+ porty - (pxmax > portx) - 1,
+ portx - (pymax > porty) - 1);
+
+ doupdate();
+#if HAVE_GETTIMEOFDAY
+ if (timing) {
+ double elapsed;
+ gettimeofday(&after, 0);
+ elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
+ - (before.tv_sec + before.tv_usec / 1.0e6);
+ move(LINES-1, COLS-20);
+ printw("Secs: %2.03f", elapsed);
+ refresh();
+ }
+#endif
+
+ } while
+ ((c = pgetc(pad)) != KEY_EXIT);
+
+ scrollok(stdscr, TRUE); /* reset to driver's default */
+}
+
+static
+int padgetch(WINDOW *win)
+{
+ int c;
+
+ switch(c = wGetchar(win))
+ {
+ case '!': ShellOut(FALSE); return KEY_REFRESH;
+ case CTRL('r'): endwin(); refresh(); return KEY_REFRESH;
+ case CTRL('l'): return KEY_REFRESH;
+ case 'U': return(KEY_UP);
+ case 'D': return(KEY_DOWN);
+ case 'R': return(KEY_RIGHT);
+ case 'L': return(KEY_LEFT);
+ case '+': return(KEY_IL);
+ case '-': return(KEY_DL);
+ case '>': return(KEY_IC);
+ case '<': return(KEY_DC);
+ case ERR: /* FALLTHRU */
+ case 'q': return(KEY_EXIT);
+ default: return(c);
+ }
+}
+
+static void demo_pad(void)
+/* Demonstrate pads. */
+{
+ int i, j;
+ unsigned gridcount = 0;
+ WINDOW *panpad = newpad(200, 200);
+
+ for (i = 0; i < 200; i++)
+ {
+ for (j = 0; j < 200; j++)
+ if (i % GRIDSIZE == 0 && j % GRIDSIZE == 0)
+ {
+ if (i == 0 || j == 0)
+ waddch(panpad, '+');
+ else
+ waddch(panpad, (chtype)('A' + (gridcount++ % 26)));
+ }
+ else if (i % GRIDSIZE == 0)
+ waddch(panpad, '-');
+ else if (j % GRIDSIZE == 0)
+ waddch(panpad, '|');
+ else
+ waddch(panpad, ' ');
+ }
+ panner_legend(LINES - 4);
+ panner_legend(LINES - 3);
+ panner_legend(LINES - 2);
+ panner_legend(LINES - 1);
+
+ keypad(panpad, TRUE);
+
+ /* Make the pad (initially) narrow enough that a trace file won't wrap.
+ * We'll still be able to widen it during a test, since that's required
+ * for testing boundaries.
+ */
+ panner(panpad, 2, 2, LINES - 5, COLS-15, padgetch);
+
+ delwin(panpad);
+ endwin();
+ erase();
+}
+#endif /* USE_LIBPANEL */
+
+/****************************************************************************
+ *
+ * Tests from John Burnell's PDCurses tester
+ *
+ ****************************************************************************/
+
+static void Continue (WINDOW *win)
+{
+ noecho();
+ wmove(win, 10, 1);
+ mvwaddstr(win, 10, 1, " Press any key to continue");
+ wrefresh(win);
+ wGetchar(win);
+}
+
+static void flushinp_test(WINDOW *win)
+/* Input test, adapted from John Burnell's PDCurses tester */
+{
+ int w, h, bx, by, sw, sh, i;
+
+ WINDOW *subWin;
+ wclear (win);
+
+ getmaxyx(win, h, w);
+ getbegyx(win, by, bx);
+ sw = w / 3;
+ sh = h / 3;
+ if((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
+ return;
+
+#ifdef A_COLOR
+ if (has_colors())
+ {
+ init_pair(2,COLOR_CYAN,COLOR_BLUE);
+ wattrset(subWin, COLOR_PAIR(2) | A_BOLD);
+ }
+ else
+ wattrset(subWin, A_BOLD);
+#else
+ wattrset(subWin, A_BOLD);
+#endif
+ box(subWin, ACS_VLINE, ACS_HLINE);
+ mvwaddstr(subWin, 2, 1, "This is a subwindow");
+ wrefresh(win);
+
+ /*
+ * This used to set 'nocbreak()'. However, Alexander Lukyanov says that
+ * it only happened to "work" on SVr4 because that implementation does not
+ * emulate nocbreak+noecho mode, whereas ncurses does. To get the desired
+ * test behavior, we're using 'cbreak()', which will allow a single
+ * character to return without needing a newline. - T.Dickey 1997/10/11.
+ */
+ cbreak();
+ mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
+
+ mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
+ mvwaddstr(win, 3, 1,
+ "These should be discarded (not echoed) after the subwindow goes away.");
+ wrefresh(win);
+
+ for (i = 0; i < 5; i++)
+ {
+ mvwprintw (subWin, 1, 1, "Time = %d", i);
+ wrefresh(subWin);
+ napms(1000);
+ flushinp();
+ }
+
+ delwin (subWin);
+ werase(win);
+ flash();
+ wrefresh(win);
+ napms(1000);
+
+ mvwaddstr(win, 2, 1,
+ "If you were still typing when the window timer expired,");
+ mvwaddstr(win, 3, 1,
+ "or else you typed nothing at all while it was running,");
+ mvwaddstr(win, 4, 1,
+ "test was invalid. You'll see garbage or nothing at all. ");
+ mvwaddstr(win, 6, 1, "Press a key");
+ wmove(win, 9, 10);
+ wrefresh(win);
+ echo();
+ wGetchar(win);
+ flushinp();
+ mvwaddstr(win, 12, 0,
+ "If you see any key other than what you typed, flushinp() is broken.");
+ Continue(win);
+
+ wmove(win, 9, 10);
+ wdelch(win);
+ wrefresh(win);
+ wmove(win, 12, 0);
+ clrtoeol();
+ waddstr(win,
+ "What you typed should now have been deleted; if not, wdelch() failed.");
+ Continue(win);
+
+ cbreak();
+}
+
+/****************************************************************************
+ *
+ * Menu test
+ *
+ ****************************************************************************/
+
+#if USE_LIBMENU
+
+#define MENU_Y 8
+#define MENU_X 8
+
+static int menu_virtualize(int c)
+{
+ if (c == '\n' || c == KEY_EXIT)
+ return(MAX_COMMAND + 1);
+ else if (c == 'u')
+ return(REQ_SCR_ULINE);
+ else if (c == 'd')
+ return(REQ_SCR_DLINE);
+ else if (c == 'b' || c == KEY_NPAGE)
+ return(REQ_SCR_UPAGE);
+ else if (c == 'f' || c == KEY_PPAGE)
+ return(REQ_SCR_DPAGE);
+ else if (c == 'n' || c == KEY_DOWN)
+ return(REQ_NEXT_ITEM);
+ else if (c == 'p' || c == KEY_UP)
+ return(REQ_PREV_ITEM);
+ else if (c == ' ')
+ return(REQ_TOGGLE_ITEM);
+ else {
+ if (c != KEY_MOUSE)
+ beep();
+ return(c);
+ }
+}
+
+static const char *animals[] =
+{
+ "Lions", "Tigers", "Bears", "(Oh my!)", "Newts", "Platypi", "Lemurs",
+ (char *)0
+};
+
+static void menu_test(void)
+{
+ MENU *m;
+ ITEM *items[SIZEOF(animals)];
+ ITEM **ip = items;
+ const char **ap;
+ int mrows, mcols, c;
+ WINDOW *menuwin;
+
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(ALL_MOUSE_EVENTS, (mmask_t *)0);
+#endif
+ mvaddstr(0, 0, "This is the menu test:");
+ mvaddstr(2, 0, " Use up and down arrow to move the select bar.");
+ mvaddstr(3, 0, " 'n' and 'p' act like arrows.");
+ mvaddstr(4, 0, " 'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
+ mvaddstr(5, 0, " Press return to exit.");
+ refresh();
+
+ for (ap = animals; *ap; ap++)
+ *ip++ = new_item(*ap, "");
+ *ip = (ITEM *)0;
+
+ m = new_menu(items);
+
+ set_menu_format(m, (SIZEOF(animals)+1)/2, 1);
+ scale_menu(m, &mrows, &mcols);
+
+ menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
+ set_menu_win(m, menuwin);
+ keypad(menuwin, TRUE);
+ box(menuwin, 0, 0);
+
+ set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
+
+ post_menu(m);
+
+ while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
+ if (c == E_REQUEST_DENIED)
+ beep();
+ continue;
+ }
+
+ (void) mvprintw(LINES - 2, 0,
+ "You chose: %s\n", item_name(current_item(m)));
+ (void) addstr("Press any key to continue...");
+ wGetchar(stdscr);
+
+ unpost_menu(m);
+ delwin(menuwin);
+
+ free_menu(m);
+ for (ip = items; *ip; ip++)
+ free_item(*ip);
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(0, (mmask_t *)0);
+#endif
+}
+
+#ifdef TRACE
+#define T_TBL(name) { #name, name }
+static struct {
+ const char *name;
+ int mask;
+} t_tbl[] = {
+ T_TBL(TRACE_DISABLE),
+ T_TBL(TRACE_TIMES),
+ T_TBL(TRACE_TPUTS),
+ T_TBL(TRACE_UPDATE),
+ T_TBL(TRACE_MOVE),
+ T_TBL(TRACE_CHARPUT),
+ T_TBL(TRACE_ORDINARY),
+ T_TBL(TRACE_CALLS),
+ T_TBL(TRACE_VIRTPUT),
+ T_TBL(TRACE_IEVENT),
+ T_TBL(TRACE_BITS),
+ T_TBL(TRACE_ICALLS),
+ T_TBL(TRACE_CCALLS),
+ T_TBL(TRACE_MAXIMUM),
+ { (char *)0, 0 }
+};
+
+static char *tracetrace(int tlevel)
+{
+ static char *buf;
+ int n;
+
+ if (buf == 0) {
+ size_t need = 12;
+ for (n = 0; t_tbl[n].name != 0; n++)
+ need += strlen(t_tbl[n].name) + 2;
+ buf = (char *)malloc(need);
+ }
+ sprintf(buf, "0x%02x = {", tlevel);
+ if (tlevel == 0) {
+ sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
+ } else {
+ for (n = 1; t_tbl[n].name != 0; n++)
+ if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask)
+ {
+ strcat(buf, t_tbl[n].name);
+ strcat(buf, ", ");
+ }
+ }
+ if (buf[strlen(buf) - 2] == ',')
+ buf[strlen(buf) - 2] = '\0';
+ return(strcat(buf,"}"));
+}
+
+/* fake a dynamically reconfigurable menu using the 0th entry to deselect
+ * the others
+ */
+static int run_trace_menu(MENU *m)
+{
+ ITEM **items;
+ ITEM *i, **p;
+
+ for (;;) {
+ bool changed = FALSE;
+ switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
+ case E_UNKNOWN_COMMAND:
+ return FALSE;
+ default:
+ items = menu_items(m);
+ i = current_item(m);
+ if (i == items[0]) {
+ if (item_value(i)) {
+ for (p = items+1; *p != 0; p++)
+ if (item_value(*p)) {
+ set_item_value(*p, FALSE);
+ changed = TRUE;
+ }
+ }
+ } else {
+ for (p = items+1; *p != 0; p++)
+ if (item_value(*p)) {
+ set_item_value(items[0], FALSE);
+ changed = TRUE;
+ break;
+ }
+ }
+ if (!changed)
+ return TRUE;
+ }
+ }
+}
+
+static void trace_set(void)
+/* interactively set the trace level */
+{
+ MENU *m;
+ ITEM *items[SIZEOF(t_tbl)];
+ ITEM **ip = items;
+ int mrows, mcols, newtrace;
+ int n;
+ WINDOW *menuwin;
+
+ mvaddstr(0, 0, "Interactively set trace level:");
+ mvaddstr(2, 0, " Press space bar to toggle a selection.");
+ mvaddstr(3, 0, " Use up and down arrow to move the select bar.");
+ mvaddstr(4, 0, " Press return to set the trace level.");
+ mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
+
+ refresh();
+
+ for (n = 0; t_tbl[n].name != 0; n++)
+ *ip++ = new_item(t_tbl[n].name, "");
+ *ip = (ITEM *)0;
+
+ m = new_menu(items);
+
+ set_menu_format(m, 0, 2);
+ scale_menu(m, &mrows, &mcols);
+
+ menu_opts_off(m, O_ONEVALUE);
+ menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
+ set_menu_win(m, menuwin);
+ keypad(menuwin, TRUE);
+ box(menuwin, 0, 0);
+
+ set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
+
+ post_menu(m);
+
+ for (ip = menu_items(m); *ip; ip++) {
+ int mask = t_tbl[item_index(*ip)].mask;
+ if (mask == 0)
+ set_item_value(*ip, _nc_tracing == 0);
+ else if ((mask & _nc_tracing) == mask)
+ set_item_value(*ip, TRUE);
+ }
+
+ while (run_trace_menu(m))
+ continue;
+
+ newtrace = 0;
+ for (ip = menu_items(m); *ip; ip++)
+ if (item_value(*ip))
+ newtrace |= t_tbl[item_index(*ip)].mask;
+ trace(newtrace);
+ _tracef("trace level interactively set to %s", tracetrace(_nc_tracing));
+
+ (void) mvprintw(LINES - 2, 0,
+ "Trace level is %s\n", tracetrace(_nc_tracing));
+ (void) addstr("Press any key to continue...");
+ wGetchar(stdscr);
+
+ unpost_menu(m);
+ delwin(menuwin);
+
+ free_menu(m);
+ for (ip = items; *ip; ip++)
+ free_item(*ip);
+}
+#endif /* TRACE */
+#endif /* USE_LIBMENU */
+
+/****************************************************************************
+ *
+ * Forms test
+ *
+ ****************************************************************************/
+#if USE_LIBFORM
+static FIELD *make_label(int frow, int fcol, NCURSES_CONST char *label)
+{
+ FIELD *f = new_field(1, strlen(label), frow, fcol, 0, 0);
+
+ if (f)
+ {
+ set_field_buffer(f, 0, label);
+ set_field_opts(f, field_opts(f) & ~O_ACTIVE);
+ }
+ return(f);
+}
+
+static FIELD *make_field(int frow, int fcol, int rows, int cols, bool secure)
+{
+ FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
+
+ if (f) {
+ set_field_back(f, A_UNDERLINE);
+ set_field_userptr(f, (void *)0);
+ }
+ return(f);
+}
+
+static void display_form(FORM *f)
+{
+ WINDOW *w;
+ int rows, cols;
+
+ scale_form(f, &rows, &cols);
+
+ if ((w =newwin(rows+2, cols+4, 0, 0)) != (WINDOW *)0)
+ {
+ set_form_win(f, w);
+ set_form_sub(f, derwin(w, rows, cols, 1, 2));
+ box(w, 0, 0);
+ keypad(w, TRUE);
+ }
+
+ if (post_form(f) != E_OK)
+ wrefresh(w);
+}
+
+static void erase_form(FORM *f)
+{
+ WINDOW *w = form_win(f);
+ WINDOW *s = form_sub(f);
+
+ unpost_form(f);
+ werase(w);
+ wrefresh(w);
+ delwin(s);
+ delwin(w);
+}
+
+static int edit_secure(FIELD *me, int c)
+{
+ int rows, cols, frow, fcol, nbuf;
+
+ if (field_info(me, &rows, &cols, &frow, &fcol, (int *)0, &nbuf) == E_OK
+ && nbuf > 0) {
+ char temp[80];
+ long len;
+
+ strcpy(temp, field_buffer(me, 1));
+ len = (long)(char *) field_userptr(me);
+ if (c <= KEY_MAX) {
+ if (isgraph(c)) {
+ temp[len++] = c;
+ temp[len] = 0;
+ set_field_buffer(me, 1, temp);
+ c = '*';
+ } else {
+ c = 0;
+ }
+ } else {
+ switch (c) {
+ case REQ_BEG_FIELD:
+ case REQ_CLR_EOF:
+ case REQ_CLR_EOL:
+ case REQ_DEL_LINE:
+ case REQ_DEL_WORD:
+ case REQ_DOWN_CHAR:
+ case REQ_END_FIELD:
+ case REQ_INS_CHAR:
+ case REQ_INS_LINE:
+ case REQ_LEFT_CHAR:
+ case REQ_NEW_LINE:
+ case REQ_NEXT_WORD:
+ case REQ_PREV_WORD:
+ case REQ_RIGHT_CHAR:
+ case REQ_UP_CHAR:
+ c = 0; /* we don't want to do inline editing */
+ break;
+ case REQ_CLR_FIELD:
+ if (len) {
+ temp[0] = 0;
+ set_field_buffer(me, 1, temp);
+ }
+ break;
+ case REQ_DEL_CHAR:
+ case REQ_DEL_PREV:
+ if (len) {
+ temp[--len] = 0;
+ set_field_buffer(me, 1, temp);
+ }
+ break;
+ }
+ }
+ set_field_userptr(me, (void *)len);
+ }
+ return c;
+}
+
+static int form_virtualize(FORM *f, WINDOW *w)
+{
+ static const struct {
+ int code;
+ int result;
+ } lookup[] = {
+ { CTRL('A'), REQ_NEXT_CHOICE },
+ { CTRL('B'), REQ_PREV_WORD },
+ { CTRL('C'), REQ_CLR_EOL },
+ { CTRL('D'), REQ_DOWN_FIELD },
+ { CTRL('E'), REQ_END_FIELD },
+ { CTRL('F'), REQ_NEXT_PAGE },
+ { CTRL('G'), REQ_DEL_WORD },
+ { CTRL('H'), REQ_DEL_PREV },
+ { CTRL('I'), REQ_INS_CHAR },
+ { CTRL('K'), REQ_CLR_EOF },
+ { CTRL('L'), REQ_LEFT_FIELD },
+ { CTRL('M'), REQ_NEW_LINE },
+ { CTRL('N'), REQ_NEXT_FIELD },
+ { CTRL('O'), REQ_INS_LINE },
+ { CTRL('P'), REQ_PREV_FIELD },
+ { CTRL('R'), REQ_RIGHT_FIELD },
+ { CTRL('S'), REQ_BEG_FIELD },
+ { CTRL('U'), REQ_UP_FIELD },
+ { CTRL('V'), REQ_DEL_CHAR },
+ { CTRL('W'), REQ_NEXT_WORD },
+ { CTRL('X'), REQ_CLR_FIELD },
+ { CTRL('Y'), REQ_DEL_LINE },
+ { CTRL('Z'), REQ_PREV_CHOICE },
+ { ESCAPE, MAX_FORM_COMMAND + 1 },
+ { KEY_BACKSPACE, REQ_DEL_PREV },
+ { KEY_DOWN, REQ_DOWN_CHAR },
+ { KEY_END, REQ_LAST_FIELD },
+ { KEY_HOME, REQ_FIRST_FIELD },
+ { KEY_LEFT, REQ_LEFT_CHAR },
+ { KEY_LL, REQ_LAST_FIELD },
+ { KEY_NEXT, REQ_NEXT_FIELD },
+ { KEY_NPAGE, REQ_NEXT_PAGE },
+ { KEY_PPAGE, REQ_PREV_PAGE },
+ { KEY_PREVIOUS, REQ_PREV_FIELD },
+ { KEY_RIGHT, REQ_RIGHT_CHAR },
+ { KEY_UP, REQ_UP_CHAR },
+ { QUIT, MAX_FORM_COMMAND + 1 }
+ };
+
+ static int mode = REQ_INS_MODE;
+ int c = wGetchar(w);
+ unsigned n;
+ FIELD *me = current_field(f);
+
+ if (c == CTRL(']')) {
+ if (mode == REQ_INS_MODE)
+ mode = REQ_OVL_MODE;
+ else
+ mode = REQ_INS_MODE;
+ c = mode;
+ } else {
+ for (n = 0; n < sizeof(lookup)/sizeof(lookup[0]); n++) {
+ if (lookup[n].code == c) {
+ c = lookup[n].result;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Force the field that the user is typing into to be in reverse video,
+ * while the other fields are shown underlined.
+ */
+ if (c <= KEY_MAX) {
+ c = edit_secure(me, c);
+ set_field_back(me, A_REVERSE);
+ } else if (c <= MAX_FORM_COMMAND) {
+ c = edit_secure(me, c);
+ set_field_back(me, A_UNDERLINE);
+ }
+ return c;
+}
+
+static int my_form_driver(FORM *form, int c)
+{
+ if (c == (MAX_FORM_COMMAND + 1)
+ && form_driver(form, REQ_VALIDATION) == E_OK)
+ return(TRUE);
+ else
+ {
+ beep();
+ return(FALSE);
+ }
+}
+
+static void demo_forms(void)
+{
+ WINDOW *w;
+ FORM *form;
+ FIELD *f[12], *secure;
+ int finished = 0, c;
+ unsigned n = 0;
+
+ move(18, 0);
+ addstr("Defined form-traversal keys: ^Q/ESC- exit form\n");
+ addstr("^N -- go to next field ^P -- go to previous field\n");
+ addstr("Home -- go to first field End -- go to last field\n");
+ addstr("^L -- go to field to left ^R -- go to field to right\n");
+ addstr("^U -- move upward to field ^D -- move downward to field\n");
+ addstr("^W -- go to next word ^B -- go to previous word\n");
+ addstr("^S -- go to start of field ^E -- go to end of field\n");
+ addstr("^H -- delete previous char ^Y -- delete line\n");
+ addstr("^G -- delete current word ^C -- clear to end of line\n");
+ addstr("^K -- clear to end of field ^X -- clear field\n");
+ addstr("Arrow keys move within a field as you would expect.");
+
+ mvaddstr(4, 57, "Forms Entry Test");
+
+ refresh();
+
+ /* describe the form */
+ f[n++] = make_label(0, 15, "Sample Form");
+ f[n++] = make_label(2, 0, "Last Name");
+ f[n++] = make_field(3, 0, 1, 18, FALSE);
+ f[n++] = make_label(2, 20, "First Name");
+ f[n++] = make_field(3, 20, 1, 12, FALSE);
+ f[n++] = make_label(2, 34, "Middle Name");
+ f[n++] = make_field(3, 34, 1, 12, FALSE);
+ f[n++] = make_label(5, 0, "Comments");
+ f[n++] = make_field(6, 0, 4, 46, FALSE);
+ f[n++] = make_label(5, 20, "Password:");
+ secure =
+ f[n++] = make_field(5, 30, 1, 9, TRUE);
+ f[n++] = (FIELD *)0;
+
+ form = new_form(f);
+
+ display_form(form);
+
+ w = form_win(form);
+ raw();
+ while (!finished)
+ {
+ switch(form_driver(form, c = form_virtualize(form, w)))
+ {
+ case E_OK:
+ mvaddstr(5, 57, field_buffer(secure, 1));
+ clrtoeol();
+ refresh();
+ break;
+ case E_UNKNOWN_COMMAND:
+ finished = my_form_driver(form, c);
+ break;
+ default:
+ beep();
+ break;
+ }
+ }
+
+ erase_form(form);
+
+ free_form(form);
+ for (c = 0; f[c] != 0; c++)
+ free_field(f[c]);
+ noraw();
+}
+#endif /* USE_LIBFORM */
+
+/****************************************************************************
+ *
+ * Overlap test
+ *
+ ****************************************************************************/
+
+static void fillwin(WINDOW *win, char ch)
+{
+ int y, x;
+ int y1, x1;
+
+ getmaxyx(win, y1, x1);
+ for (y = 0; y < y1; y++)
+ {
+ wmove(win, y, 0);
+ for (x = 0; x < x1; x++)
+ waddch(win, ch);
+ }
+}
+
+static void crosswin(WINDOW *win, char ch)
+{
+ int y, x;
+ int y1, x1;
+
+ getmaxyx(win, y1, x1);
+ for (y = 0; y < y1; y++)
+ {
+ for (x = 0; x < x1; x++)
+ if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
+ || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3))))
+ {
+ wmove(win, y, x);
+ waddch(win, ch);
+ }
+ }
+}
+
+static void overlap_test(void)
+/* test effects of overlapping windows */
+{
+ int ch;
+
+ WINDOW *win1 = newwin(9, 20, 3, 3);
+ WINDOW *win2 = newwin(9, 20, 9, 16);
+
+ raw();
+ refresh();
+ move(0, 0);
+ printw("This test shows the behavior of wnoutrefresh() with respect to\n");
+ printw("the shared region of two overlapping windows A and B. The cross\n");
+ printw("pattern in each window does not overlap the other.\n");
+
+
+ move(18, 0);
+ printw("a = refresh A, then B, then doupdate. b = refresh B, then A, then doupdaute\n");
+ printw("c = fill window A with letter A. d = fill window B with letter B.\n");
+ printw("e = cross pattern in window A. f = cross pattern in window B.\n");
+ printw("g = clear window A. h = clear window B.\n");
+ printw("i = overwrite A onto B. j = overwrite B onto A.\n");
+ printw("^Q/ESC = terminate test.");
+
+ while ((ch = Getchar()) != QUIT && ch != ESCAPE)
+ switch (ch)
+ {
+ case 'a': /* refresh window A first, then B */
+ wnoutrefresh(win1);
+ wnoutrefresh(win2);
+ doupdate();
+ break;
+
+ case 'b': /* refresh window B first, then A */
+ wnoutrefresh(win2);
+ wnoutrefresh(win1);
+ doupdate();
+ break;
+
+ case 'c': /* fill window A so it's visible */
+ fillwin(win1, 'A');
+ break;
+
+ case 'd': /* fill window B so it's visible */
+ fillwin(win2, 'B');
+ break;
+
+ case 'e': /* cross test pattern in window A */
+ crosswin(win1, 'A');
+ break;
+
+ case 'f': /* cross test pattern in window A */
+ crosswin(win2, 'B');
+ break;
+
+ case 'g': /* clear window A */
+ wclear(win1);
+ wmove(win1, 0, 0);
+ break;
+
+ case 'h': /* clear window B */
+ wclear(win2);
+ wmove(win2, 0, 0);
+ break;
+
+ case 'i': /* overwrite A onto B */
+ overwrite(win1, win2);
+ break;
+
+ case 'j': /* overwrite B onto A */
+ overwrite(win2, win1);
+ break;
+ }
+
+ delwin(win2);
+ delwin(win1);
+ erase();
+ endwin();
+}
+
+/****************************************************************************
+ *
+ * Main sequence
+ *
+ ****************************************************************************/
+
+static bool
+do_single_test(const char c)
+/* perform a single specified test */
+{
+ switch (c)
+ {
+ case 'a':
+ getch_test();
+ break;
+
+ case 'b':
+ attr_test();
+ break;
+
+ case 'c':
+ if (!has_colors())
+ Cannot("does not support color.");
+ else
+ color_test();
+ break;
+
+ case 'd':
+ if (!has_colors())
+ Cannot("does not support color.");
+ else if (!can_change_color())
+ Cannot("has hardwired color values.");
+ else
+ color_edit();
+ break;
+
+ case 'e':
+ slk_test();
+ break;
+
+ case 'f':
+ acs_display();
+ break;
+
+#if USE_LIBPANEL
+ case 'o':
+ demo_panels();
+ break;
+#endif
+
+ case 'g':
+ acs_and_scroll();
+ break;
+
+ case 'i':
+ flushinp_test(stdscr);
+ break;
+
+ case 'k':
+ test_sgr_attributes();
+ break;
+
+#if USE_LIBMENU
+ case 'm':
+ menu_test();
+ break;
+#endif
+
+#if USE_LIBPANEL
+ case 'p':
+ demo_pad();
+ break;
+#endif
+
+#if USE_LIBFORM
+ case 'r':
+ demo_forms();
+ break;
+#endif
+
+ case 's':
+ overlap_test();
+ break;
+
+#if USE_LIBMENU && defined(TRACE)
+ case 't':
+ trace_set();
+ break;
+#endif
+
+ case '?':
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+usage(void)
+{
+ static const char *const tbl[] = {
+ "Usage: ncurses [options]"
+ ,""
+ ,"Options:"
+ ," -e fmt specify format for soft-keys test (e)"
+ ," -f rip-off footer line (can repeat)"
+ ," -h rip-off header line (can repeat)"
+ ," -s msec specify nominal time for panel-demo (default: 1, to hold)"
+#ifdef TRACE
+ ," -t mask specify default trace-level (may toggle with ^T)"
+#endif
+ };
+ size_t n;
+ for (n = 0; n < sizeof(tbl)/sizeof(tbl[0]); n++)
+ fprintf(stderr, "%s\n", tbl[n]);
+ exit(EXIT_FAILURE);
+}
+
+static void
+set_terminal_modes(void)
+{
+ noraw();
+ cbreak();
+ noecho();
+ scrollok(stdscr, TRUE);
+ idlok(stdscr, TRUE);
+ keypad(stdscr, TRUE);
+}
+
+#ifdef SIGUSR1
+static RETSIGTYPE announce_sig(int sig)
+{
+ (void) fprintf(stderr, "Handled signal %d\r\n", sig);
+}
+#endif
+
+static int rip_footer(WINDOW *win, int columns)
+{
+ wbkgd(win, A_REVERSE);
+ werase(win);
+ wmove(win, 0, 0);
+ wprintw(win, "footer: %d columns", columns);
+ wnoutrefresh(win);
+ return OK;
+}
+
+static int rip_header(WINDOW *win, int columns)
+{
+ wbkgd(win, A_REVERSE);
+ werase(win);
+ wmove(win, 0, 0);
+ wprintw(win, "header: %d columns", columns);
+ wnoutrefresh(win);
+ return OK;
+}
+
+/*+-------------------------------------------------------------------------
+ main(argc,argv)
+--------------------------------------------------------------------------*/
+
+int
+main(int argc, char *argv[])
+{
+ int command, c;
+ int my_e_param = 1;
+
+#if HAVE_LOCALE_H
+ setlocale(LC_CTYPE, "");
+#endif
+
+ while ((c = getopt(argc, argv, "e:fhs:t:")) != EOF) {
+ switch (c) {
+ case 'e':
+ my_e_param = atoi(optarg);
+#ifdef NCURSES_VERSION
+ if (my_e_param > 3) /* allow extended layouts */
+ usage();
+#else
+ if (my_e_param > 1)
+ usage();
+#endif
+ break;
+ case 'f':
+ ripoffline(-1, rip_footer);
+ break;
+ case 'h':
+ ripoffline(1, rip_header);
+ break;
+#if USE_LIBPANEL
+ case 's':
+ nap_msec = atol(optarg);
+ break;
+#endif
+#ifdef TRACE
+ case 't':
+ save_trace = atoi(optarg);
+ break;
+#endif
+ default:
+ usage();
+ }
+ }
+
+ /*
+ * If there's no menus (unlikely for ncurses!), then we'll have to set
+ * tracing on initially, just in case the user wants to test something that
+ * doesn't involve wGetchar.
+ */
+#ifdef TRACE
+ /* enable debugging */
+#if !USE_LIBMENU
+ trace(save_trace);
+#else
+ if (!isatty(fileno(stdin)))
+ trace(save_trace);
+#endif /* USE_LIBMENU */
+#endif /* TRACE */
+
+ /* tell it we're going to play with soft keys */
+ slk_init(my_e_param);
+
+#ifdef SIGUSR1
+ /* set up null signal catcher so we can see what interrupts to getch do */
+ signal(SIGUSR1, announce_sig);
+#endif
+
+ /* we must initialize the curses data structure only once */
+ initscr();
+ bkgdset(BLANK);
+
+ /* tests, in general, will want these modes */
+ start_color();
+ set_terminal_modes();
+ def_prog_mode();
+
+ /*
+ * Return to terminal mode, so we're guaranteed of being able to
+ * select terminal commands even if the capabilities are wrong.
+ */
+ endwin();
+
+ (void) puts("Welcome to ncurses. Press ? for help.");
+
+ do {
+ (void) puts("This is the ncurses main menu");
+ (void) puts("a = keyboard and mouse input test");
+ (void) puts("b = character attribute test");
+ (void) puts("c = color test pattern");
+ (void) puts("d = edit RGB color values");
+ (void) puts("e = exercise soft keys");
+ (void) puts("f = display ACS characters");
+ (void) puts("g = display windows and scrolling");
+ (void) puts("i = test of flushinp()");
+ (void) puts("k = display character attributes");
+#if USE_LIBMENU
+ (void) puts("m = menu code test");
+#endif
+#if USE_LIBPANEL
+ (void) puts("o = exercise panels library");
+ (void) puts("p = exercise pad features");
+ (void) puts("q = quit");
+#endif
+#if USE_LIBFORM
+ (void) puts("r = exercise forms code");
+#endif
+ (void) puts("s = overlapping-refresh test");
+#if USE_LIBMENU && defined(TRACE)
+ (void) puts("t = set trace level");
+#endif
+ (void) puts("? = repeat this command summary");
+
+ (void) fputs("> ", stdout);
+ (void) fflush(stdout); /* necessary under SVr4 curses */
+
+ /*
+ * This used to be an 'fgets()' call. However (on Linux, at least)
+ * mixing stream I/O and 'read()' (used in the library) causes the
+ * input stream to be flushed when switching between the two.
+ */
+ command = 0;
+ for(;;) {
+ char ch;
+ if (read(fileno(stdin), &ch, 1) <= 0) {
+ if (command == 0)
+ command = 'q';
+ break;
+ } else if (command == 0 && !isspace(ch)) {
+ command = ch;
+ } else if (ch == '\n' || ch == '\r') {
+ if (command != 0)
+ break;
+ (void) fputs("> ", stdout);
+ (void) fflush(stdout);
+ }
+ }
+
+ if (do_single_test(command)) {
+ /*
+ * This may be overkill; it's intended to reset everything back
+ * to the initial terminal modes so that tests don't get in
+ * each other's way.
+ */
+ flushinp();
+ set_terminal_modes();
+ reset_prog_mode();
+ clear();
+ refresh();
+ endwin();
+ if (command == '?') {
+ (void) puts("This is the ncurses capability tester.");
+ (void) puts("You may select a test from the main menu by typing the");
+ (void) puts("key letter of the choice (the letter to left of the =)");
+ (void) puts("at the > prompt. The commands `x' or `q' will exit.");
+ }
+ continue;
+ }
+ } while
+ (command != 'q');
+
+ ExitProgram(EXIT_SUCCESS);
+}
+
+/* ncurses.c ends here */