+#if defined(NCURSES_VERSION) && NCURSES_EXT_FUNCS
+#if (NCURSES_VERSION_PATCH < 20070331)
+#define is_keypad(win) (win)->_use_keypad
+#define is_scrollok(win) (win)->_scroll
+#endif
+#else
+#define is_keypad(win) FALSE
+#define is_scrollok(win) FALSE
+#endif
+
+static WINDOW *
+frame_win(FRAME * curp)
+{
+ return (curp != 0) ? curp->wind : stdscr;
+}
+
+/* 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. The
+ * _use_keypad name does not work with Solaris 8.
+ */
+static bool
+HaveKeypad(FRAME * curp)
+{
+ WINDOW *win = frame_win(curp);
+ (void) win;
+ return is_keypad(win);
+}
+
+static bool
+HaveScroll(FRAME * curp)
+{
+ WINDOW *win = frame_win(curp);
+ (void) win;
+ return is_scrollok(win);
+}
+
+static void
+newwin_legend(FRAME * curp)
+{
+#define DATA(num, name) { name, num }
+ static const struct {
+ const char *msg;
+ int code;
+ } legend[] = {
+ DATA(0, "^C = create window"),
+ DATA(0, "^N = next window"),
+ DATA(0, "^P = previous window"),
+ DATA(0, "^F = scroll forward"),
+ DATA(0, "^B = scroll backward"),
+ DATA(1, "^K = keypad(%s)"),
+ DATA(2, "^S = scrollok(%s)"),
+ DATA(0, "^W = save window"),
+ DATA(0, "^R = restore window"),
+#if HAVE_WRESIZE
+ DATA(0, "^X = resize"),
+#endif
+ DATA(3, "^Q%s = exit")
+ };
+#undef DATA
+ size_t n;
+ bool do_keypad = HaveKeypad(curp);
+ bool do_scroll = HaveScroll(curp);
+ char buf[BUFSIZ];
+
+ move(LINES - 4, 0);
+
+ for (n = 0; n < SIZEOF(legend); n++) {
+ int x;
+
+ switch (legend[n].code) {
+ default:
+ _nc_STRCPY(buf, legend[n].msg, sizeof(buf));
+ break;
+ case 1:
+ _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
+ legend[n].msg, do_keypad ? "yes" : "no");
+ break;
+ case 2:
+ _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
+ legend[n].msg, do_scroll ? "yes" : "no");
+ break;
+ case 3:
+ _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
+ legend[n].msg, do_keypad ? "/ESC" : "");
+ break;
+ }
+ x = getcurx(stdscr);
+ 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 = frame_win(curp);
+ 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 case_QUIT:
+ 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;
+ }
+ }
+#endif
+ /* FALLTHRU */
+ 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();
+
+ if (rwindow != 0)
+ wrefresh(rwindow);
+
+ move(0, 0);
+ clrtoeol();
+ return (rwindow);
+}
+
+static void
+newwin_move(FRAME * curp, int dy, int dx)
+{
+ WINDOW *win = frame_win(curp);
+ 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 = 0;
+
+ if (fp != 0) {
+ 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) ? NULL : fp->next;
+ free(fp);
+ }
+ return np;
+}
+
+static int
+scroll_test(bool recur GCC_UNUSED)
+/* Demonstrate windows */
+{
+ int c;
+ FRAME *current = (FRAME *) 0, *neww;
+ WINDOW *usescr;
+#if HAVE_PUTWIN && HAVE_GETWIN
+ FILE *fp;
+#endif
+
+#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'):
+ if ((neww = typeCalloc(FRAME, (size_t) 1)) == 0) {
+ failed("scroll_test");
+ goto breakout;
+ }
+ if ((neww->wind = getwindow()) == (WINDOW *) 0) {
+ failed("scroll_test");
+ free(neww);
+ 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(frame_win(current), 1);
+ break;
+
+ case CTRL('B'): /* scroll current window backwards */
+ if (current)
+ wscrl(frame_win(current), -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;
+
+#if HAVE_PUTWIN && HAVE_GETWIN
+ case CTRL('W'): /* save and delete window */
+ if ((current != 0) && (current == current->next)) {
+ transient(current, "Will not save/delete ONLY window");
+ break;
+ } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
+ transient(current, "Can't open screen dump file");
+ } else {
+ int rc = putwin(frame_win(current), fp);
+ (void) fclose(fp);
+
+ if (rc == OK) {
+ current = delete_framed(current, TRUE);
+ } else {
+ transient(current, "Can't write screen dump file");
+ }
+ }
+ break;
+
+ case CTRL('R'): /* restore window */
+ if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
+ transient(current, "Can't open screen dump file");
+ } else {
+ if ((neww = typeCalloc(FRAME, (size_t) 1)) != 0) {
+
+ neww->next = current ? current->next : 0;
+ neww->last = current;
+ if (neww->last != 0)
+ neww->last->next = neww;
+ if (neww->next != 0)
+ neww->next->last = neww;
+
+ neww->wind = getwin(fp);
+
+ wrefresh(neww->wind);
+ } else {
+ failed("scroll_test");
+ }
+ (void) fclose(fp);
+ }
+ break;
+#endif
+
+#if HAVE_WRESIZE
+ 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) {
+ int i;
+ 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 /* HAVE_WRESIZE */
+
+ 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(frame_win(current), y, x);
+ if (--x < 0) {
+ if (--y < 0)
+ break;
+ x = getmaxx(frame_win(current)) - 1;
+ }
+ (void) mvwdelch(frame_win(current), y, x);
+ }
+ break;
+
+ case '\r':
+ c = '\n';
+ /* FALLTHROUGH */
+
+ default:
+ if (current)
+ waddch(current->wind, (chtype) c);
+ else
+ beep();
+ break;
+ }
+ newwin_report(current);
+ usescr = frame_win(current);
+ wrefresh(usescr);
+ } while
+ (!isQuit(c = wGetchar(usescr), TRUE)
+ && (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();
+ return OK;
+}
+
+/****************************************************************************
+ *
+ * Panels tester
+ *
+ ****************************************************************************/
+
+#if USE_LIBPANEL
+static int nap_msec = 1;
+
+static NCURSES_CONST char *mod[] =
+{
+ "test ",
+ "TEST ",
+ "(**) ",
+ "*()* ",
+ "<--> ",
+ "LAST "
+};
+
+/*+-------------------------------------------------------------------------
+ wait_a_while(msec)
+--------------------------------------------------------------------------*/
+static void
+wait_a_while(int 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 > 1000)
+ sleep((unsigned) msec / 1000);
+ 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);
+ if (text != 0 && *text != '\0') {
+ waddstr(stdscr, text);
+ waddstr(stdscr, "; ");
+ }
+ waddstr(stdscr, "press any key to continue");
+} /* end of saywhat */
+
+/*+-------------------------------------------------------------------------
+ mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
+--------------------------------------------------------------------------*/
+static PANEL *
+mkpanel(NCURSES_COLOR_T 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 (UseColors) {
+ NCURSES_COLOR_T fg = (NCURSES_COLOR_T) ((color == COLOR_BLUE)
+ ? COLOR_WHITE
+ : COLOR_BLACK);
+ NCURSES_COLOR_T bg = color;
+
+ init_pair(color, fg, bg);
+ wbkgdset(win, (attr_t) (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
+init_panel(WINDOW *win)
+{
+ register int y, x;
+
+ for (y = 0; y < LINES - 1; y++) {
+ for (x = 0; x < COLS; x++)
+ wprintw(win, "%d", (y + x) % 10);
+ }
+}
+
+static void
+fill_panel(PANEL *pan)
+{
+ WINDOW *win = panel_window(pan);
+ const char *userptr = (const char *) panel_userptr(pan);
+ int num = (userptr && *userptr) ? userptr[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, UChar(num));
+ }
+ }
+}
+
+#if USE_WIDEC_SUPPORT
+static void
+init_wide_panel(WINDOW *win)
+{
+ int digit;
+ cchar_t temp[10];
+
+ for (digit = 0; digit < 10; ++digit)
+ make_fullwidth_digit(&temp[digit], digit);
+
+ do {
+ int y, x;
+ getyx(stdscr, y, x);
+ digit = (y + x / 2) % 10;
+ } while (wadd_wch(win, &temp[digit]) != ERR);
+}
+
+static void
+fill_wide_panel(PANEL *pan)
+{
+ WINDOW *win = panel_window(pan);
+ const char *userptr = (const char *) panel_userptr(pan);
+ int num = (userptr && *userptr) ? userptr[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, UChar(num));
+ }
+ }
+}
+#endif
+
+#define MAX_PANELS 5
+
+static void
+canned_panel(PANEL *px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
+{
+ int which = cmd[1] - '0';
+
+ saywhat(cmd);
+ switch (*cmd) {
+ case 'h':
+ hide_panel(px[which]);
+ break;
+ case 's':
+ show_panel(px[which]);
+ break;
+ case 't':
+ top_panel(px[which]);
+ break;
+ case 'b':
+ bottom_panel(px[which]);
+ break;
+ case 'd':
+ rmpanel(px[which]);
+ break;
+ }
+ pflush();
+ wait_a_while(nap_msec);
+}
+
+static int
+demo_panels(void (*InitPanel) (WINDOW *), void (*FillPanel) (PANEL *))
+{
+ int count;
+ int itmp;
+ PANEL *px[MAX_PANELS + 1];
+
+ scrollok(stdscr, FALSE); /* we don't want stdscr to scroll! */
+ refresh();
+
+ InitPanel(stdscr);
+ for (count = 0; count < 5; count++) {
+ px[1] = mkpanel(COLOR_RED,
+ LINES / 2 - 2,
+ COLS / 8 + 1,
+ 0,
+ 0);
+ set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
+
+ px[2] = mkpanel(COLOR_GREEN,
+ LINES / 2 + 1,
+ COLS / 7,
+ LINES / 4,
+ COLS / 10);
+ set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
+
+ px[3] = mkpanel(COLOR_YELLOW,
+ LINES / 4,
+ COLS / 10,
+ LINES / 2,
+ COLS / 9);
+ set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
+
+ px[4] = mkpanel(COLOR_BLUE,
+ LINES / 2 - 2,
+ COLS / 8,
+ LINES / 2 - 2,
+ COLS / 3);
+ set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
+
+ px[5] = mkpanel(COLOR_MAGENTA,
+ LINES / 2 - 2,
+ COLS / 8,
+ LINES / 2,
+ COLS / 2 - 2);
+ set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
+
+ FillPanel(px[1]);
+ FillPanel(px[2]);
+ FillPanel(px[3]);
+ FillPanel(px[4]);
+ FillPanel(px[5]);
+
+ hide_panel(px[4]);
+ hide_panel(px[5]);
+ pflush();
+ saywhat("");
+ wait_a_while(nap_msec);
+
+ saywhat("h3 s1 s2 s4 s5");
+ move_panel(px[1], 0, 0);
+ hide_panel(px[3]);
+ show_panel(px[1]);
+ show_panel(px[2]);
+ show_panel(px[4]);
+ show_panel(px[5]);
+ pflush();
+ wait_a_while(nap_msec);
+
+ canned_panel(px, "s1");
+ canned_panel(px, "s2");
+
+ saywhat("m2");
+ move_panel(px[2], LINES / 3 + 1, COLS / 8);
+ pflush();
+ wait_a_while(nap_msec);
+
+ canned_panel(px, "s3");
+
+ saywhat("m3");
+ move_panel(px[3], LINES / 4 + 1, COLS / 15);
+ pflush();
+ wait_a_while(nap_msec);
+
+ canned_panel(px, "b3");
+ canned_panel(px, "s4");
+ canned_panel(px, "s5");
+ canned_panel(px, "t3");
+ canned_panel(px, "t1");
+ canned_panel(px, "t2");
+ canned_panel(px, "t3");
+ canned_panel(px, "t4");
+
+ for (itmp = 0; itmp < 6; itmp++) {
+ WINDOW *w4 = panel_window(px[4]);
+ WINDOW *w5 = panel_window(px[5]);
+
+ saywhat("m4");
+ wmove(w4, LINES / 8, 1);
+ waddstr(w4, mod[itmp]);
+ move_panel(px[4], LINES / 6, itmp * (COLS / 8));
+ wmove(w5, LINES / 6, 1);
+ waddstr(w5, mod[itmp]);
+ pflush();
+ wait_a_while(nap_msec);
+
+ saywhat("m5");
+ wmove(w4, LINES / 6, 1);
+ waddstr(w4, mod[itmp]);
+ move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
+ wmove(w5, LINES / 8, 1);
+ waddstr(w5, mod[itmp]);
+ pflush();
+ wait_a_while(nap_msec);
+ }
+
+ saywhat("m4");
+ move_panel(px[4], LINES / 6, itmp * (COLS / 8));
+ pflush();
+ wait_a_while(nap_msec);
+
+ canned_panel(px, "t5");
+ canned_panel(px, "t2");
+ canned_panel(px, "t1");
+ canned_panel(px, "d2");
+ canned_panel(px, "h3");
+ canned_panel(px, "d1");
+ canned_panel(px, "d4");
+ canned_panel(px, "d5");
+ canned_panel(px, "d3");
+
+ wait_a_while(nap_msec);
+ if (nap_msec == 1)
+ break;
+ nap_msec = 100L;
+ }
+
+ erase();
+ endwin();
+ return OK;
+}
+
+#if USE_LIBPANEL
+static int
+panel_test(bool recur GCC_UNUSED)
+{
+ return demo_panels(init_panel, fill_panel);
+}
+#endif
+
+#if USE_WIDEC_SUPPORT && USE_LIBPANEL
+static int
+x_panel_test(bool recur GCC_UNUSED)
+{
+ return demo_panels(init_wide_panel, fill_wide_panel);
+}
+#endif
+#endif /* USE_LIBPANEL */
+
+/****************************************************************************
+ *
+ * Pad tester
+ *
+ ****************************************************************************/
+
+#if HAVE_NEWPAD
+
+/* 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));
+}
+
+#define GRIDSIZE 3
+
+static bool pending_pan = FALSE;
+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, ESC to quit, ! to shell-out.",
+ "Use +,- (or j,k) to grow/shrink the panner vertically.",
+ "Use <,> (or h,l) to grow/shrink the panner horizontally.",
+ "Number repeats. Toggle legend:? filler:a timer:t scrollmark:s."
+ };
+ int n = ((int) SIZEOF(legend) - (LINES - line));
+ if (n >= 0 && n < (int) SIZEOF(legend)) {
+ if (move(line, 0) != ERR) {
+ 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
+fill_pad(WINDOW *panpad, bool pan_lines, bool colored)
+{
+ int y, x;
+ unsigned gridcount = 0;
+ chtype fill = 0;
+#ifdef A_COLOR
+ if (colored)
+ fill = (chtype) COLOR_PAIR(1);
+#endif
+
+ wmove(panpad, 0, 0);
+ for (y = 0; y < getmaxy(panpad); y++) {
+ for (x = 0; x < getmaxx(panpad); x++) {
+ if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
+ if (y == 0 && x == 0)
+ waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
+ else if (y == 0)
+ waddch(panpad, pan_lines ? ACS_TTEE : '+');
+ else if (y == 0 || x == 0)
+ waddch(panpad, pan_lines ? ACS_LTEE : '+');
+ else
+ waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
+ (int) (gridcount++ % 26)) | fill);
+ } else if (y % GRIDSIZE == 0)
+ waddch(panpad, pan_lines ? ACS_HLINE : '-');
+ else if (x % GRIDSIZE == 0)
+ waddch(panpad, pan_lines ? ACS_VLINE : '|');
+ else
+ waddch(panpad, ' ');
+ }
+ }
+}
+
+static void
+panner(WINDOW *pad,
+ int top_x, int top_y, int porty, int portx,
+ int (*pgetc) (WINDOW *),
+ bool colored)
+{
+ TimeType before, after;
+ bool timing = TRUE;
+ bool pan_lines = FALSE;
+ 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 HELP_KEY_1:
+ if (c == HELP_KEY_1)
+ show_panner_legend = !show_panner_legend;
+ panner_legend(LINES - 4);
+ panner_legend(LINES - 3);
+ panner_legend(LINES - 2);
+ panner_legend(LINES - 1);
+ break;
+ case 'a':
+ pan_lines = !pan_lines;
+ fill_pad(pad, pan_lines, colored);
+ pending_pan = FALSE;
+ break;
+
+ case 't':
+ timing = !timing;
+ if (!timing)
+ panner_legend(LINES - 1);
+ break;
+ 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;
+
+ case 'H':
+ case KEY_HOME:
+ case KEY_FIND:
+ basey = 0;
+ break;
+
+ case 'E':
+ case KEY_END:
+ case KEY_SELECT:
+ basey = pymax - porty;
+ if (basey < 0)
+ basey = 0;
+ 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) ((float) top_x + ((float) basex * ratio));
+ highend = (int) ((float) top_x + ((float) (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) ((float) top_y + ((float) basey * ratio));
+ highend = (int) ((float) top_y + ((float) (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 (!pending_pan) {
+ GetClockTime(&before);
+ wnoutrefresh(stdscr);
+
+ pnoutrefresh(pad,
+ basey, basex,
+ top_y, top_x,
+ porty - (pxmax > portx) - 1,
+ portx - (pymax > porty) - 1);
+
+ doupdate();
+ if (timing) {
+ GetClockTime(&after);
+ move(LINES - 1, COLS - 12);
+ printw("Secs: %6.03f", ElapsedSeconds(&before, &after));
+ refresh();
+ }
+ }
+
+ } while
+ ((c = pgetc(pad)) != KEY_EXIT);
+
+ scrollok(stdscr, TRUE); /* reset to driver's default */
+}
+
+static int
+padgetch(WINDOW *win)
+{
+ static int count;
+ static int last;
+
+ if ((pending_pan = (count > 0)) != FALSE) {
+ count--;
+ pending_pan = (count != 0);
+ } else {
+ for (;;) {
+ int c;
+ switch (c = wGetchar(win)) {
+ case '!':
+ ShellOut(FALSE);
+ /* FALLTHRU */
+ case CTRL('r'):
+ endwin();
+ refresh();
+ c = KEY_REFRESH;
+ break;
+ case CTRL('l'):
+ c = KEY_REFRESH;
+ break;
+ case 'U':
+ c = KEY_UP;
+ break;
+ case 'D':
+ c = KEY_DOWN;
+ break;
+ case 'R':
+ c = KEY_RIGHT;
+ break;
+ case 'L':
+ c = KEY_LEFT;
+ break;
+ case '+':
+ c = KEY_IL;
+ break;
+ case '-':
+ c = KEY_DL;
+ break;
+ case '>':
+ c = KEY_IC;
+ break;
+ case '<':
+ c = KEY_DC;
+ break;
+ case ERR: /* FALLTHRU */
+ case case_QUIT:
+ count = 0;
+ c = KEY_EXIT;
+ break;
+ default:
+ if (c >= '0' && c <= '9') {
+ count = count * 10 + (c - '0');
+ continue;
+ }
+ break;
+ }
+ last = c;
+ break;
+ }
+ if (count > 0)
+ count--;
+ }
+ return (last);
+}
+
+#define PAD_HIGH 200
+#define PAD_WIDE 200
+
+static int
+pad_test(bool recur GCC_UNUSED)
+/* Demonstrate pads. */
+{
+ WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
+
+ if (panpad == 0) {
+ Cannot("cannot create requested pad");
+ return ERR;
+ }
+#ifdef A_COLOR
+ if (UseColors) {
+ init_pair(1, COLOR_BLACK, COLOR_GREEN);
+ init_pair(2, COLOR_CYAN, COLOR_BLUE);
+ wbkgd(panpad, (chtype) (COLOR_PAIR(2) | ' '));
+ }
+#endif
+ fill_pad(panpad, FALSE, TRUE);
+
+ 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, TRUE);
+
+ delwin(panpad);
+ endwin();
+ erase();
+ return OK;
+}
+#endif /* HAVE_NEWPAD */
+
+/****************************************************************************
+ *
+ * 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 int
+flushinp_test(bool recur GCC_UNUSED)
+/* Input test, adapted from John Burnell's PDCurses tester */
+{
+ WINDOW *win = stdscr;
+ 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 ERR;
+
+#ifdef A_COLOR
+ if (UseColors) {
+ init_pair(2, COLOR_CYAN, COLOR_BLUE);
+ wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
+ }
+#endif
+ (void) wattrset(subWin, A_BOLD);
+ 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();
+ return OK;
+}
+
+/****************************************************************************
+ *
+ * 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_MENUS char *animals[] =
+{
+ "Lions",
+ "Tigers",
+ "Bears",
+ "(Oh my!)",
+ "Newts",
+ "Platypi",
+ "Lemurs",
+ "(Oh really?!)",
+ "Leopards",
+ "Panthers",
+ "Pumas",
+ "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
+ "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
+ (char *) 0
+};
+
+static int
+menu_test(bool recur GCC_UNUSED)
+{
+ MENU *m;
+ ITEM *items[SIZEOF(animals)];
+ ITEM **ip = items;
+ CONST_MENUS 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++) {
+ if ((*ip = new_item(*ap, "")) != 0)
+ ++ip;
+ }
+ *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_NOT_POSTED)
+ break;
+ if (c == E_REQUEST_DENIED)
+ beep();
+ }
+
+ 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
+ return OK;
+}
+
+#ifdef TRACE
+#define T_TBL(name) { #name, name }
+static struct {
+ const char *name;
+ unsigned 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_DATABASE),
+ T_TBL(TRACE_ATTRS),
+ T_TBL(TRACE_MAXIMUM),
+ {
+ (char *) 0, 0
+ }
+};
+
+static char *
+tracetrace(unsigned tlevel)
+{
+ static char *buf;
+ static size_t need = 12;
+ int n;
+
+ if (buf == 0) {
+ for (n = 0; t_tbl[n].name != 0; n++)
+ need += strlen(t_tbl[n].name) + 2;
+ buf = typeMalloc(char, need);
+ if (!buf)
+ failed("tracetrace");
+ }
+ _nc_SPRINTF(buf, _nc_SLIMIT(need) "0x%02x = {", tlevel);
+ if (tlevel == 0) {
+ _nc_STRCAT(buf, t_tbl[0].name ? t_tbl[0].name : "", need);
+ _nc_STRCAT(buf, ", ", need);
+ } else {
+ for (n = 1; t_tbl[n].name != 0; n++)
+ if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
+ _nc_STRCAT(buf, t_tbl[n].name, need);
+ _nc_STRCAT(buf, ", ", need);
+ }
+ }
+ if (buf[strlen(buf) - 2] == ',')
+ buf[strlen(buf) - 2] = '\0';
+ _nc_STRCAT(buf, "}", need);
+ return 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 int
+trace_set(bool recur GCC_UNUSED)
+/* interactively set the trace level */
+{
+ MENU *m;
+ ITEM *items[SIZEOF(t_tbl)];
+ ITEM **ip = items;
+ int mrows, mcols;
+ unsigned 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++) {
+ if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
+ ++ip;
+ }
+ }
+ *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++) {
+ unsigned 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)) {
+ /* EMPTY */ ;
+ }
+
+ newtrace = 0;
+ for (ip = menu_items(m); *ip; ip++)
+ if (item_value(*ip))
+ newtrace |= t_tbl[item_index(*ip)].mask;
+ curses_trace(newtrace);
+ Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
+
+ 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);
+
+ return OK;
+}
+#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, (int) strlen(label), frow, fcol, 0, 0);
+
+ if (f) {
+ set_field_buffer(f, 0, label);
+ set_field_opts(f, (int) ((unsigned) field_opts(f) & (unsigned) ~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, nrow, nbuf;
+
+ if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
+ && nbuf > 0) {
+ char *source = field_buffer(me, 1);
+ size_t have = (source ? strlen(source) : 0) + 1;
+ size_t need = 80 + have;
+ char *temp = malloc(need);
+
+ if (temp != 0) {
+ size_t len;
+ _nc_STRNCPY(temp, source ? source : "", have + 1);
+ len = (size_t) (char *) field_userptr(me);
+ if (c <= KEY_MAX) {
+ if (isgraph(c) && (len + 1) < sizeof(temp)) {
+ temp[len++] = (char) 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);
+ free(temp);
+ }
+ }
+ return c;
+}
+
+static int
+form_virtualize(FORM *f, WINDOW *w)
+{
+ /* *INDENT-OFF* */
+ 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 }
+ };
+ /* *INDENT-ON* */
+
+ static int mode = REQ_INS_MODE;
+ int c = wGetchar(w);
+ FIELD *me = current_field(f);
+ bool current = TRUE;
+
+ if (c == CTRL(']')) {
+ if (mode == REQ_INS_MODE) {
+ mode = REQ_OVL_MODE;
+ } else {
+ mode = REQ_INS_MODE;
+ }
+ c = mode;
+ } else {
+ unsigned n;
+ for (n = 0; n < SIZEOF(lookup); n++) {
+ if (lookup[n].code == c) {
+ c = lookup[n].result;
+ break;
+ }
+ }
+ }
+ MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
+
+ /*
+ * Force the field that the user is typing into to be in reverse video,
+ * while the other fields are shown underlined.
+ */
+ switch (c) {
+ case REQ_BEG_FIELD:
+ case REQ_CLR_EOF:
+ case REQ_CLR_EOL:
+ case REQ_CLR_FIELD:
+ case REQ_DEL_CHAR:
+ case REQ_DEL_LINE:
+ case REQ_DEL_PREV:
+ case REQ_DEL_WORD:
+ case REQ_END_FIELD:
+ case REQ_INS_CHAR:
+ case REQ_INS_LINE:
+ case REQ_LEFT_CHAR:
+ case REQ_LEFT_FIELD:
+ case REQ_NEXT_WORD:
+ case REQ_RIGHT_CHAR:
+ current = TRUE;
+ break;
+ default:
+ current = (c < KEY_MAX);
+ break;
+ }
+ if (current) {
+ c = edit_secure(me, c);
+ set_field_back(me, A_REVERSE);
+ } else {
+ 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);
+ }
+}
+
+#ifdef NCURSES_VERSION
+#define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
+#define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
+#else
+#define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
+#define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
+#endif
+
+/*
+ * Allow a middle initial, optionally with a '.' to end it.
+ */
+static
+FIELDCHECK_CB(mi_field_check)
+{
+ char *s = field_buffer(fld, 0);
+ int state = 0;
+ int n;
+
+ for (n = 0; s[n] != '\0'; ++n) {
+ switch (state) {
+ case 0:
+ if (s[n] == '.') {
+ if (n != 1)
+ return FALSE;
+ state = 2;
+ } else if (isspace(UChar(s[n]))) {
+ state = 2;
+ }
+ break;
+ case 2:
+ if (!isspace(UChar(s[n])))
+ return FALSE;
+ break;
+ }
+ }
+
+ /* force the form to display a leading capital */
+ if (islower(UChar(s[0]))) {
+ s[0] = (char) toupper(UChar(s[0]));
+ set_field_buffer(fld, 0, s);
+ }
+ return TRUE;
+}
+
+static
+CHAR_CHECK_CB(mi_char_check)
+{
+ return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
+}
+
+/*
+ * Passwords should be at least 6 characters.
+ */
+static
+FIELDCHECK_CB(pw_field_check)
+{
+ char *s = field_buffer(fld, 0);
+ int n;
+
+ for (n = 0; s[n] != '\0'; ++n) {
+ if (isspace(UChar(s[n]))) {
+ if (n < 6)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static
+CHAR_CHECK_CB(pw_char_check)
+{
+ return (isgraph(ch) ? TRUE : FALSE);
+}
+
+static int
+form_test(bool recur GCC_UNUSED)
+{
+ FORM *form;
+ FIELD *f[12], *secure;
+ FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
+ FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
+ int c;
+ unsigned n = 0;
+
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
+#endif
+
+ move(18, 0);
+ addstr("Defined edit/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. ^] toggles overlay mode.");
+
+ MvAddStr(4, 57, "Forms Entry Test");
+
+ refresh();
+
+ /* describe the form */
+ memset(f, 0, sizeof(f));
+ 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);
+ set_field_type(f[n - 1], TYPE_ALPHA, 1);
+
+ f[n++] = make_label(2, 20, "First Name");
+ f[n++] = make_field(3, 20, 1, 12, FALSE);
+ set_field_type(f[n - 1], TYPE_ALPHA, 1);
+
+ f[n++] = make_label(2, 34, "Middle Name");
+ f[n++] = make_field(3, 34, 1, 12, FALSE);
+ set_field_type(f[n - 1], fty_middle);
+
+ 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);
+ set_field_type(f[n - 1], fty_passwd);
+ f[n] = (FIELD *) 0;
+
+ if ((form = new_form(f)) != 0) {
+ WINDOW *w;
+ int finished = 0;
+
+ display_form(form);
+
+ w = form_win(form);
+ raw();
+ nonl(); /* lets us read ^M's */
+ 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]);
+ free_fieldtype(fty_middle);
+ free_fieldtype(fty_passwd);
+ noraw();
+ nl();