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