034bdf81efba5e3b7084b59777fb0d34d5dae98f
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2004,2005 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /****************************************************************************
29
30 NAME
31    ncurses.c --- ncurses library exerciser
32
33 SYNOPSIS
34    ncurses
35
36 DESCRIPTION
37    An interactive test module for the ncurses library.
38
39 AUTHOR
40    Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
41            Thomas E. Dickey (beginning revision 1.27 in 1996).
42
43 $Id: ncurses.c,v 1.253 2005/10/01 16:00:56 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #if HAVE_GETTIMEOFDAY
50 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
51 #include <sys/time.h>
52 #endif
53 #if HAVE_SYS_SELECT_H
54 #include <sys/select.h>
55 #endif
56 #endif
57
58 #if USE_LIBPANEL
59 #include <panel.h>
60 #endif
61
62 #if USE_LIBMENU
63 #include <menu.h>
64 #endif
65
66 #if USE_LIBFORM
67 #include <form.h>
68 #endif
69
70 #ifdef NCURSES_VERSION
71
72 #ifdef TRACE
73 static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS;
74 extern unsigned _nc_tracing;
75 #endif
76
77 #else
78
79 #define mmask_t chtype          /* not specified in XSI */
80
81 #ifdef CURSES_ACS_ARRAY
82 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
83 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
84 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
85 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
86 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
87 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
88 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
89 #else
90 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
91 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
92 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
93 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
94 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
95 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
96 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
97 #endif
98
99 #ifdef CURSES_WACS_ARRAY
100 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
101 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
102 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
103 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
104 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
105 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
106 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
107 #endif
108
109 #endif
110
111 #define P(string)       printw("%s\n", string)
112
113 #define BLANK           ' '     /* this is the background character */
114
115 #undef max_colors
116 static int max_colors;          /* the actual number of colors we'll use */
117 static int min_colors;          /* the minimum color code */
118
119 #undef max_pairs
120 static int max_pairs;           /* ...and the number of color pairs */
121
122 typedef struct {
123     short red;
124     short green;
125     short blue;
126 } RGB_DATA;
127
128 static RGB_DATA *all_colors;
129
130 static void main_menu(bool);
131
132 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
133  * though we can rely on negative x/y values to stop the macro.
134  */
135 static void
136 do_h_line(int y, int x, chtype c, int to)
137 {
138     if ((to) > (x))
139         mvhline(y, x, c, (to) - (x));
140 }
141
142 static void
143 do_v_line(int y, int x, chtype c, int to)
144 {
145     if ((to) > (y))
146         mvvline(y, x, c, (to) - (y));
147 }
148
149 static void
150 Repaint(void)
151 {
152     touchwin(stdscr);
153     touchwin(curscr);
154     wrefresh(curscr);
155 }
156
157 /* Common function to allow ^T to toggle trace-mode in the middle of a test
158  * so that trace-files can be made smaller.
159  */
160 static int
161 wGetchar(WINDOW *win)
162 {
163     int c;
164 #ifdef TRACE
165     while ((c = wgetch(win)) == CTRL('T')) {
166         if (_nc_tracing) {
167             save_trace = _nc_tracing;
168             _tracef("TOGGLE-TRACING OFF");
169             _nc_tracing = 0;
170         } else {
171             _nc_tracing = save_trace;
172         }
173         trace(_nc_tracing);
174         if (_nc_tracing)
175             _tracef("TOGGLE-TRACING ON");
176     }
177 #else
178     c = wgetch(win);
179 #endif
180     return c;
181 }
182 #define Getchar() wGetchar(stdscr)
183
184 /* replaces wgetnstr(), since we want to be able to edit values */
185 static void
186 wGetstring(WINDOW *win, char *buffer, int limit)
187 {
188     int y0, x0, x, ch;
189     bool done = FALSE;
190
191     echo();
192     getyx(win, y0, x0);
193     wattrset(win, A_REVERSE);
194
195     x = strlen(buffer);
196     while (!done) {
197         if (x > (int) strlen(buffer))
198             x = (int) strlen(buffer);
199         wmove(win, y0, x0);
200         wprintw(win, "%-*s", limit, buffer);
201         wmove(win, y0, x0 + x);
202         switch (ch = wGetchar(win)) {
203         case '\n':
204         case KEY_ENTER:
205             done = TRUE;
206             break;
207         case CTRL('U'):
208             *buffer = '\0';
209             break;
210         case '\b':
211         case KEY_BACKSPACE:
212         case KEY_DC:
213             if (x > 0) {
214                 int j;
215                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
216                     ;
217                 }
218             } else {
219                 beep();
220             }
221             break;
222         case KEY_LEFT:
223             if (x > 0) {
224                 --x;
225             } else {
226                 flash();
227             }
228             break;
229         case KEY_RIGHT:
230             ++x;
231             break;
232         default:
233             if (!isprint(ch) || ch >= KEY_MIN) {
234                 beep();
235             } else if ((int) strlen(buffer) < limit) {
236                 int j;
237                 for (j = strlen(buffer) + 1; j > x; --j) {
238                     buffer[j] = buffer[j - 1];
239                 }
240                 buffer[x++] = ch;
241             } else {
242                 flash();
243             }
244         }
245     }
246
247     wattroff(win, A_REVERSE);
248     wmove(win, y0, x0);
249     noecho();
250 }
251
252 #if USE_WIDEC_SUPPORT
253 static int
254 wGet_wchar(WINDOW *win, wint_t *result)
255 {
256     int c;
257 #ifdef TRACE
258     while ((c = wget_wch(win, result)) == CTRL('T')) {
259         if (_nc_tracing) {
260             save_trace = _nc_tracing;
261             _tracef("TOGGLE-TRACING OFF");
262             _nc_tracing = 0;
263         } else {
264             _nc_tracing = save_trace;
265         }
266         trace(_nc_tracing);
267         if (_nc_tracing)
268             _tracef("TOGGLE-TRACING ON");
269     }
270 #else
271     c = wget_wch(win, result);
272 #endif
273     return c;
274 }
275 #define Get_wchar(result) wGet_wchar(stdscr, result)
276
277 /* replaces wgetn_wstr(), since we want to be able to edit values */
278 static void
279 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
280 {
281     int y0, x0, x;
282     wint_t ch;
283     bool done = FALSE;
284     bool fkey = FALSE;
285
286     echo();
287     getyx(win, y0, x0);
288     wattrset(win, A_REVERSE);
289
290     x = wcslen(buffer);
291     while (!done) {
292         if (x > (int) wcslen(buffer))
293             x = (int) wcslen(buffer);
294
295         /* clear the "window' */
296         wmove(win, y0, x0);
297         wprintw(win, "%*s", limit, " ");
298
299         /* write the existing buffer contents */
300         wmove(win, y0, x0);
301         waddnwstr(win, buffer, limit);
302
303         /* positions the cursor past character 'x' */
304         wmove(win, y0, x0);
305         waddnwstr(win, buffer, x);
306
307         switch (wGet_wchar(win, &ch)) {
308         case KEY_CODE_YES:
309             fkey = TRUE;
310             switch (ch) {
311             case KEY_ENTER:
312                 ch = '\n';
313                 fkey = FALSE;
314                 break;
315             case KEY_BACKSPACE:
316             case KEY_DC:
317                 ch = '\b';
318                 fkey = FALSE;
319                 break;
320             case KEY_LEFT:
321             case KEY_RIGHT:
322                 break;
323             default:
324                 ch = (wint_t) -1;
325                 break;
326             }
327             break;
328         case OK:
329             fkey = FALSE;
330             break;
331         default:
332             ch = (wint_t) -1;
333             fkey = TRUE;
334             break;
335         }
336
337         switch (ch) {
338         case '\n':
339             done = TRUE;
340             break;
341         case CTRL('U'):
342             *buffer = '\0';
343             break;
344         case '\b':
345             if (x > 0) {
346                 int j;
347                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
348                     ;
349                 }
350             } else {
351                 beep();
352             }
353             break;
354         case KEY_LEFT:
355             if (x > 0) {
356                 --x;
357             } else {
358                 beep();
359             }
360             break;
361         case KEY_RIGHT:
362             ++x;
363             break;
364         default:
365             if (fkey) {
366                 beep();
367             } else if ((int) wcslen(buffer) < limit) {
368                 int j;
369                 for (j = wcslen(buffer) + 1; j > x; --j) {
370                     buffer[j] = buffer[j - 1];
371                 }
372                 buffer[x++] = ch;
373             } else {
374                 beep();
375             }
376         }
377     }
378
379     wattroff(win, A_REVERSE);
380     wmove(win, y0, x0);
381     noecho();
382 }
383
384 #endif
385
386 static void
387 Pause(void)
388 {
389     move(LINES - 1, 0);
390     addstr("Press any key to continue... ");
391     (void) Getchar();
392 }
393
394 static void
395 Cannot(const char *what)
396 {
397     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
398     Pause();
399 }
400
401 static void
402 ShellOut(bool message)
403 {
404     if (message)
405         addstr("Shelling out...");
406     def_prog_mode();
407     endwin();
408     system("sh");
409     if (message)
410         addstr("returned from shellout.\n");
411     refresh();
412 }
413
414 #ifdef NCURSES_MOUSE_VERSION
415 /*
416  * This function is the same as _tracemouse(), but we cannot count on that
417  * being available in the non-debug library.
418  */
419 static const char *
420 mouse_decode(MEVENT const *ep)
421 {
422     static char buf[80 + (5 * 10) + (32 * 15)];
423
424     (void) sprintf(buf, "id %2d  at (%2d, %2d, %2d) state %4lx = {",
425                    ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
426
427 #define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
428
429     SHOW(BUTTON1_RELEASED, "release-1");
430     SHOW(BUTTON1_PRESSED, "press-1");
431     SHOW(BUTTON1_CLICKED, "click-1");
432     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
433     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
434 #if NCURSES_MOUSE_VERSION == 1
435     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
436 #endif
437
438     SHOW(BUTTON2_RELEASED, "release-2");
439     SHOW(BUTTON2_PRESSED, "press-2");
440     SHOW(BUTTON2_CLICKED, "click-2");
441     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
442     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
443 #if NCURSES_MOUSE_VERSION == 1
444     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
445 #endif
446
447     SHOW(BUTTON3_RELEASED, "release-3");
448     SHOW(BUTTON3_PRESSED, "press-3");
449     SHOW(BUTTON3_CLICKED, "click-3");
450     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
451     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
452 #if NCURSES_MOUSE_VERSION == 1
453     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
454 #endif
455
456     SHOW(BUTTON4_RELEASED, "release-4");
457     SHOW(BUTTON4_PRESSED, "press-4");
458     SHOW(BUTTON4_CLICKED, "click-4");
459     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
460     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
461 #if NCURSES_MOUSE_VERSION == 1
462     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
463 #endif
464
465 #if NCURSES_MOUSE_VERSION == 2
466     SHOW(BUTTON5_RELEASED, "release-5");
467     SHOW(BUTTON5_PRESSED, "press-5");
468     SHOW(BUTTON5_CLICKED, "click-5");
469     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
470     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
471 #endif
472
473     SHOW(BUTTON_CTRL, "ctrl");
474     SHOW(BUTTON_SHIFT, "shift");
475     SHOW(BUTTON_ALT, "alt");
476     SHOW(ALL_MOUSE_EVENTS, "all-events");
477     SHOW(REPORT_MOUSE_POSITION, "position");
478
479 #undef SHOW
480
481     if (buf[strlen(buf) - 1] == ' ')
482         buf[strlen(buf) - 2] = '\0';
483     (void) strcat(buf, "}");
484     return (buf);
485 }
486 #endif /* NCURSES_MOUSE_VERSION */
487
488 /****************************************************************************
489  *
490  * Character input test
491  *
492  ****************************************************************************/
493
494 static void
495 setup_getch(WINDOW *win, bool flags[])
496 {
497     keypad(win, flags['k']);    /* should be redundant, but for testing */
498     meta(win, flags['m']);      /* force this to a known state */
499     if (flags['e'])
500         echo();
501     else
502         noecho();
503 }
504
505 static void
506 wgetch_help(WINDOW *win, bool flags[])
507 {
508     static const char *help[] =
509     {
510         "e -- toggle echo mode"
511         ,"g -- triggers a getstr test"
512         ,"k -- toggle keypad/literal mode"
513         ,"m -- toggle meta (7-bit/8-bit) mode"
514         ,"q -- quit (x also exits)"
515         ,"s -- shell out\n"
516         ,"w -- create a new window"
517 #ifdef SIGTSTP
518         ,"z -- suspend this process"
519 #endif
520     };
521     int y, x;
522     unsigned chk = ((SIZEOF(help) + 1) / 2);
523     unsigned n;
524
525     getyx(win, y, x);
526     move(0, 0);
527     printw("Type any key to see its %s value.  Also:\n",
528            flags['k'] ? "keypad" : "literal");
529     for (n = 0; n < SIZEOF(help); ++n) {
530         int row = 1 + (n % chk);
531         int col = (n >= chk) ? COLS / 2 : 0;
532         int flg = ((strstr(help[n], "toggle") != 0)
533                    && (flags[UChar(*help[n])] != FALSE));
534         if (flg)
535             standout();
536         mvprintw(row, col, "%s", help[n]);
537         if (col == 0)
538             clrtoeol();
539         if (flg)
540             standend();
541     }
542     wrefresh(stdscr);
543     wmove(win, y, x);
544 }
545
546 static void
547 wgetch_wrap(WINDOW *win, int first_y)
548 {
549     int last_y = getmaxy(win) - 1;
550     int y = getcury(win) + 1;
551
552     if (y >= last_y)
553         y = first_y;
554     wmove(win, y, 0);
555     wclrtoeol(win);
556 }
557
558 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
559 typedef struct {
560     WINDOW *text;
561     WINDOW *frame;
562 } WINSTACK;
563
564 static WINSTACK *winstack = 0;
565 static unsigned len_winstack = 0;
566
567 static void
568 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
569 {
570     unsigned need = (level + 1) * 2;
571
572     if (winstack == 0) {
573         len_winstack = 20;
574         winstack = (WINSTACK *) malloc(len_winstack * sizeof(WINSTACK));
575     } else if (need >= len_winstack) {
576         len_winstack = need;
577         winstack = (WINSTACK *) realloc(winstack, len_winstack * sizeof(WINSTACK));
578     }
579     winstack[level].text = txt_win;
580     winstack[level].frame = box_win;
581 }
582
583 /*
584  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
585  * Resize both and paint the box in the parent.
586  */
587 static void
588 resize_boxes(unsigned level, WINDOW *win)
589 {
590     unsigned n;
591     int base = 5;
592     int high = LINES - base;
593     int wide = COLS;
594
595     touchwin(stdscr);
596     wnoutrefresh(stdscr);
597
598     /* FIXME: this chunk should be done in resizeterm() */
599     slk_touch();
600     slk_clear();
601     slk_noutrefresh();
602
603     for (n = 0; n < level; ++n) {
604         wresize(winstack[n].frame, high, wide);
605         wresize(winstack[n].text, high - 2, wide - 2);
606         high -= 2;
607         wide -= 2;
608         werase(winstack[n].text);
609         box(winstack[n].frame, 0, 0);
610         wnoutrefresh(winstack[n].frame);
611         wprintw(winstack[n].text,
612                 "size %dx%d\n",
613                 getmaxy(winstack[n].text),
614                 getmaxx(winstack[n].text));
615         wnoutrefresh(winstack[n].text);
616         if (winstack[n].text == win)
617             break;
618     }
619     doupdate();
620 }
621 #else
622 #define remember_boxes(level,text,frame)        /* nothing */
623 #endif
624
625 static void
626 wgetch_test(unsigned level, WINDOW *win, int delay)
627 {
628     char buf[BUFSIZ];
629     int first_y, first_x;
630     int c;
631     int incount = 0;
632     bool flags[256];
633     bool blocking = (delay < 0);
634
635     memset(flags, FALSE, sizeof(flags));
636     flags[UChar('k')] = (win == stdscr);
637
638     setup_getch(win, flags);
639     wtimeout(win, delay);
640     getyx(win, first_y, first_x);
641
642     wgetch_help(win, flags);
643     wsetscrreg(win, first_y, getmaxy(win) - 1);
644     scrollok(win, TRUE);
645
646     for (;;) {
647         while ((c = wGetchar(win)) == ERR) {
648             incount++;
649             if (blocking) {
650                 (void) wprintw(win, "%05d: input error", incount);
651                 break;
652             } else {
653                 (void) wprintw(win, "%05d: input timed out", incount);
654             }
655             wgetch_wrap(win, first_y);
656         }
657         if (c == ERR && blocking) {
658             wprintw(win, "ERR");
659             wgetch_wrap(win, first_y);
660         } else if (c == 'x' || c == 'q') {
661             break;
662         } else if (c == 'e') {
663             flags[UChar('e')] = !flags[UChar('e')];
664             setup_getch(win, flags);
665             wgetch_help(win, flags);
666         } else if (c == 'g') {
667             waddstr(win, "getstr test: ");
668             echo();
669             wgetnstr(win, buf, sizeof(buf) - 1);
670             noecho();
671             wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
672             wclrtoeol(win);
673             wgetch_wrap(win, first_y);
674         } else if (c == 'k') {
675             flags[UChar('k')] = !flags[UChar('k')];
676             setup_getch(win, flags);
677             wgetch_help(win, flags);
678         } else if (c == 'm') {
679             flags[UChar('m')] = !flags[UChar('m')];
680             setup_getch(win, flags);
681             wgetch_help(win, flags);
682         } else if (c == 's') {
683             ShellOut(TRUE);
684         } else if (c == 'w') {
685             int high = getmaxy(win) - 1 - first_y + 1;
686             int wide = getmaxx(win) - first_x;
687             int old_y, old_x;
688             int new_y = first_y + getbegy(win);
689             int new_x = first_x + getbegx(win);
690
691             getyx(win, old_y, old_x);
692             if (high > 2 && wide > 2) {
693                 WINDOW *wb = newwin(high, wide, new_y, new_x);
694                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
695
696                 box(wb, 0, 0);
697                 wrefresh(wb);
698                 wmove(wi, 0, 0);
699                 remember_boxes(level, wi, wb);
700                 wgetch_test(level + 1, wi, delay);
701                 delwin(wi);
702                 delwin(wb);
703
704                 wgetch_help(win, flags);
705                 wmove(win, old_y, old_x);
706                 touchwin(win);
707                 wrefresh(win);
708                 doupdate();
709             }
710 #ifdef SIGTSTP
711         } else if (c == 'z') {
712             kill(getpid(), SIGTSTP);
713 #endif
714         } else {
715             wprintw(win, "Key pressed: %04o ", c);
716 #ifdef NCURSES_MOUSE_VERSION
717             if (c == KEY_MOUSE) {
718                 int y, x;
719                 MEVENT event;
720
721                 getmouse(&event);
722                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
723                 getyx(win, y, x);
724                 move(event.y, event.x);
725                 addch('*');
726                 wmove(win, y, x);
727             } else
728 #endif /* NCURSES_MOUSE_VERSION */
729             if (c >= KEY_MIN) {
730 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
731                 if (c == KEY_RESIZE) {
732                     resize_boxes(level, win);
733                 }
734 #endif
735                 (void) waddstr(win, keyname(c));
736             } else if (c > 0x80) {
737                 unsigned c2 = (c & 0x7f);
738                 if (isprint(c2))
739                     (void) wprintw(win, "M-%c", UChar(c2));
740                 else
741                     (void) wprintw(win, "M-%s", unctrl(c2));
742                 waddstr(win, " (high-half character)");
743             } else {
744                 if (isprint(c))
745                     (void) wprintw(win, "%c (ASCII printable character)", c);
746                 else
747                     (void) wprintw(win, "%s (ASCII control character)",
748                                    unctrl(UChar(c)));
749             }
750             wgetch_wrap(win, first_y);
751         }
752     }
753
754     wtimeout(win, -1);
755 }
756
757 static int
758 begin_getch_test(void)
759 {
760     char buf[BUFSIZ];
761     int delay;
762
763     refresh();
764
765 #ifdef NCURSES_MOUSE_VERSION
766     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
767 #endif
768
769     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
770     echo();
771     getnstr(buf, sizeof(buf) - 1);
772     noecho();
773     nonl();
774
775     if (isdigit(UChar(buf[0]))) {
776         delay = atoi(buf) * 100;
777     } else {
778         delay = -1;
779     }
780     raw();
781     move(5, 0);
782     return delay;
783 }
784
785 static void
786 finish_getch_test(void)
787 {
788 #ifdef NCURSES_MOUSE_VERSION
789     mousemask(0, (mmask_t *) 0);
790 #endif
791     erase();
792     noraw();
793     nl();
794     endwin();
795 }
796
797 static void
798 getch_test(void)
799 {
800     int delay = begin_getch_test();
801     wgetch_test(0, stdscr, delay);
802     finish_getch_test();
803 }
804
805 #if USE_WIDEC_SUPPORT
806 /*
807  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
808  * Resize both and paint the box in the parent.
809  */
810 #ifdef KEY_RESIZE
811 static void
812 resize_wide_boxes(unsigned level, WINDOW *win)
813 {
814     unsigned n;
815     int base = 5;
816     int high = LINES - base;
817     int wide = COLS;
818
819     touchwin(stdscr);
820     wnoutrefresh(stdscr);
821
822     /* FIXME: this chunk should be done in resizeterm() */
823     slk_touch();
824     slk_clear();
825     slk_noutrefresh();
826
827     for (n = 0; n < level; ++n) {
828         wresize(winstack[n].frame, high, wide);
829         wresize(winstack[n].text, high - 2, wide - 2);
830         high -= 2;
831         wide -= 2;
832         werase(winstack[n].text);
833         box_set(winstack[n].frame, 0, 0);
834         wnoutrefresh(winstack[n].frame);
835         wprintw(winstack[n].text,
836                 "size %dx%d\n",
837                 getmaxy(winstack[n].text),
838                 getmaxx(winstack[n].text));
839         wnoutrefresh(winstack[n].text);
840         if (winstack[n].text == win)
841             break;
842     }
843     doupdate();
844 }
845 #endif /* KEY_RESIZE */
846
847 static char *
848 wcstos(const wchar_t *src)
849 {
850     int need;
851     mbstate_t state;
852     char *result = 0;
853     const wchar_t *tmp = src;
854
855     memset(&state, 0, sizeof(state));
856     if ((need = wcsrtombs(0, &tmp, 0, &state)) > 0) {
857         unsigned have = need;
858         result = (char *) calloc(have + 1, 1);
859         tmp = src;
860         if (wcsrtombs(result, &tmp, have, &state) != have) {
861             free(result);
862             result = 0;
863         }
864     }
865     return result;
866 }
867
868 static void
869 wget_wch_test(unsigned level, WINDOW *win, int delay)
870 {
871     wchar_t wchar_buf[BUFSIZ];
872     wint_t wint_buf[BUFSIZ];
873     int first_y, first_x;
874     wint_t c;
875     int incount = 0;
876     bool flags[256];
877     bool blocking = (delay < 0);
878     int y, x, code;
879     char *temp;
880
881     memset(flags, FALSE, sizeof(flags));
882     flags[UChar('k')] = (win == stdscr);
883
884     setup_getch(win, flags);
885     wtimeout(win, delay);
886     getyx(win, first_y, first_x);
887
888     wgetch_help(win, flags);
889     wsetscrreg(win, first_y, getmaxy(win) - 1);
890     scrollok(win, TRUE);
891
892     for (;;) {
893         while ((code = wGet_wchar(win, &c)) == ERR) {
894             incount++;
895             if (blocking) {
896                 (void) wprintw(win, "%05d: input error", incount);
897                 break;
898             } else {
899                 (void) wprintw(win, "%05d: input timed out", incount);
900             }
901             wgetch_wrap(win, first_y);
902         }
903         if (code == ERR && blocking) {
904             wprintw(win, "ERR");
905             wgetch_wrap(win, first_y);
906         } else if (c == 'x' || c == 'q') {
907             break;
908         } else if (c == 'e') {
909             flags[UChar('e')] = !flags[UChar('e')];
910             setup_getch(win, flags);
911             wgetch_help(win, flags);
912         } else if (c == 'g') {
913             waddstr(win, "getstr test: ");
914             echo();
915             code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
916             noecho();
917             if (code == ERR) {
918                 wprintw(win, "wgetn_wstr returns an error.");
919             } else {
920                 int n;
921                 for (n = 0; (wchar_buf[n] = wint_buf[n]) != 0; ++n) ;
922                 if ((temp = wcstos(wchar_buf)) != 0) {
923                     wprintw(win, "I saw %d characters:\n\t`%s'.",
924                             wcslen(wchar_buf), temp);
925                     free(temp);
926                 } else {
927                     wprintw(win, "I saw %d characters (cannot convert).",
928                             wcslen(wchar_buf));
929                 }
930             }
931             wclrtoeol(win);
932             wgetch_wrap(win, first_y);
933         } else if (c == 'k') {
934             flags[UChar('k')] = !flags[UChar('k')];
935             setup_getch(win, flags);
936             wgetch_help(win, flags);
937         } else if (c == 'm') {
938             flags[UChar('m')] = !flags[UChar('m')];
939             setup_getch(win, flags);
940             wgetch_help(win, flags);
941         } else if (c == 's') {
942             ShellOut(TRUE);
943         } else if (c == 'w') {
944             int high = getmaxy(win) - 1 - first_y + 1;
945             int wide = getmaxx(win) - first_x;
946             int old_y, old_x;
947             int new_y = first_y + getbegy(win);
948             int new_x = first_x + getbegx(win);
949
950             getyx(win, old_y, old_x);
951             if (high > 2 && wide > 2) {
952                 WINDOW *wb = newwin(high, wide, new_y, new_x);
953                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
954
955                 box_set(wb, 0, 0);
956                 wrefresh(wb);
957                 wmove(wi, 0, 0);
958                 remember_boxes(level, wi, wb);
959                 wget_wch_test(level + 1, wi, delay);
960                 delwin(wi);
961                 delwin(wb);
962
963                 wgetch_help(win, flags);
964                 wmove(win, old_y, old_x);
965                 touchwin(win);
966                 wrefresh(win);
967             }
968 #ifdef SIGTSTP
969         } else if (c == 'z') {
970             kill(getpid(), SIGTSTP);
971 #endif
972         } else {
973             wprintw(win, "Key pressed: %04o ", c);
974 #ifdef NCURSES_MOUSE_VERSION
975             if (c == KEY_MOUSE) {
976                 MEVENT event;
977
978                 getmouse(&event);
979                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
980                 getyx(win, y, x);
981                 move(event.y, event.x);
982                 addch('*');
983                 wmove(win, y, x);
984             } else
985 #endif /* NCURSES_MOUSE_VERSION */
986             if (code == KEY_CODE_YES) {
987 #ifdef KEY_RESIZE
988                 if (c == KEY_RESIZE) {
989                     resize_wide_boxes(level, win);
990                 }
991 #endif
992                 (void) waddstr(win, key_name((wchar_t) c));
993             } else {
994                 if (c < 256 && iscntrl(c)) {
995                     (void) wprintw(win, "%s (control character)", unctrl(c));
996                 } else {
997                     wchar_t c2 = c;
998                     waddnwstr(win, &c2, 1);
999                     (void) wprintw(win, " = %#x (printable character)", c);
1000                 }
1001             }
1002             wgetch_wrap(win, first_y);
1003         }
1004     }
1005
1006     wtimeout(win, -1);
1007 }
1008
1009 static void
1010 get_wch_test(void)
1011 {
1012     int delay = begin_getch_test();
1013     wget_wch_test(0, stdscr, delay);
1014     finish_getch_test();
1015 }
1016 #endif
1017
1018 /****************************************************************************
1019  *
1020  * Character attributes test
1021  *
1022  ****************************************************************************/
1023
1024 #define MAX_ATTRSTRING 31
1025 #define LEN_ATTRSTRING 26
1026
1027 static char attr_test_string[MAX_ATTRSTRING + 1];
1028
1029 static void
1030 attr_legend(WINDOW *helpwin)
1031 {
1032     int row = 1;
1033     int col = 1;
1034
1035     mvwprintw(helpwin, row++, col,
1036               "q or ESC to exit.");
1037     mvwprintw(helpwin, row++, col,
1038               "^L repaints.");
1039     ++row;
1040     mvwprintw(helpwin, row++, col,
1041               "Modify the test strings:");
1042     mvwprintw(helpwin, row++, col,
1043               "  A digit sets gaps on each side of displayed attributes");
1044     mvwprintw(helpwin, row++, col,
1045               "  </> shifts the text left/right. ");
1046     ++row;
1047     mvwprintw(helpwin, row++, col,
1048               "Toggles:");
1049     if (has_colors()) {
1050         mvwprintw(helpwin, row++, col,
1051                   "  f/F/b/F toggle foreground/background background color");
1052         mvwprintw(helpwin, row++, col,
1053                   "  t/T     toggle text/background color attribute");
1054     }
1055     mvwprintw(helpwin, row++, col,
1056               "  a/A     toggle ACS (alternate character set) mapping");
1057     mvwprintw(helpwin, row++, col,
1058               "  v/V     toggle video attribute to combine with each line");
1059 }
1060
1061 static void
1062 show_color_attr(int fg, int bg, int tx)
1063 {
1064     if (has_colors()) {
1065         printw("  Colors (fg %d, bg %d", fg, bg);
1066         if (tx >= 0)
1067             printw(", text %d", tx);
1068         printw("),");
1069     }
1070 }
1071
1072 static bool
1073 cycle_color_attr(int ch, int *fg, int *bg, int *tx)
1074 {
1075     bool error = FALSE;
1076
1077     if (has_colors()) {
1078         switch (ch) {
1079         case 'f':
1080             *fg = (*fg + 1);
1081             break;
1082         case 'F':
1083             *fg = (*fg - 1);
1084             break;
1085         case 'b':
1086             *bg = (*bg + 1);
1087             break;
1088         case 'B':
1089             *bg = (*bg - 1);
1090             break;
1091         case 't':
1092             *tx = (*tx + 1);
1093             break;
1094         case 'T':
1095             *tx = (*tx - 1);
1096             break;
1097         default:
1098             beep();
1099             error = TRUE;
1100             break;
1101         }
1102         if (*fg >= COLORS)
1103             *fg = min_colors;
1104         if (*fg < min_colors)
1105             *fg = COLORS - 1;
1106         if (*bg >= COLORS)
1107             *bg = min_colors;
1108         if (*bg < min_colors)
1109             *bg = COLORS - 1;
1110         if (*tx >= COLORS)
1111             *tx = -1;
1112         if (*tx < -1)
1113             *tx = COLORS - 1;
1114     } else {
1115         beep();
1116         error = TRUE;
1117     }
1118     return error;
1119 }
1120
1121 static void
1122 adjust_attr_string(int adjust)
1123 {
1124     int first = ((int) UChar(attr_test_string[0])) + adjust;
1125     int last = first + LEN_ATTRSTRING;
1126
1127     if (first >= ' ' && last <= '~') {  /* 32..126 */
1128         int j, k;
1129         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1130             attr_test_string[j] = k;
1131             if (((k + 1 - first) % 5) == 0) {
1132                 ++j;
1133                 if (j < MAX_ATTRSTRING)
1134                     attr_test_string[j] = ' ';
1135             }
1136         }
1137         while (j < MAX_ATTRSTRING)
1138             attr_test_string[j++] = ' ';
1139         attr_test_string[j] = '\0';
1140     } else {
1141         beep();
1142     }
1143 }
1144
1145 static void
1146 init_attr_string(void)
1147 {
1148     attr_test_string[0] = 'a';
1149     adjust_attr_string(0);
1150 }
1151
1152 static int
1153 show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
1154 {
1155     int ncv = tigetnum("ncv");
1156     chtype test = attr & (chtype) (~A_ALTCHARSET);
1157
1158     if (arrow)
1159         mvprintw(row, 5, "-->");
1160     mvprintw(row, 8, "%s mode:", name);
1161     mvprintw(row, 24, "|");
1162     if (skip)
1163         printw("%*s", skip, " ");
1164     /*
1165      * Just for testing, write text using the alternate character set one
1166      * character at a time (to pass its rendition directly), and use the
1167      * string operation for the other attributes.
1168      */
1169     if (attr & A_ALTCHARSET) {
1170         const char *s;
1171         chtype ch;
1172
1173         for (s = attr_test_string; *s != '\0'; ++s) {
1174             ch = UChar(*s);
1175             addch(ch | attr);
1176         }
1177     } else {
1178         attrset(attr);
1179         addstr(attr_test_string);
1180         attroff(attr);
1181     }
1182     if (skip)
1183         printw("%*s", skip, " ");
1184     printw("|");
1185     if (test != A_NORMAL) {
1186         if (!(termattrs() & test)) {
1187             printw(" (N/A)");
1188         } else {
1189             if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
1190                 static const chtype table[] =
1191                 {
1192                     A_STANDOUT,
1193                     A_UNDERLINE,
1194                     A_REVERSE,
1195                     A_BLINK,
1196                     A_DIM,
1197                     A_BOLD,
1198                     A_INVIS,
1199                     A_PROTECT,
1200                     A_ALTCHARSET
1201                 };
1202                 unsigned n;
1203                 bool found = FALSE;
1204                 for (n = 0; n < SIZEOF(table); n++) {
1205                     if ((table[n] & attr) != 0
1206                         && ((1 << n) & ncv) != 0) {
1207                         found = TRUE;
1208                         break;
1209                     }
1210                 }
1211                 if (found)
1212                     printw(" (NCV)");
1213             }
1214             if ((termattrs() & test) != test)
1215                 printw(" (Part)");
1216         }
1217     }
1218     return row + 2;
1219 }
1220 /* *INDENT-OFF* */
1221 static const struct {
1222     attr_t                      attr;
1223     NCURSES_CONST char *        name;
1224 } attrs_to_test[] = {
1225     { A_STANDOUT,       "STANDOUT" },
1226     { A_REVERSE,        "REVERSE" },
1227     { A_BOLD,           "BOLD" },
1228     { A_UNDERLINE,      "UNDERLINE" },
1229     { A_DIM,            "DIM" },
1230     { A_BLINK,          "BLINK" },
1231     { A_PROTECT,        "PROTECT" },
1232     { A_INVIS,          "INVISIBLE" },
1233     { A_NORMAL,         "NORMAL" },
1234 };
1235 /* *INDENT-ON* */
1236
1237 static bool
1238 attr_getc(int *skip, int *fg, int *bg, int *tx, int *ac, unsigned *kc)
1239 {
1240     bool result = TRUE;
1241     bool error = FALSE;
1242     WINDOW *helpwin;
1243
1244     do {
1245         int ch = Getchar();
1246
1247         error = FALSE;
1248         if (ch < 256 && isdigit(ch)) {
1249             *skip = (ch - '0');
1250         } else {
1251             switch (ch) {
1252             case CTRL('L'):
1253                 Repaint();
1254                 break;
1255             case '?':
1256                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1257                     box(helpwin, 0, 0);
1258                     attr_legend(helpwin);
1259                     wGetchar(helpwin);
1260                     delwin(helpwin);
1261                 }
1262                 break;
1263             case 'a':
1264                 *ac = 0;
1265                 break;
1266             case 'A':
1267                 *ac = A_ALTCHARSET;
1268                 break;
1269             case 'v':
1270                 if (*kc == 0)
1271                     *kc = SIZEOF(attrs_to_test) - 1;
1272                 else
1273                     *kc -= 1;
1274                 break;
1275             case 'V':
1276                 *kc += 1;
1277                 if (*kc >= SIZEOF(attrs_to_test))
1278                     *kc = 0;
1279                 break;
1280             case '<':
1281                 adjust_attr_string(-1);
1282                 break;
1283             case '>':
1284                 adjust_attr_string(1);
1285                 break;
1286             case 'q':
1287             case ESCAPE:
1288                 result = FALSE;
1289                 break;
1290             default:
1291                 error = cycle_color_attr(ch, fg, bg, tx);
1292                 break;
1293             }
1294         }
1295     } while (error);
1296     return result;
1297 }
1298
1299 static void
1300 attr_test(void)
1301 /* test text attributes */
1302 {
1303     int n;
1304     int skip = tigetnum("xmc");
1305     int fg = COLOR_BLACK;       /* color pair 0 is special */
1306     int bg = COLOR_BLACK;
1307     int tx = -1;
1308     int ac = 0;
1309     unsigned j, k;
1310
1311     if (skip < 0)
1312         skip = 0;
1313
1314     n = skip;                   /* make it easy */
1315     k = SIZEOF(attrs_to_test) - 1;
1316     init_attr_string();
1317
1318     do {
1319         int row = 2;
1320         chtype normal = A_NORMAL | BLANK;
1321         chtype extras = ac;
1322
1323         if (has_colors()) {
1324             int pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1325             if (pair != 0) {
1326                 pair = 1;
1327                 if (init_pair(pair, fg, bg) == ERR) {
1328                     beep();
1329                 } else {
1330                     normal |= COLOR_PAIR(pair);
1331                 }
1332             }
1333             if (tx >= 0) {
1334                 pair = 2;
1335                 if (init_pair(pair, tx, bg) == ERR) {
1336                     beep();
1337                 } else {
1338                     extras |= COLOR_PAIR(pair);
1339                 }
1340             }
1341         }
1342         bkgd(normal);
1343         bkgdset(normal);
1344         erase();
1345
1346         box(stdscr, 0, 0);
1347         mvaddstr(0, 20, "Character attribute test display");
1348
1349         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1350             row = show_attr(row, n, j == k,
1351                             extras |
1352                             attrs_to_test[j].attr |
1353                             attrs_to_test[k].attr,
1354                             attrs_to_test[j].name);
1355         }
1356
1357         mvprintw(row, 8,
1358                  "This terminal does %shave the magic-cookie glitch",
1359                  tigetnum("xmc") > -1 ? "" : "not ");
1360         mvprintw(row + 1, 8, "Enter '?' for help.");
1361         show_color_attr(fg, bg, tx);
1362         printw("  ACS (%d)", ac != 0);
1363
1364         refresh();
1365     } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1366
1367     bkgdset(A_NORMAL | BLANK);
1368     erase();
1369     endwin();
1370 }
1371
1372 #if USE_WIDEC_SUPPORT
1373 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1374
1375 static void
1376 wide_adjust_attr_string(int adjust)
1377 {
1378     int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
1379     int last = first + LEN_ATTRSTRING;
1380
1381     if (first >= ' ' && last <= '~') {  /* 32..126 */
1382         int j, k;
1383         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1384             wide_attr_test_string[j] = k;
1385             if (((k + 1 - first) % 5) == 0) {
1386                 ++j;
1387                 if (j < MAX_ATTRSTRING)
1388                     wide_attr_test_string[j] = ' ';
1389             }
1390         }
1391         while (j < MAX_ATTRSTRING)
1392             wide_attr_test_string[j++] = ' ';
1393         wide_attr_test_string[j] = '\0';
1394     } else {
1395         beep();
1396     }
1397 }
1398
1399 static void
1400 wide_init_attr_string(void)
1401 {
1402     wide_attr_test_string[0] = 'a';
1403     wide_adjust_attr_string(0);
1404 }
1405
1406 static void
1407 set_wide_background(short pair)
1408 {
1409     cchar_t normal;
1410     wchar_t blank[2];
1411
1412     blank[0] = ' ';
1413     blank[1] = 0;
1414     setcchar(&normal, blank, A_NORMAL, pair, 0);
1415     bkgrnd(&normal);
1416     bkgrndset(&normal);
1417 }
1418
1419 static attr_t
1420 get_wide_background(void)
1421 {
1422     attr_t result = A_NORMAL;
1423     attr_t attr;
1424     cchar_t ch;
1425     short pair;
1426     wchar_t wch;
1427
1428     if (getbkgrnd(&ch) != ERR) {
1429         if (getcchar(&ch, &wch, &attr, &pair, 0) != ERR) {
1430             result = attr;
1431         }
1432     }
1433     return result;
1434 }
1435
1436 static int
1437 wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
1438 {
1439     int ncv = tigetnum("ncv");
1440     chtype test = attr & ~WA_ALTCHARSET;
1441
1442     if (arrow)
1443         mvprintw(row, 5, "-->");
1444     mvprintw(row, 8, "%s mode:", name);
1445     mvprintw(row, 24, "|");
1446     if (skip)
1447         printw("%*s", skip, " ");
1448
1449     /*
1450      * Just for testing, write text using the alternate character set one
1451      * character at a time (to pass its rendition directly), and use the
1452      * string operation for the other attributes.
1453      */
1454     if (attr & WA_ALTCHARSET) {
1455         const wchar_t *s;
1456         cchar_t ch;
1457
1458         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1459             wchar_t fill[2];
1460             fill[0] = *s;
1461             fill[1] = L'\0';
1462             setcchar(&ch, fill, attr, pair, 0);
1463             add_wch(&ch);
1464         }
1465     } else {
1466         attr_t old_attr;
1467         short old_pair;
1468
1469         attr_get(&old_attr, &old_pair, 0);
1470         attr_set(attr, pair, 0);
1471         addwstr(wide_attr_test_string);
1472         attr_set(old_attr, old_pair, 0);
1473     }
1474     if (skip)
1475         printw("%*s", skip, " ");
1476     printw("|");
1477     if (test != A_NORMAL) {
1478         if (!(term_attrs() & test)) {
1479             printw(" (N/A)");
1480         } else {
1481             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1482                 static const attr_t table[] =
1483                 {
1484                     WA_STANDOUT,
1485                     WA_UNDERLINE,
1486                     WA_REVERSE,
1487                     WA_BLINK,
1488                     WA_DIM,
1489                     WA_BOLD,
1490                     WA_INVIS,
1491                     WA_PROTECT,
1492                     WA_ALTCHARSET
1493                 };
1494                 unsigned n;
1495                 bool found = FALSE;
1496                 for (n = 0; n < SIZEOF(table); n++) {
1497                     if ((table[n] & attr) != 0
1498                         && ((1 << n) & ncv) != 0) {
1499                         found = TRUE;
1500                         break;
1501                     }
1502                 }
1503                 if (found)
1504                     printw(" (NCV)");
1505             }
1506             if ((term_attrs() & test) != test)
1507                 printw(" (Part)");
1508         }
1509     }
1510     return row + 2;
1511 }
1512
1513 static bool
1514 wide_attr_getc(int *skip, int *fg, int *bg, int *tx, int *ac, unsigned *kc)
1515 {
1516     bool result = TRUE;
1517     bool error = FALSE;
1518     WINDOW *helpwin;
1519
1520     do {
1521         int ch = Getchar();
1522
1523         error = FALSE;
1524         if (ch < 256 && isdigit(ch)) {
1525             *skip = (ch - '0');
1526         } else {
1527             switch (ch) {
1528             case CTRL('L'):
1529                 Repaint();
1530                 break;
1531             case '?':
1532                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1533                     box_set(helpwin, 0, 0);
1534                     attr_legend(helpwin);
1535                     wGetchar(helpwin);
1536                     delwin(helpwin);
1537                 }
1538                 break;
1539             case 'a':
1540                 *ac = 0;
1541                 break;
1542             case 'A':
1543                 *ac = A_ALTCHARSET;
1544                 break;
1545             case 'v':
1546                 if (*kc == 0)
1547                     *kc = SIZEOF(attrs_to_test) - 1;
1548                 else
1549                     *kc -= 1;
1550                 break;
1551             case 'V':
1552                 *kc += 1;
1553                 if (*kc >= SIZEOF(attrs_to_test))
1554                     *kc = 0;
1555                 break;
1556             case '<':
1557                 wide_adjust_attr_string(-1);
1558                 break;
1559             case '>':
1560                 wide_adjust_attr_string(1);
1561                 break;
1562             case 'q':
1563             case ESCAPE:
1564                 result = FALSE;
1565                 break;
1566             default:
1567                 error = cycle_color_attr(ch, fg, bg, tx);
1568                 break;
1569             }
1570         }
1571     } while (error);
1572     return result;
1573 }
1574
1575 static void
1576 wide_attr_test(void)
1577 /* test text attributes using wide-character calls */
1578 {
1579     int n;
1580     int skip = tigetnum("xmc");
1581     int fg = COLOR_BLACK;       /* color pair 0 is special */
1582     int bg = COLOR_BLACK;
1583     int tx = -1;
1584     int ac = 0;
1585     unsigned j, k;
1586
1587     if (skip < 0)
1588         skip = 0;
1589
1590     n = skip;                   /* make it easy */
1591     k = SIZEOF(attrs_to_test) - 1;
1592     wide_init_attr_string();
1593
1594     do {
1595         int row = 2;
1596         short pair = 0;
1597         short extras = 0;
1598
1599         if (has_colors()) {
1600             pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1601             if (pair != 0) {
1602                 pair = 1;
1603                 if (init_pair(pair, fg, bg) == ERR) {
1604                     beep();
1605                 }
1606             }
1607             extras = pair;
1608             if (tx >= 0) {
1609                 extras = 2;
1610                 if (init_pair(extras, tx, bg) == ERR) {
1611                     beep();
1612                 }
1613             }
1614         }
1615         set_wide_background(pair);
1616         erase();
1617
1618         box_set(stdscr, 0, 0);
1619         mvaddstr(0, 20, "Character attribute test display");
1620
1621         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1622             row = wide_show_attr(row, n, j == k,
1623                                  ac |
1624                                  attrs_to_test[j].attr |
1625                                  attrs_to_test[k].attr,
1626                                  extras,
1627                                  attrs_to_test[j].name);
1628         }
1629
1630         mvprintw(row, 8,
1631                  "This terminal does %shave the magic-cookie glitch",
1632                  tigetnum("xmc") > -1 ? "" : "not ");
1633         mvprintw(row + 1, 8, "Enter '?' for help.");
1634         show_color_attr(fg, bg, tx);
1635         printw("  ACS (%d)", ac != 0);
1636
1637         refresh();
1638     } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1639
1640     set_wide_background(0);
1641     erase();
1642     endwin();
1643 }
1644 #endif
1645
1646 /****************************************************************************
1647  *
1648  * Color support tests
1649  *
1650  ****************************************************************************/
1651
1652 static NCURSES_CONST char *the_color_names[] =
1653 {
1654     "black",
1655     "red",
1656     "green",
1657     "yellow",
1658     "blue",
1659     "magenta",
1660     "cyan",
1661     "white",
1662     "BLACK",
1663     "RED",
1664     "GREEN",
1665     "YELLOW",
1666     "BLUE",
1667     "MAGENTA",
1668     "CYAN",
1669     "WHITE"
1670 };
1671
1672 static void
1673 show_color_name(int y, int x, int color, bool wide)
1674 {
1675     if (move(y, x) != ERR) {
1676         char temp[80];
1677         int width = 8;
1678
1679         if (wide) {
1680             sprintf(temp, "%02d", color);
1681             width = 4;
1682         } else if (color >= 8) {
1683             sprintf(temp, "[%02d]", color);
1684         } else {
1685             strcpy(temp, the_color_names[color]);
1686         }
1687         printw("%-*.*s", width, width, temp);
1688     }
1689 }
1690
1691 static void
1692 color_legend(WINDOW *helpwin)
1693 {
1694     int row = 1;
1695     int col = 1;
1696
1697     mvwprintw(helpwin, row++, col,
1698               "q or ESC to exit.");
1699     ++row;
1700     mvwprintw(helpwin, row++, col,
1701               "Use up/down arrow to scroll through the display if it is");
1702     mvwprintw(helpwin, row++, col,
1703               "longer than one screen. Control/N and Control/P can be used");
1704     mvwprintw(helpwin, row++, col,
1705               "in place up up/down arrow.  Use pageup/pagedown to scroll a");
1706     mvwprintw(helpwin, row++, col,
1707               "full screen; control/B and control/F can be used here.");
1708     ++row;
1709     mvwprintw(helpwin, row++, col,
1710               "Toggles:");
1711     mvwprintw(helpwin, row++, col,
1712               "  b/B     toggle bold off/on");
1713     mvwprintw(helpwin, row++, col,
1714               "  n/N     toggle text/number on/off");
1715     mvwprintw(helpwin, row++, col,
1716               "  w/W     toggle width between 8/16 colors");
1717 }
1718
1719 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1720
1721 /* generate a color test pattern */
1722 static void
1723 color_test(void)
1724 {
1725     int c;
1726     int i;
1727     int top = 0, width;
1728     int base_row = 0;
1729     int grid_top = top + 3;
1730     int page_size = (LINES - grid_top);
1731     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1732     int row_limit;
1733     int per_row;
1734     char numbered[80];
1735     const char *hello;
1736     bool done = FALSE;
1737     bool opt_bold = FALSE;
1738     bool opt_wide = FALSE;
1739     bool opt_nums = FALSE;
1740     WINDOW *helpwin;
1741
1742     if (pairs_max > COLOR_PAIRS)
1743         pairs_max = COLOR_PAIRS;
1744
1745     while (!done) {
1746         int shown = 0;
1747
1748         /* this assumes an 80-column line */
1749         if (opt_wide) {
1750             width = 4;
1751             hello = "Test";
1752             per_row = (COLORS > 8) ? 16 : 8;
1753         } else {
1754             width = 8;
1755             hello = "Hello";
1756             per_row = 8;
1757         }
1758
1759         row_limit = (pairs_max + per_row - 1) / per_row;
1760
1761         move(0, 0);
1762         (void) printw("There are %d color pairs and %d colors\n",
1763                       pairs_max, COLORS);
1764
1765         clrtobot();
1766         (void) mvprintw(top + 1, 0,
1767                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1768                         row_limit,
1769                         per_row,
1770                         opt_bold ? "on" : "off");
1771
1772         /* show color names/numbers across the top */
1773         for (i = 0; i < per_row; i++)
1774             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1775
1776         /* show a grid of colors, with color names/ numbers on the left */
1777         for (i = (base_row * per_row); i < pairs_max; i++) {
1778             int row = grid_top + (i / per_row) - base_row;
1779             int col = (i % per_row + 1) * width;
1780             int pair = i;
1781
1782             if (row >= 0 && move(row, col) != ERR) {
1783                 init_pair(pair, i % COLORS, i / COLORS);
1784                 attron((attr_t) COLOR_PAIR(pair));
1785                 if (opt_bold)
1786                     attron((attr_t) A_BOLD);
1787
1788                 if (opt_nums) {
1789                     sprintf(numbered, "{%02X}", i);
1790                     hello = numbered;
1791                 }
1792                 printw("%-*.*s", width, width, hello);
1793                 attrset(A_NORMAL);
1794
1795                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
1796                     show_color_name(row, 0, i / COLORS, opt_wide);
1797                 }
1798                 ++shown;
1799             } else if (shown) {
1800                 break;
1801             }
1802         }
1803
1804         switch (c = wGetchar(stdscr)) {
1805         case 'b':
1806             opt_bold = FALSE;
1807             break;
1808         case 'B':
1809             opt_bold = TRUE;
1810             break;
1811         case 'n':
1812             opt_nums = FALSE;
1813             break;
1814         case 'N':
1815             opt_nums = TRUE;
1816             break;
1817         case ESCAPE:
1818         case 'q':
1819             done = TRUE;
1820             continue;
1821         case 'w':
1822             set_color_test(opt_wide, FALSE);
1823             break;
1824         case 'W':
1825             set_color_test(opt_wide, TRUE);
1826             break;
1827         case CTRL('p'):
1828         case KEY_UP:
1829             if (base_row <= 0) {
1830                 beep();
1831             } else {
1832                 base_row -= 1;
1833             }
1834             break;
1835         case CTRL('n'):
1836         case KEY_DOWN:
1837             if (base_row + page_size >= row_limit) {
1838                 beep();
1839             } else {
1840                 base_row += 1;
1841             }
1842             break;
1843         case CTRL('b'):
1844         case KEY_PREVIOUS:
1845         case KEY_PPAGE:
1846             if (base_row <= 0) {
1847                 beep();
1848             } else {
1849                 base_row -= (page_size - 1);
1850                 if (base_row < 0)
1851                     base_row = 0;
1852             }
1853             break;
1854         case CTRL('f'):
1855         case KEY_NEXT:
1856         case KEY_NPAGE:
1857             if (base_row + page_size >= row_limit) {
1858                 beep();
1859             } else {
1860                 base_row += page_size - 1;
1861                 if (base_row + page_size >= row_limit) {
1862                     base_row = row_limit - page_size - 1;
1863                 }
1864             }
1865             break;
1866         case '?':
1867             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1868                 box(helpwin, 0, 0);
1869                 color_legend(helpwin);
1870                 wGetchar(helpwin);
1871                 delwin(helpwin);
1872             }
1873             break;
1874         default:
1875             beep();
1876             continue;
1877         }
1878     }
1879
1880     erase();
1881     endwin();
1882 }
1883
1884 #if USE_WIDEC_SUPPORT
1885 /* generate a color test pattern */
1886 static void
1887 wide_color_test(void)
1888 {
1889     int c;
1890     int i;
1891     int top = 0, width;
1892     int base_row = 0;
1893     int grid_top = top + 3;
1894     int page_size = (LINES - grid_top);
1895     int pairs_max = COLOR_PAIRS;
1896     int row_limit;
1897     int per_row;
1898     char numbered[80];
1899     const char *hello;
1900     bool done = FALSE;
1901     bool opt_bold = FALSE;
1902     bool opt_wide = FALSE;
1903     bool opt_nums = FALSE;
1904     WINDOW *helpwin;
1905
1906     while (!done) {
1907         int shown = 0;
1908
1909         /* this assumes an 80-column line */
1910         if (opt_wide) {
1911             width = 4;
1912             hello = "Test";
1913             per_row = (COLORS > 8) ? 16 : 8;
1914         } else {
1915             width = 8;
1916             hello = "Hello";
1917             per_row = 8;
1918         }
1919
1920         row_limit = (pairs_max + per_row - 1) / per_row;
1921
1922         move(0, 0);
1923         (void) printw("There are %d color pairs and %d colors\n",
1924                       pairs_max, COLORS);
1925
1926         clrtobot();
1927         (void) mvprintw(top + 1, 0,
1928                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1929                         row_limit,
1930                         per_row,
1931                         opt_bold ? "on" : "off");
1932
1933         /* show color names/numbers across the top */
1934         for (i = 0; i < per_row; i++)
1935             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1936
1937         /* show a grid of colors, with color names/ numbers on the left */
1938         for (i = (base_row * per_row); i < pairs_max; i++) {
1939             int row = grid_top + (i / per_row) - base_row;
1940             int col = (i % per_row + 1) * width;
1941             int pair = i;
1942
1943             if (row >= 0 && move(row, col) != ERR) {
1944                 init_pair(pair, i % COLORS, i / COLORS);
1945                 color_set(pair, NULL);
1946                 if (opt_bold)
1947                     attr_on((attr_t) A_BOLD, NULL);
1948
1949                 if (opt_nums) {
1950                     sprintf(numbered, "{%02X}", i);
1951                     hello = numbered;
1952                 }
1953                 printw("%-*.*s", width, width, hello);
1954                 attr_set(A_NORMAL, 0, NULL);
1955
1956                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
1957                     show_color_name(row, 0, i / COLORS, opt_wide);
1958                 }
1959                 ++shown;
1960             } else if (shown) {
1961                 break;
1962             }
1963         }
1964
1965         switch (c = wGetchar(stdscr)) {
1966         case 'b':
1967             opt_bold = FALSE;
1968             break;
1969         case 'B':
1970             opt_bold = TRUE;
1971             break;
1972         case 'n':
1973             opt_nums = FALSE;
1974             break;
1975         case 'N':
1976             opt_nums = TRUE;
1977             break;
1978         case ESCAPE:
1979         case 'q':
1980             done = TRUE;
1981             continue;
1982         case 'w':
1983             set_color_test(opt_wide, FALSE);
1984             break;
1985         case 'W':
1986             set_color_test(opt_wide, TRUE);
1987             break;
1988         case CTRL('p'):
1989         case KEY_UP:
1990             if (base_row <= 0) {
1991                 beep();
1992             } else {
1993                 base_row -= 1;
1994             }
1995             break;
1996         case CTRL('n'):
1997         case KEY_DOWN:
1998             if (base_row + page_size >= row_limit) {
1999                 beep();
2000             } else {
2001                 base_row += 1;
2002             }
2003             break;
2004         case CTRL('b'):
2005         case KEY_PREVIOUS:
2006         case KEY_PPAGE:
2007             if (base_row <= 0) {
2008                 beep();
2009             } else {
2010                 base_row -= (page_size - 1);
2011                 if (base_row < 0)
2012                     base_row = 0;
2013             }
2014             break;
2015         case CTRL('f'):
2016         case KEY_NEXT:
2017         case KEY_NPAGE:
2018             if (base_row + page_size >= row_limit) {
2019                 beep();
2020             } else {
2021                 base_row += page_size - 1;
2022                 if (base_row + page_size >= row_limit) {
2023                     base_row = row_limit - page_size - 1;
2024                 }
2025             }
2026             break;
2027         case '?':
2028             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2029                 box(helpwin, 0, 0);
2030                 color_legend(helpwin);
2031                 wGetchar(helpwin);
2032                 delwin(helpwin);
2033             }
2034             break;
2035         default:
2036             beep();
2037             continue;
2038         }
2039     }
2040
2041     erase();
2042     endwin();
2043 }
2044 #endif /* USE_WIDEC_SUPPORT */
2045
2046 static void
2047 change_color(int current, int field, int value, int usebase)
2048 {
2049     short red, green, blue;
2050
2051     if (usebase)
2052         color_content(current, &red, &green, &blue);
2053     else
2054         red = green = blue = 0;
2055
2056     switch (field) {
2057     case 0:
2058         red += value;
2059         break;
2060     case 1:
2061         green += value;
2062         break;
2063     case 2:
2064         blue += value;
2065         break;
2066     }
2067
2068     if (init_color(current, red, green, blue) == ERR)
2069         beep();
2070 }
2071
2072 static void
2073 init_all_colors(void)
2074 {
2075     int c;
2076     for (c = 0; c < COLORS; ++c)
2077         init_color(c,
2078                    all_colors[c].red,
2079                    all_colors[c].green,
2080                    all_colors[c].blue);
2081 }
2082
2083 #define scaled_rgb(n) ((255 * (n)) / 1000)
2084
2085 static void
2086 color_edit(void)
2087 /* display the color test pattern, without trying to edit colors */
2088 {
2089     int i, this_c = 0, value = 0, current = 0, field = 0;
2090     int last_c;
2091     int top_color = 0;
2092     int page_size = (LINES - 6);
2093
2094     init_all_colors();
2095     refresh();
2096
2097     for (i = 0; i < max_colors; i++)
2098         init_pair(i, COLOR_WHITE, i);
2099
2100     mvprintw(LINES - 2, 0, "Number: %d", value);
2101
2102     do {
2103         short red, green, blue;
2104
2105         attron(A_BOLD);
2106         mvaddstr(0, 20, "Color RGB Value Editing");
2107         attroff(A_BOLD);
2108
2109         for (i = top_color;
2110              (i - top_color < page_size)
2111              && (i < max_colors); i++) {
2112             char numeric[80];
2113             sprintf(numeric, "[%d]", i);
2114             mvprintw(2 + i - top_color, 0, "%c %-8s:",
2115                      (i == current ? '>' : ' '),
2116                      (i < (int) SIZEOF(the_color_names)
2117                       ? the_color_names[i] : numeric));
2118             attrset(COLOR_PAIR(i));
2119             addstr("        ");
2120             attrset(A_NORMAL);
2121
2122             color_content(i, &red, &green, &blue);
2123             addstr("   R = ");
2124             if (current == i && field == 0)
2125                 attron(A_STANDOUT);
2126             printw("%04d", red);
2127             if (current == i && field == 0)
2128                 attrset(A_NORMAL);
2129             addstr(", G = ");
2130             if (current == i && field == 1)
2131                 attron(A_STANDOUT);
2132             printw("%04d", green);
2133             if (current == i && field == 1)
2134                 attrset(A_NORMAL);
2135             addstr(", B = ");
2136             if (current == i && field == 2)
2137                 attron(A_STANDOUT);
2138             printw("%04d", blue);
2139             if (current == i && field == 2)
2140                 attrset(A_NORMAL);
2141             attrset(A_NORMAL);
2142             printw(" ( %3d %3d %3d )",
2143                    scaled_rgb(red),
2144                    scaled_rgb(green),
2145                    scaled_rgb(blue));
2146         }
2147
2148         mvaddstr(LINES - 3, 0,
2149                  "Use up/down to select a color, left/right to change fields.");
2150         mvaddstr(LINES - 2, 0,
2151                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2152
2153         move(2 + current - top_color, 0);
2154
2155         last_c = this_c;
2156         this_c = Getchar();
2157         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2158             value = 0;
2159
2160         switch (this_c) {
2161         case CTRL('b'):
2162         case KEY_PPAGE:
2163             if (current > 0)
2164                 current -= (page_size - 1);
2165             else
2166                 beep();
2167             break;
2168
2169         case CTRL('f'):
2170         case KEY_NPAGE:
2171             if (current < (max_colors - 1))
2172                 current += (page_size - 1);
2173             else
2174                 beep();
2175             break;
2176
2177         case CTRL('p'):
2178         case KEY_UP:
2179             current = (current == 0 ? (max_colors - 1) : current - 1);
2180             break;
2181
2182         case CTRL('n'):
2183         case KEY_DOWN:
2184             current = (current == (max_colors - 1) ? 0 : current + 1);
2185             break;
2186
2187         case KEY_RIGHT:
2188             field = (field == 2 ? 0 : field + 1);
2189             break;
2190
2191         case KEY_LEFT:
2192             field = (field == 0 ? 2 : field - 1);
2193             break;
2194
2195         case '0':
2196         case '1':
2197         case '2':
2198         case '3':
2199         case '4':
2200         case '5':
2201         case '6':
2202         case '7':
2203         case '8':
2204         case '9':
2205             value = value * 10 + (this_c - '0');
2206             break;
2207
2208         case '+':
2209             change_color(current, field, value, 1);
2210             break;
2211
2212         case '-':
2213             change_color(current, field, -value, 1);
2214             break;
2215
2216         case '=':
2217             change_color(current, field, value, 0);
2218             break;
2219
2220         case '?':
2221             erase();
2222             P("                      RGB Value Editing Help");
2223             P("");
2224             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2225             P("the fields in one of the RGB triples of the current colors; the one");
2226             P("currently selected will be reverse-video highlighted.");
2227             P("");
2228             P("To change a field, enter the digits of the new value; they are echoed");
2229             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2230             P("To increment or decrement a value, use the same procedure, but finish");
2231             P("with a `+' or `-'.");
2232             P("");
2233             P("Press 'm' to invoke the top-level menu with the current color settings.");
2234             P("To quit, do `x' or 'q'");
2235
2236             Pause();
2237             erase();
2238             break;
2239
2240         case 'm':
2241             endwin();
2242             main_menu(FALSE);
2243             refresh();
2244             break;
2245
2246         case 'x':
2247         case 'q':
2248             break;
2249
2250         default:
2251             beep();
2252             break;
2253         }
2254
2255         if (current < 0)
2256             current = 0;
2257         if (current >= max_colors)
2258             current = max_colors - 1;
2259         if (current < top_color)
2260             top_color = current;
2261         if (current - top_color >= page_size)
2262             top_color = current - (page_size - 1);
2263
2264         mvprintw(LINES - 1, 0, "Number: %d", value);
2265         clrtoeol();
2266     } while
2267         (this_c != 'x' && this_c != 'q');
2268
2269     erase();
2270
2271     /*
2272      * ncurses does not reset each color individually when calling endwin().
2273      */
2274     init_all_colors();
2275
2276     endwin();
2277 }
2278
2279 /****************************************************************************
2280  *
2281  * Soft-key label test
2282  *
2283  ****************************************************************************/
2284
2285 #define SLK_HELP 17
2286 #define SLK_WORK (SLK_HELP + 3)
2287
2288 static void
2289 slk_help(void)
2290 {
2291     static const char *table[] =
2292     {
2293         "Available commands are:"
2294         ,""
2295         ,"^L         -- repaint this message and activate soft keys"
2296         ,"a/d        -- activate/disable soft keys"
2297         ,"c          -- set centered format for labels"
2298         ,"l          -- set left-justified format for labels"
2299         ,"r          -- set right-justified format for labels"
2300         ,"[12345678] -- set label; labels are numbered 1 through 8"
2301         ,"e          -- erase stdscr (should not erase labels)"
2302         ,"s          -- test scrolling of shortened screen"
2303 #if HAVE_SLK_COLOR
2304         ,"F/B        -- cycle through foreground/background colors"
2305 #endif
2306         ,"x, q       -- return to main menu"
2307         ,""
2308         ,"Note: if activating the soft keys causes your terminal to scroll up"
2309         ,"one line, your terminal auto-scrolls when anything is written to the"
2310         ,"last screen position.  The ncurses code does not yet handle this"
2311         ,"gracefully."
2312     };
2313     unsigned j;
2314
2315     move(2, 0);
2316     for (j = 0; j < SIZEOF(table); ++j) {
2317         P(table[j]);
2318     }
2319     refresh();
2320 }
2321
2322 static void
2323 slk_test(void)
2324 /* exercise the soft keys */
2325 {
2326     int c, fmt = 1;
2327     char buf[9];
2328     char *s;
2329 #if HAVE_SLK_COLOR
2330     short fg = COLOR_BLACK;
2331     short bg = COLOR_WHITE;
2332     bool new_color = FALSE;
2333 #endif
2334
2335     c = CTRL('l');
2336 #if HAVE_SLK_COLOR
2337     if (has_colors()) {
2338         new_color = TRUE;
2339     }
2340 #endif
2341
2342     do {
2343 #if HAVE_SLK_COLOR
2344         if (new_color) {
2345             init_pair(1, bg, fg);
2346             slk_color(1);
2347             new_color = FALSE;
2348             mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2349             refresh();
2350         }
2351 #endif
2352         move(0, 0);
2353         switch (c) {
2354         case CTRL('l'):
2355             erase();
2356             attron(A_BOLD);
2357             mvaddstr(0, 20, "Soft Key Exerciser");
2358             attroff(A_BOLD);
2359
2360             slk_help();
2361             /* fall through */
2362
2363         case 'a':
2364             slk_restore();
2365             break;
2366
2367         case 'e':
2368             wclear(stdscr);
2369             break;
2370
2371         case 's':
2372             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2373             while ((c = Getchar()) != 'Q' && (c != ERR))
2374                 addch((chtype) c);
2375             break;
2376
2377         case 'd':
2378             slk_clear();
2379             break;
2380
2381         case 'l':
2382             fmt = 0;
2383             break;
2384
2385         case 'c':
2386             fmt = 1;
2387             break;
2388
2389         case 'r':
2390             fmt = 2;
2391             break;
2392
2393         case '1':
2394         case '2':
2395         case '3':
2396         case '4':
2397         case '5':
2398         case '6':
2399         case '7':
2400         case '8':
2401             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2402             strcpy(buf, "");
2403             if ((s = slk_label(c - '0')) != 0) {
2404                 strncpy(buf, s, 8);
2405             }
2406             wGetstring(stdscr, buf, 8);
2407             slk_set((c - '0'), buf, fmt);
2408             slk_refresh();
2409             move(SLK_WORK, 0);
2410             clrtobot();
2411             break;
2412
2413         case 'x':
2414         case 'q':
2415             goto done;
2416
2417 #if HAVE_SLK_COLOR
2418         case 'F':
2419             if (has_colors()) {
2420                 fg = (fg + 1) % COLORS;
2421                 new_color = TRUE;
2422             }
2423             break;
2424         case 'B':
2425             if (has_colors()) {
2426                 bg = (bg + 1) % COLORS;
2427                 new_color = TRUE;
2428             }
2429             break;
2430 #endif
2431
2432         default:
2433             beep();
2434         }
2435     } while
2436         ((c = Getchar()) != EOF);
2437
2438   done:
2439     erase();
2440     endwin();
2441 }
2442
2443 #if USE_WIDEC_SUPPORT
2444 #define SLKLEN 8
2445 static void
2446 wide_slk_test(void)
2447 /* exercise the soft keys */
2448 {
2449     int c, fmt = 1;
2450     wchar_t buf[SLKLEN + 1];
2451     char *s;
2452     short fg = COLOR_BLACK;
2453     short bg = COLOR_WHITE;
2454     bool new_color = FALSE;
2455
2456     c = CTRL('l');
2457     if (has_colors()) {
2458         new_color = TRUE;
2459     }
2460     do {
2461         if (new_color) {
2462             init_pair(1, bg, fg);
2463             slk_color(1);
2464             new_color = FALSE;
2465             mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2466             refresh();
2467         }
2468         move(0, 0);
2469         switch (c) {
2470         case CTRL('l'):
2471             erase();
2472             attr_on(WA_BOLD, NULL);
2473             mvaddstr(0, 20, "Soft Key Exerciser");
2474             attr_off(WA_BOLD, NULL);
2475
2476             slk_help();
2477             /* fall through */
2478
2479         case 'a':
2480             slk_restore();
2481             break;
2482
2483         case 'e':
2484             wclear(stdscr);
2485             break;
2486
2487         case 's':
2488             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2489             while ((c = Getchar()) != 'Q' && (c != ERR))
2490                 addch((chtype) c);
2491             break;
2492
2493         case 'd':
2494             slk_clear();
2495             break;
2496
2497         case 'l':
2498             fmt = 0;
2499             break;
2500
2501         case 'c':
2502             fmt = 1;
2503             break;
2504
2505         case 'r':
2506             fmt = 2;
2507             break;
2508
2509         case '1':
2510         case '2':
2511         case '3':
2512         case '4':
2513         case '5':
2514         case '6':
2515         case '7':
2516         case '8':
2517             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2518             *buf = 0;
2519             if ((s = slk_label(c - '0')) != 0) {
2520                 char *temp = strdup(s);
2521                 size_t used = strlen(temp);
2522                 size_t want = SLKLEN;
2523                 size_t test;
2524                 mbstate_t state;
2525
2526                 buf[0] = L'\0';
2527                 while (want > 0 && used != 0) {
2528                     const char *base = s;
2529                     memset(&state, 0, sizeof(state));
2530                     test = mbsrtowcs(0, &base, 0, &state);
2531                     if (test == (size_t) -1) {
2532                         temp[--used] = 0;
2533                     } else if (test > want) {
2534                         temp[--used] = 0;
2535                     } else {
2536                         memset(&state, 0, sizeof(state));
2537                         mbsrtowcs(buf, &base, want, &state);
2538                         break;
2539                     }
2540                 }
2541                 free(temp);
2542             }
2543             wGet_wstring(stdscr, buf, SLKLEN);
2544             slk_wset((c - '0'), buf, fmt);
2545             slk_refresh();
2546             move(SLK_WORK, 0);
2547             clrtobot();
2548             break;
2549
2550         case 'x':
2551         case 'q':
2552             goto done;
2553
2554         case 'F':
2555             if (has_colors()) {
2556                 fg = (fg + 1) % COLORS;
2557                 new_color = TRUE;
2558             }
2559             break;
2560         case 'B':
2561             if (has_colors()) {
2562                 bg = (bg + 1) % COLORS;
2563                 new_color = TRUE;
2564             }
2565             break;
2566
2567         default:
2568             beep();
2569         }
2570     } while
2571         ((c = Getchar()) != EOF);
2572
2573   done:
2574     erase();
2575     endwin();
2576 }
2577 #endif
2578
2579 /****************************************************************************
2580  *
2581  * Alternate character-set stuff
2582  *
2583  ****************************************************************************/
2584
2585 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2586  * terminal to perform functions.  The remaining codes can be graphic.
2587  */
2588 static void
2589 show_upper_chars(unsigned first)
2590 {
2591     bool C1 = (first == 128);
2592     unsigned code;
2593     unsigned last = first + 31;
2594     int reply;
2595
2596     erase();
2597     attron(A_BOLD);
2598     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2599              C1 ? "C1" : "GR", first, last);
2600     attroff(A_BOLD);
2601     refresh();
2602
2603     for (code = first; code <= last; code++) {
2604         int row = 4 + ((code - first) % 16);
2605         int col = ((code - first) / 16) * COLS / 2;
2606         char tmp[80];
2607         sprintf(tmp, "%3u (0x%x)", code, code);
2608         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2609         if (C1)
2610             nodelay(stdscr, TRUE);
2611         echochar(code);
2612         if (C1) {
2613             /* (yes, this _is_ crude) */
2614             while ((reply = Getchar()) != ERR) {
2615                 addch(UChar(reply));
2616                 napms(10);
2617             }
2618             nodelay(stdscr, FALSE);
2619         }
2620     }
2621 }
2622
2623 static void
2624 show_box_chars(void)
2625 {
2626     erase();
2627     attron(A_BOLD);
2628     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
2629     attroff(A_BOLD);
2630     refresh();
2631     box(stdscr, 0, 0);
2632     /* *INDENT-OFF* */
2633     mvhline(LINES / 2, 0,        ACS_HLINE, COLS);
2634     mvvline(0,         COLS / 2, ACS_VLINE, LINES);
2635     mvaddch(0,         COLS / 2, ACS_TTEE);
2636     mvaddch(LINES / 2, COLS / 2, ACS_PLUS);
2637     mvaddch(LINES - 1, COLS / 2, ACS_BTEE);
2638     mvaddch(LINES / 2, 0,        ACS_LTEE);
2639     mvaddch(LINES / 2, COLS - 1, ACS_RTEE);
2640     /* *INDENT-ON* */
2641
2642 }
2643
2644 static int
2645 show_1_acs(int n, const char *name, chtype code)
2646 {
2647     const int height = 16;
2648     int row = 4 + (n % height);
2649     int col = (n / height) * COLS / 2;
2650     mvprintw(row, col, "%*s : ", COLS / 4, name);
2651     addch(code);
2652     return n + 1;
2653 }
2654
2655 static void
2656 show_acs_chars(void)
2657 /* display the ACS character set */
2658 {
2659     int n;
2660
2661 #define BOTH(name) #name, name
2662
2663     erase();
2664     attron(A_BOLD);
2665     mvaddstr(0, 20, "Display of the ACS Character Set");
2666     attroff(A_BOLD);
2667     refresh();
2668
2669     n = show_1_acs(0, BOTH(ACS_ULCORNER));
2670     n = show_1_acs(n, BOTH(ACS_URCORNER));
2671     n = show_1_acs(n, BOTH(ACS_LLCORNER));
2672     n = show_1_acs(n, BOTH(ACS_LRCORNER));
2673
2674     n = show_1_acs(n, BOTH(ACS_LTEE));
2675     n = show_1_acs(n, BOTH(ACS_RTEE));
2676     n = show_1_acs(n, BOTH(ACS_TTEE));
2677     n = show_1_acs(n, BOTH(ACS_BTEE));
2678
2679     n = show_1_acs(n, BOTH(ACS_HLINE));
2680     n = show_1_acs(n, BOTH(ACS_VLINE));
2681
2682     n = show_1_acs(n, BOTH(ACS_LARROW));
2683     n = show_1_acs(n, BOTH(ACS_RARROW));
2684     n = show_1_acs(n, BOTH(ACS_UARROW));
2685     n = show_1_acs(n, BOTH(ACS_DARROW));
2686
2687     n = show_1_acs(n, BOTH(ACS_BLOCK));
2688     n = show_1_acs(n, BOTH(ACS_BOARD));
2689     n = show_1_acs(n, BOTH(ACS_LANTERN));
2690     n = show_1_acs(n, BOTH(ACS_BULLET));
2691     n = show_1_acs(n, BOTH(ACS_CKBOARD));
2692     n = show_1_acs(n, BOTH(ACS_DEGREE));
2693     n = show_1_acs(n, BOTH(ACS_DIAMOND));
2694     n = show_1_acs(n, BOTH(ACS_PLMINUS));
2695     n = show_1_acs(n, BOTH(ACS_PLUS));
2696
2697     n = show_1_acs(n, BOTH(ACS_GEQUAL));
2698     n = show_1_acs(n, BOTH(ACS_NEQUAL));
2699     n = show_1_acs(n, BOTH(ACS_LEQUAL));
2700
2701     n = show_1_acs(n, BOTH(ACS_STERLING));
2702     n = show_1_acs(n, BOTH(ACS_PI));
2703     n = show_1_acs(n, BOTH(ACS_S1));
2704     n = show_1_acs(n, BOTH(ACS_S3));
2705     n = show_1_acs(n, BOTH(ACS_S7));
2706     n = show_1_acs(n, BOTH(ACS_S9));
2707 }
2708
2709 static void
2710 acs_display(void)
2711 {
2712     int c = 'a';
2713
2714     do {
2715         switch (c) {
2716         case CTRL('L'):
2717             Repaint();
2718             break;
2719         case 'a':
2720             show_acs_chars();
2721             break;
2722         case 'b':
2723             show_box_chars();
2724             break;
2725         case '0':
2726         case '1':
2727         case '2':
2728         case '3':
2729             show_upper_chars((unsigned) ((c - '0') * 32 + 128));
2730             break;
2731         }
2732         mvprintw(LINES - 3, 0,
2733                  "Note: ANSI terminals may not display C1 characters.");
2734         mvprintw(LINES - 2, 0,
2735                  "Select: a=ACS, b=box, 0=C1, 1,2,3=GR characters, q=quit");
2736         refresh();
2737     } while ((c = Getchar()) != 'x' && c != 'q');
2738
2739     Pause();
2740     erase();
2741     endwin();
2742 }
2743
2744 #if USE_WIDEC_SUPPORT
2745 static void
2746 show_upper_widechars(int first, int repeat, int space)
2747 {
2748     cchar_t temp;
2749     wchar_t code;
2750     int last = first + 31;
2751
2752     erase();
2753     attron(A_BOLD);
2754     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
2755     attroff(A_BOLD);
2756
2757     for (code = first; code <= last; code++) {
2758         int row = 4 + ((code - first) % 16);
2759         int col = ((code - first) / 16) * COLS / 2;
2760         wchar_t codes[10];
2761         attr_t attrs = A_NORMAL;
2762         char tmp[80];
2763         int count = repeat;
2764
2765         memset(&codes, 0, sizeof(codes));
2766         codes[0] = code;
2767         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
2768         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2769         setcchar(&temp, codes, attrs, 0, 0);
2770         do {
2771             /*
2772              * Give non-spacing characters something to combine with.  If we
2773              * don't, they'll bunch up in a heap on the space after the ":".
2774              * Mark them with reverse-video to make them simpler to find on
2775              * the display.
2776              */
2777             if (wcwidth(code) == 0)
2778                 addch(space | A_REVERSE);
2779             /*
2780              * This could use add_wch(), but is done for comparison with the
2781              * normal 'f' test (and to make a test-case for echo_wchar()).
2782              * The screen will flicker because the erase() at the top of the
2783              * function is met by the builtin refresh() in echo_wchar().
2784              */
2785             echo_wchar(&temp);
2786         } while (--count > 0);
2787     }
2788 }
2789
2790 static int
2791 show_1_wacs(int n, const char *name, const cchar_t *code)
2792 {
2793     const int height = 16;
2794     int row = 4 + (n % height);
2795     int col = (n / height) * COLS / 2;
2796     mvprintw(row, col, "%*s : ", COLS / 4, name);
2797     add_wchnstr(code, 1);
2798     return n + 1;
2799 }
2800
2801 static void
2802 show_wacs_chars(void)
2803 /* display the wide-ACS character set */
2804 {
2805     int n;
2806
2807 /*#define BOTH2(name) #name, &(name) */
2808 #define BOTH2(name) #name, name
2809
2810     erase();
2811     attron(A_BOLD);
2812     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
2813     attroff(A_BOLD);
2814     refresh();
2815
2816     n = show_1_wacs(0, BOTH2(WACS_ULCORNER));
2817     n = show_1_wacs(n, BOTH2(WACS_URCORNER));
2818     n = show_1_wacs(n, BOTH2(WACS_LLCORNER));
2819     n = show_1_wacs(n, BOTH2(WACS_LRCORNER));
2820
2821     n = show_1_wacs(n, BOTH2(WACS_LTEE));
2822     n = show_1_wacs(n, BOTH2(WACS_RTEE));
2823     n = show_1_wacs(n, BOTH2(WACS_TTEE));
2824     n = show_1_wacs(n, BOTH2(WACS_BTEE));
2825
2826     n = show_1_wacs(n, BOTH2(WACS_HLINE));
2827     n = show_1_wacs(n, BOTH2(WACS_VLINE));
2828
2829     n = show_1_wacs(n, BOTH2(WACS_LARROW));
2830     n = show_1_wacs(n, BOTH2(WACS_RARROW));
2831     n = show_1_wacs(n, BOTH2(WACS_UARROW));
2832     n = show_1_wacs(n, BOTH2(WACS_DARROW));
2833
2834     n = show_1_wacs(n, BOTH2(WACS_BLOCK));
2835     n = show_1_wacs(n, BOTH2(WACS_BOARD));
2836     n = show_1_wacs(n, BOTH2(WACS_LANTERN));
2837     n = show_1_wacs(n, BOTH2(WACS_BULLET));
2838     n = show_1_wacs(n, BOTH2(WACS_CKBOARD));
2839     n = show_1_wacs(n, BOTH2(WACS_DEGREE));
2840     n = show_1_wacs(n, BOTH2(WACS_DIAMOND));
2841     n = show_1_wacs(n, BOTH2(WACS_PLMINUS));
2842     n = show_1_wacs(n, BOTH2(WACS_PLUS));
2843
2844 #ifdef CURSES_WACS_ARRAY
2845     n = show_1_wacs(n, BOTH2(WACS_GEQUAL));
2846     n = show_1_wacs(n, BOTH2(WACS_NEQUAL));
2847     n = show_1_wacs(n, BOTH2(WACS_LEQUAL));
2848
2849     n = show_1_wacs(n, BOTH2(WACS_STERLING));
2850     n = show_1_wacs(n, BOTH2(WACS_PI));
2851     n = show_1_wacs(n, BOTH2(WACS_S1));
2852     n = show_1_wacs(n, BOTH2(WACS_S3));
2853     n = show_1_wacs(n, BOTH2(WACS_S7));
2854     n = show_1_wacs(n, BOTH2(WACS_S9));
2855 #endif
2856 }
2857
2858 static void
2859 show_wbox_chars(void)
2860 {
2861     erase();
2862     attron(A_BOLD);
2863     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
2864     attroff(A_BOLD);
2865     refresh();
2866     box_set(stdscr, 0, 0);
2867     /* *INDENT-OFF* */
2868     mvhline_set(LINES / 2, 0,        WACS_HLINE, COLS);
2869     mvvline_set(0,         COLS / 2, WACS_VLINE, LINES);
2870     mvadd_wch(0,           COLS / 2, WACS_TTEE);
2871     mvadd_wch(LINES / 2,   COLS / 2, WACS_PLUS);
2872     mvadd_wch(LINES - 1,   COLS / 2, WACS_BTEE);
2873     mvadd_wch(LINES / 2,   0,        WACS_LTEE);
2874     mvadd_wch(LINES / 2,   COLS - 1, WACS_RTEE);
2875     /* *INDENT-ON* */
2876
2877 }
2878
2879 static int
2880 show_2_wacs(int n, const char *name, const char *code)
2881 {
2882     const int height = 16;
2883     int row = 4 + (n % height);
2884     int col = (n / height) * COLS / 2;
2885     char temp[80];
2886
2887     mvprintw(row, col, "%*s : ", COLS / 4, name);
2888     addstr(strcpy(temp, code));
2889     return n + 1;
2890 }
2891
2892 static void
2893 show_utf8_chars(void)
2894 /* display the wide-ACS character set */
2895 {
2896     int n;
2897
2898     erase();
2899     attron(A_BOLD);
2900     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
2901     attroff(A_BOLD);
2902     refresh();
2903     /* *INDENT-OFF* */
2904     n = show_2_wacs(0, "WACS_ULCORNER", "\342\224\214");
2905     n = show_2_wacs(n, "WACS_URCORNER", "\342\224\220");
2906     n = show_2_wacs(n, "WACS_LLCORNER", "\342\224\224");
2907     n = show_2_wacs(n, "WACS_LRCORNER", "\342\224\230");
2908
2909     n = show_2_wacs(n, "WACS_LTEE",     "\342\224\234");
2910     n = show_2_wacs(n, "WACS_RTEE",     "\342\224\244");
2911     n = show_2_wacs(n, "WACS_TTEE",     "\342\224\254");
2912     n = show_2_wacs(n, "WACS_BTEE",     "\342\224\264");
2913
2914     n = show_2_wacs(n, "WACS_HLINE",    "\342\224\200");
2915     n = show_2_wacs(n, "WACS_VLINE",    "\342\224\202");
2916
2917     n = show_2_wacs(n, "WACS_LARROW",   "\342\206\220");
2918     n = show_2_wacs(n, "WACS_RARROW",   "\342\206\222");
2919     n = show_2_wacs(n, "WACS_UARROW",   "\342\206\221");
2920     n = show_2_wacs(n, "WACS_DARROW",   "\342\206\223");
2921
2922     n = show_2_wacs(n, "WACS_BLOCK",    "\342\226\256");
2923     n = show_2_wacs(n, "WACS_BOARD",    "\342\226\222");
2924     n = show_2_wacs(n, "WACS_LANTERN",  "\342\230\203");
2925     n = show_2_wacs(n, "WACS_BULLET",   "\302\267");
2926     n = show_2_wacs(n, "WACS_CKBOARD",  "\342\226\222");
2927     n = show_2_wacs(n, "WACS_DEGREE",   "\302\260");
2928     n = show_2_wacs(n, "WACS_DIAMOND",  "\342\227\206");
2929     n = show_2_wacs(n, "WACS_PLMINUS",  "\302\261");
2930     n = show_2_wacs(n, "WACS_PLUS",     "\342\224\274");
2931     n = show_2_wacs(n, "WACS_GEQUAL",   "\342\211\245");
2932     n = show_2_wacs(n, "WACS_NEQUAL",   "\342\211\240");
2933     n = show_2_wacs(n, "WACS_LEQUAL",   "\342\211\244");
2934
2935     n = show_2_wacs(n, "WACS_STERLING", "\302\243");
2936     n = show_2_wacs(n, "WACS_PI",       "\317\200");
2937     n = show_2_wacs(n, "WACS_S1",       "\342\216\272");
2938     n = show_2_wacs(n, "WACS_S3",       "\342\216\273");
2939     n = show_2_wacs(n, "WACS_S7",       "\342\216\274");
2940     n = show_2_wacs(n, "WACS_S9",       "\342\216\275");
2941     /* *INDENT-ON* */
2942
2943 }
2944
2945 static void
2946 wide_acs_display(void)
2947 {
2948     int c = 'a';
2949     int digit = 0;
2950     int repeat = 0;
2951     int space = ' ';
2952
2953     do {
2954         switch (c) {
2955         case CTRL('L'):
2956             Repaint();
2957             break;
2958         case 'a':
2959             show_wacs_chars();
2960             break;
2961         case 'b':
2962             show_wbox_chars();
2963             break;
2964         case 'u':
2965             show_utf8_chars();
2966             break;
2967         default:
2968             if (c < 256 && isdigit(c))
2969                 digit = (c - '0');
2970             else if (c == '+')
2971                 ++digit;
2972             else if (c == '-' && digit > 0)
2973                 --digit;
2974             else if (c == '>')
2975                 ++repeat;
2976             else if (c == '<' && repeat > 0)
2977                 --repeat;
2978             else if (c == '_')
2979                 space = (space == ' ') ? '_' : ' ';
2980             else {
2981                 beep();
2982                 break;
2983             }
2984             show_upper_widechars(digit * 32 + 128, repeat, space);
2985             break;
2986         }
2987         mvprintw(LINES - 2, 0,
2988                  "Select: a WACS, b box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, q=quit");
2989         refresh();
2990     } while ((c = Getchar()) != 'x' && c != 'q');
2991
2992     Pause();
2993     erase();
2994     endwin();
2995 }
2996
2997 #endif
2998
2999 /*
3000  * Graphic-rendition test (adapted from vttest)
3001  */
3002 static void
3003 test_sgr_attributes(void)
3004 {
3005     int pass;
3006
3007     for (pass = 0; pass < 2; pass++) {
3008         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3009
3010         /* Use non-default colors if possible to exercise bce a little */
3011         if (has_colors()) {
3012             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3013             normal |= COLOR_PAIR(1);
3014         }
3015         bkgdset(normal);
3016         erase();
3017         mvprintw(1, 20, "Graphic rendition test pattern:");
3018
3019         mvprintw(4, 1, "vanilla");
3020
3021 #define set_sgr(mask) bkgdset((normal^(mask)));
3022         set_sgr(A_BOLD);
3023         mvprintw(4, 40, "bold");
3024
3025         set_sgr(A_UNDERLINE);
3026         mvprintw(6, 6, "underline");
3027
3028         set_sgr(A_BOLD | A_UNDERLINE);
3029         mvprintw(6, 45, "bold underline");
3030
3031         set_sgr(A_BLINK);
3032         mvprintw(8, 1, "blink");
3033
3034         set_sgr(A_BLINK | A_BOLD);
3035         mvprintw(8, 40, "bold blink");
3036
3037         set_sgr(A_UNDERLINE | A_BLINK);
3038         mvprintw(10, 6, "underline blink");
3039
3040         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3041         mvprintw(10, 45, "bold underline blink");
3042
3043         set_sgr(A_REVERSE);
3044         mvprintw(12, 1, "negative");
3045
3046         set_sgr(A_BOLD | A_REVERSE);
3047         mvprintw(12, 40, "bold negative");
3048
3049         set_sgr(A_UNDERLINE | A_REVERSE);
3050         mvprintw(14, 6, "underline negative");
3051
3052         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3053         mvprintw(14, 45, "bold underline negative");
3054
3055         set_sgr(A_BLINK | A_REVERSE);
3056         mvprintw(16, 1, "blink negative");
3057
3058         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3059         mvprintw(16, 40, "bold blink negative");
3060
3061         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3062         mvprintw(18, 6, "underline blink negative");
3063
3064         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3065         mvprintw(18, 45, "bold underline blink negative");
3066
3067         bkgdset(normal);
3068         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3069                  "Light");
3070         clrtoeol();
3071         Pause();
3072     }
3073
3074     bkgdset(A_NORMAL | BLANK);
3075     erase();
3076     endwin();
3077 }
3078
3079 /****************************************************************************
3080  *
3081  * Windows and scrolling tester.
3082  *
3083  ****************************************************************************/
3084
3085 #define BOTLINES        4       /* number of line stolen from screen bottom */
3086
3087 typedef struct {
3088     int y, x;
3089 } pair;
3090
3091 #define FRAME struct frame
3092 FRAME
3093 {
3094     FRAME *next, *last;
3095     bool do_scroll;
3096     bool do_keypad;
3097     WINDOW *wind;
3098 };
3099
3100 #ifdef NCURSES_VERSION
3101 #define keypad_active(win) (win)->_use_keypad
3102 #define scroll_active(win) (win)->_scroll
3103 #else
3104 #define keypad_active(win) FALSE
3105 #define scroll_active(win) FALSE
3106 #endif
3107
3108 /* We need to know if these flags are actually set, so don't look in FRAME.
3109  * These names are known to work with SVr4 curses as well as ncurses.  The
3110  * _use_keypad name does not work with Solaris 8.
3111  */
3112 static bool
3113 HaveKeypad(FRAME * curp)
3114 {
3115     WINDOW *win = (curp ? curp->wind : stdscr);
3116     return keypad_active(win);
3117 }
3118
3119 static bool
3120 HaveScroll(FRAME * curp)
3121 {
3122     WINDOW *win = (curp ? curp->wind : stdscr);
3123     return scroll_active(win);
3124 }
3125
3126 static void
3127 newwin_legend(FRAME * curp)
3128 {
3129     static const struct {
3130         const char *msg;
3131         int code;
3132     } legend[] = {
3133         {
3134             "^C = create window", 0
3135         },
3136         {
3137             "^N = next window", 0
3138         },
3139         {
3140             "^P = previous window", 0
3141         },
3142         {
3143             "^F = scroll forward", 0
3144         },
3145         {
3146             "^B = scroll backward", 0
3147         },
3148         {
3149             "^K = keypad(%s)", 1
3150         },
3151         {
3152             "^S = scrollok(%s)", 2
3153         },
3154         {
3155             "^W = save window to file", 0
3156         },
3157         {
3158             "^R = restore window", 0
3159         },
3160 #if HAVE_WRESIZE
3161         {
3162             "^X = resize", 0
3163         },
3164 #endif
3165         {
3166             "^Q%s = exit", 3
3167         }
3168     };
3169     size_t n;
3170     int x;
3171     bool do_keypad = HaveKeypad(curp);
3172     bool do_scroll = HaveScroll(curp);
3173     char buf[BUFSIZ];
3174
3175     move(LINES - 4, 0);
3176     for (n = 0; n < SIZEOF(legend); n++) {
3177         switch (legend[n].code) {
3178         default:
3179             strcpy(buf, legend[n].msg);
3180             break;
3181         case 1:
3182             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3183             break;
3184         case 2:
3185             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3186             break;
3187         case 3:
3188             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3189             break;
3190         }
3191         x = getcurx(stdscr);
3192         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3193         addstr(buf);
3194     }
3195     clrtoeol();
3196 }
3197
3198 static void
3199 transient(FRAME * curp, NCURSES_CONST char *msg)
3200 {
3201     newwin_legend(curp);
3202     if (msg) {
3203         mvaddstr(LINES - 1, 0, msg);
3204         refresh();
3205         napms(1000);
3206     }
3207
3208     move(LINES - 1, 0);
3209     printw("%s characters are echoed, window should %sscroll.",
3210            HaveKeypad(curp) ? "Non-arrow" : "All other",
3211            HaveScroll(curp) ? "" : "not ");
3212     clrtoeol();
3213 }
3214
3215 static void
3216 newwin_report(FRAME * curp)
3217 /* report on the cursor's current position, then restore it */
3218 {
3219     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3220     int y, x;
3221
3222     if (win != stdscr)
3223         transient(curp, (char *) 0);
3224     getyx(win, y, x);
3225     move(LINES - 1, COLS - 17);
3226     printw("Y = %2d X = %2d", y, x);
3227     if (win != stdscr)
3228         refresh();
3229     else
3230         wmove(win, y, x);
3231 }
3232
3233 static pair *
3234 selectcell(int uli, int ulj, int lri, int lrj)
3235 /* arrows keys move cursor, return location at current on non-arrow key */
3236 {
3237     static pair res;            /* result cell */
3238     int si = lri - uli + 1;     /* depth of the select area */
3239     int sj = lrj - ulj + 1;     /* width of the select area */
3240     int i = 0, j = 0;           /* offsets into the select area */
3241
3242     res.y = uli;
3243     res.x = ulj;
3244     for (;;) {
3245         move(uli + i, ulj + j);
3246         newwin_report((FRAME *) 0);
3247
3248         switch (Getchar()) {
3249         case KEY_UP:
3250             i += si - 1;
3251             break;
3252         case KEY_DOWN:
3253             i++;
3254             break;
3255         case KEY_LEFT:
3256             j += sj - 1;
3257             break;
3258         case KEY_RIGHT:
3259             j++;
3260             break;
3261         case QUIT:
3262         case ESCAPE:
3263             return ((pair *) 0);
3264 #ifdef NCURSES_MOUSE_VERSION
3265         case KEY_MOUSE:
3266             {
3267                 MEVENT event;
3268
3269                 getmouse(&event);
3270                 if (event.y > uli && event.x > ulj) {
3271                     i = event.y - uli;
3272                     j = event.x - ulj;
3273                 } else {
3274                     beep();
3275                     break;
3276                 }
3277             }
3278             /* FALLTHRU */
3279 #endif
3280         default:
3281             res.y = uli + i;
3282             res.x = ulj + j;
3283             return (&res);
3284         }
3285         i %= si;
3286         j %= sj;
3287     }
3288 }
3289
3290 static void
3291 outerbox(pair ul, pair lr, bool onoff)
3292 /* draw or erase a box *outside* the given pair of corners */
3293 {
3294     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3295     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3296     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3297     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3298     move(ul.y - 1, ul.x);
3299     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3300     move(ul.y, ul.x - 1);
3301     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3302     move(lr.y + 1, ul.x);
3303     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3304     move(ul.y, lr.x + 1);
3305     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3306 }
3307
3308 static WINDOW *
3309 getwindow(void)
3310 /* Ask user for a window definition */
3311 {
3312     WINDOW *rwindow;
3313     pair ul, lr, *tmp;
3314
3315     move(0, 0);
3316     clrtoeol();
3317     addstr("Use arrows to move cursor, anything else to mark corner 1");
3318     refresh();
3319     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3320         return ((WINDOW *) 0);
3321     memcpy(&ul, tmp, sizeof(pair));
3322     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3323     move(0, 0);
3324     clrtoeol();
3325     addstr("Use arrows to move cursor, anything else to mark corner 2");
3326     refresh();
3327     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3328         (pair *) 0)
3329         return ((WINDOW *) 0);
3330     memcpy(&lr, tmp, sizeof(pair));
3331
3332     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3333
3334     outerbox(ul, lr, TRUE);
3335     refresh();
3336
3337     wrefresh(rwindow);
3338
3339     move(0, 0);
3340     clrtoeol();
3341     return (rwindow);
3342 }
3343
3344 static void
3345 newwin_move(FRAME * curp, int dy, int dx)
3346 {
3347     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3348     int cur_y, cur_x;
3349     int max_y, max_x;
3350
3351     getyx(win, cur_y, cur_x);
3352     getmaxyx(win, max_y, max_x);
3353     if ((cur_x += dx) < 0)
3354         cur_x = 0;
3355     else if (cur_x >= max_x)
3356         cur_x = max_x - 1;
3357     if ((cur_y += dy) < 0)
3358         cur_y = 0;
3359     else if (cur_y >= max_y)
3360         cur_y = max_y - 1;
3361     wmove(win, cur_y, cur_x);
3362 }
3363
3364 static FRAME *
3365 delete_framed(FRAME * fp, bool showit)
3366 {
3367     FRAME *np;
3368
3369     fp->last->next = fp->next;
3370     fp->next->last = fp->last;
3371
3372     if (showit) {
3373         werase(fp->wind);
3374         wrefresh(fp->wind);
3375     }
3376     delwin(fp->wind);
3377
3378     np = (fp == fp->next) ? 0 : fp->next;
3379     free(fp);
3380     return np;
3381 }
3382
3383 static void
3384 acs_and_scroll(void)
3385 /* Demonstrate windows */
3386 {
3387     int c, i;
3388     FILE *fp;
3389     FRAME *current = (FRAME *) 0, *neww;
3390     WINDOW *usescr = stdscr;
3391
3392 #define DUMPFILE        "screendump"
3393
3394 #ifdef NCURSES_MOUSE_VERSION
3395     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3396 #endif
3397     c = CTRL('C');
3398     raw();
3399     do {
3400         transient((FRAME *) 0, (char *) 0);
3401         switch (c) {
3402         case CTRL('C'):
3403             neww = (FRAME *) calloc(1, sizeof(FRAME));
3404             if ((neww->wind = getwindow()) == (WINDOW *) 0)
3405                 goto breakout;
3406
3407             if (current == 0) { /* First element,  */
3408                 neww->next = neww;      /*   so point it at itself */
3409                 neww->last = neww;
3410             } else {
3411                 neww->next = current->next;
3412                 neww->last = current;
3413                 neww->last->next = neww;
3414                 neww->next->last = neww;
3415             }
3416             current = neww;
3417             /* SVr4 curses sets the keypad on all newly-created windows to
3418              * false.  Someone reported that PDCurses makes new windows inherit
3419              * this flag.  Remove the following 'keypad()' call to test this
3420              */
3421             keypad(current->wind, TRUE);
3422             current->do_keypad = HaveKeypad(current);
3423             current->do_scroll = HaveScroll(current);
3424             break;
3425
3426         case CTRL('N'): /* go to next window */
3427             if (current)
3428                 current = current->next;
3429             break;
3430
3431         case CTRL('P'): /* go to previous window */
3432             if (current)
3433                 current = current->last;
3434             break;
3435
3436         case CTRL('F'): /* scroll current window forward */
3437             if (current)
3438                 wscrl(current->wind, 1);
3439             break;
3440
3441         case CTRL('B'): /* scroll current window backwards */
3442             if (current)
3443                 wscrl(current->wind, -1);
3444             break;
3445
3446         case CTRL('K'): /* toggle keypad mode for current */
3447             if (current) {
3448                 current->do_keypad = !current->do_keypad;
3449                 keypad(current->wind, current->do_keypad);
3450             }
3451             break;
3452
3453         case CTRL('S'):
3454             if (current) {
3455                 current->do_scroll = !current->do_scroll;
3456                 scrollok(current->wind, current->do_scroll);
3457             }
3458             break;
3459
3460         case CTRL('W'): /* save and delete window */
3461             if (current == current->next) {
3462                 transient(current, "Will not save/delete ONLY window");
3463                 break;
3464             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
3465                 transient(current, "Can't open screen dump file");
3466             } else {
3467                 (void) putwin(current->wind, fp);
3468                 (void) fclose(fp);
3469
3470                 current = delete_framed(current, TRUE);
3471             }
3472             break;
3473
3474         case CTRL('R'): /* restore window */
3475             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
3476                 transient(current, "Can't open screen dump file");
3477             } else {
3478                 neww = (FRAME *) calloc(1, sizeof(FRAME));
3479
3480                 neww->next = current->next;
3481                 neww->last = current;
3482                 neww->last->next = neww;
3483                 neww->next->last = neww;
3484
3485                 neww->wind = getwin(fp);
3486                 (void) fclose(fp);
3487
3488                 wrefresh(neww->wind);
3489             }
3490             break;
3491
3492 #if HAVE_WRESIZE
3493         case CTRL('X'): /* resize window */
3494             if (current) {
3495                 pair *tmp, ul, lr;
3496                 int mx, my;
3497
3498                 move(0, 0);
3499                 clrtoeol();
3500                 addstr("Use arrows to move cursor, anything else to mark new corner");
3501                 refresh();
3502
3503                 getbegyx(current->wind, ul.y, ul.x);
3504
3505                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
3506                 if (tmp == (pair *) 0) {
3507                     beep();
3508                     break;
3509                 }
3510
3511                 getmaxyx(current->wind, lr.y, lr.x);
3512                 lr.y += (ul.y - 1);
3513                 lr.x += (ul.x - 1);
3514                 outerbox(ul, lr, FALSE);
3515                 wnoutrefresh(stdscr);
3516
3517                 /* strictly cosmetic hack for the test */
3518                 getmaxyx(current->wind, my, mx);
3519                 if (my > tmp->y - ul.y) {
3520                     getyx(current->wind, lr.y, lr.x);
3521                     wmove(current->wind, tmp->y - ul.y + 1, 0);
3522                     wclrtobot(current->wind);
3523                     wmove(current->wind, lr.y, lr.x);
3524                 }
3525                 if (mx > tmp->x - ul.x)
3526                     for (i = 0; i < my; i++) {
3527                         wmove(current->wind, i, tmp->x - ul.x + 1);
3528                         wclrtoeol(current->wind);
3529                     }
3530                 wnoutrefresh(current->wind);
3531
3532                 memcpy(&lr, tmp, sizeof(pair));
3533                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
3534
3535                 getbegyx(current->wind, ul.y, ul.x);
3536                 getmaxyx(current->wind, lr.y, lr.x);
3537                 lr.y += (ul.y - 1);
3538                 lr.x += (ul.x - 1);
3539                 outerbox(ul, lr, TRUE);
3540                 wnoutrefresh(stdscr);
3541
3542                 wnoutrefresh(current->wind);
3543                 move(0, 0);
3544                 clrtoeol();
3545                 doupdate();
3546             }
3547             break;
3548 #endif /* HAVE_WRESIZE */
3549
3550         case KEY_F(10): /* undocumented --- use this to test area clears */
3551             selectcell(0, 0, LINES - 1, COLS - 1);
3552             clrtobot();
3553             refresh();
3554             break;
3555
3556         case KEY_UP:
3557             newwin_move(current, -1, 0);
3558             break;
3559         case KEY_DOWN:
3560             newwin_move(current, 1, 0);
3561             break;
3562         case KEY_LEFT:
3563             newwin_move(current, 0, -1);
3564             break;
3565         case KEY_RIGHT:
3566             newwin_move(current, 0, 1);
3567             break;
3568
3569         case KEY_BACKSPACE:
3570             /* FALLTHROUGH */
3571         case KEY_DC:
3572             {
3573                 int y, x;
3574                 getyx(current->wind, y, x);
3575                 if (--x < 0) {
3576                     if (--y < 0)
3577                         break;
3578                     x = getmaxx(current->wind) - 1;
3579                 }
3580                 mvwdelch(current->wind, y, x);
3581             }
3582             break;
3583
3584         case '\r':
3585             c = '\n';
3586             /* FALLTHROUGH */
3587
3588         default:
3589             if (current)
3590                 waddch(current->wind, (chtype) c);
3591             else
3592                 beep();
3593             break;
3594         }
3595         newwin_report(current);
3596         usescr = (current ? current->wind : stdscr);
3597         wrefresh(usescr);
3598     } while
3599         ((c = wGetchar(usescr)) != QUIT
3600          && !((c == ESCAPE) && (keypad_active(usescr)))
3601          && (c != ERR));
3602
3603   breakout:
3604     while (current != 0)
3605         current = delete_framed(current, FALSE);
3606
3607     scrollok(stdscr, TRUE);     /* reset to driver's default */
3608 #ifdef NCURSES_MOUSE_VERSION
3609     mousemask(0, (mmask_t *) 0);
3610 #endif
3611     noraw();
3612     erase();
3613     endwin();
3614 }
3615
3616 /****************************************************************************
3617  *
3618  * Panels tester
3619  *
3620  ****************************************************************************/
3621
3622 #if USE_LIBPANEL
3623 static int nap_msec = 1;
3624
3625 static NCURSES_CONST char *mod[] =
3626 {
3627     "test ",
3628     "TEST ",
3629     "(**) ",
3630     "*()* ",
3631     "<--> ",
3632     "LAST "
3633 };
3634
3635 /*+-------------------------------------------------------------------------
3636         wait_a_while(msec)
3637 --------------------------------------------------------------------------*/
3638 static void
3639 wait_a_while(int msec GCC_UNUSED)
3640 {
3641 #if HAVE_NAPMS
3642     if (nap_msec == 1)
3643         wGetchar(stdscr);
3644     else
3645         napms(nap_msec);
3646 #else
3647     if (nap_msec == 1)
3648         wGetchar(stdscr);
3649     else if (msec > 1000)
3650         sleep((unsigned) msec / 1000);
3651     else
3652         sleep(1);
3653 #endif
3654 }                               /* end of wait_a_while */
3655
3656 /*+-------------------------------------------------------------------------
3657         saywhat(text)
3658 --------------------------------------------------------------------------*/
3659 static void
3660 saywhat(NCURSES_CONST char *text)
3661 {
3662     wmove(stdscr, LINES - 1, 0);
3663     wclrtoeol(stdscr);
3664     waddstr(stdscr, text);
3665 }                               /* end of saywhat */
3666
3667 /*+-------------------------------------------------------------------------
3668         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
3669 --------------------------------------------------------------------------*/
3670 static PANEL *
3671 mkpanel(unsigned color, int rows, int cols, int tly, int tlx)
3672 {
3673     WINDOW *win;
3674     PANEL *pan = 0;
3675
3676     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
3677         if ((pan = new_panel(win)) == 0) {
3678             delwin(win);
3679         } else if (has_colors()) {
3680             int fg = (color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK;
3681             int bg = color;
3682             init_pair(color, fg, bg);
3683             wbkgdset(win, COLOR_PAIR(color) | ' ');
3684         } else {
3685             wbkgdset(win, A_BOLD | ' ');
3686         }
3687     }
3688     return pan;
3689 }                               /* end of mkpanel */
3690
3691 /*+-------------------------------------------------------------------------
3692         rmpanel(pan)
3693 --------------------------------------------------------------------------*/
3694 static void
3695 rmpanel(PANEL * pan)
3696 {
3697     WINDOW *win = panel_window(pan);
3698     del_panel(pan);
3699     delwin(win);
3700 }                               /* end of rmpanel */
3701
3702 /*+-------------------------------------------------------------------------
3703         pflush()
3704 --------------------------------------------------------------------------*/
3705 static void
3706 pflush(void)
3707 {
3708     update_panels();
3709     doupdate();
3710 }                               /* end of pflush */
3711
3712 /*+-------------------------------------------------------------------------
3713         fill_panel(win)
3714 --------------------------------------------------------------------------*/
3715 static void
3716 fill_panel(PANEL * pan)
3717 {
3718     WINDOW *win = panel_window(pan);
3719     int num = ((const char *) panel_userptr(pan))[1];
3720     int y, x;
3721
3722     wmove(win, 1, 1);
3723     wprintw(win, "-pan%c-", num);
3724     wclrtoeol(win);
3725     box(win, 0, 0);
3726     for (y = 2; y < getmaxy(win) - 1; y++) {
3727         for (x = 1; x < getmaxx(win) - 1; x++) {
3728             wmove(win, y, x);
3729             waddch(win, UChar(num));
3730         }
3731     }
3732 }                               /* end of fill_panel */
3733
3734 static void
3735 demo_panels(void)
3736 {
3737     int itmp;
3738     register int y, x;
3739
3740     refresh();
3741
3742     for (y = 0; y < LINES - 1; y++) {
3743         for (x = 0; x < COLS; x++)
3744             wprintw(stdscr, "%d", (y + x) % 10);
3745     }
3746     for (y = 0; y < 5; y++) {
3747         PANEL *p1;
3748         PANEL *p2;
3749         PANEL *p3;
3750         PANEL *p4;
3751         PANEL *p5;
3752
3753         p1 = mkpanel(COLOR_RED,
3754                      LINES / 2 - 2,
3755                      COLS / 8 + 1,
3756                      0,
3757                      0);
3758         set_panel_userptr(p1, (NCURSES_CONST void *) "p1");
3759
3760         p2 = mkpanel(COLOR_GREEN,
3761                      LINES / 2 + 1,
3762                      COLS / 7,
3763                      LINES / 4,
3764                      COLS / 10);
3765         set_panel_userptr(p2, (NCURSES_CONST void *) "p2");
3766
3767         p3 = mkpanel(COLOR_YELLOW,
3768                      LINES / 4,
3769                      COLS / 10,
3770                      LINES / 2,
3771                      COLS / 9);
3772         set_panel_userptr(p3, (NCURSES_CONST void *) "p3");
3773
3774         p4 = mkpanel(COLOR_BLUE,
3775                      LINES / 2 - 2,
3776                      COLS / 8,
3777                      LINES / 2 - 2,
3778                      COLS / 3);
3779         set_panel_userptr(p4, (NCURSES_CONST void *) "p4");
3780
3781         p5 = mkpanel(COLOR_MAGENTA,
3782                      LINES / 2 - 2,
3783                      COLS / 8,
3784                      LINES / 2,
3785