blob: a272d1423eeefd43c08134d41cdd8950a986b29b [file] [log] [blame]
Steve Kondikae271bc2015-11-15 02:50:53 +01001/****************************************************************************
micky3879b9f5e72025-07-08 18:04:53 -04002 * Copyright 2018-2022,2023 Thomas E. Dickey *
3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
Steve Kondikae271bc2015-11-15 02:50:53 +01004 * *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
12 * *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
15 * *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * *
24 * Except as contained in this notice, the name(s) of the above copyright *
25 * holders shall not be used in advertising or otherwise to promote the *
26 * sale, use or other dealings in this Software without prior written *
27 * authorization. *
28 ****************************************************************************/
29/****************************************************************************
30
31NAME
32 ncurses.c --- ncurses library exerciser
33
34SYNOPSIS
35 ncurses
36
37DESCRIPTION
38 An interactive test module for the ncurses library.
39
40AUTHOR
41 Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
42 Thomas E. Dickey (beginning revision 1.27 in 1996).
43
micky3879b9f5e72025-07-08 18:04:53 -040044$Id: ncurses.c,v 1.538 2023/11/11 01:23:59 tom Exp $
Steve Kondikae271bc2015-11-15 02:50:53 +010045
46***************************************************************************/
47
micky3879b9f5e72025-07-08 18:04:53 -040048#define NEED_TIME_H 1
Steve Kondikae271bc2015-11-15 02:50:53 +010049#include <test.priv.h>
50
51#ifdef __hpux
52#undef mvwdelch /* HPUX 11.23 macro will not compile */
53#endif
54
Steve Kondikae271bc2015-11-15 02:50:53 +010055#if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
56#include <sys/time.h>
57#endif
micky3879b9f5e72025-07-08 18:04:53 -040058
Steve Kondikae271bc2015-11-15 02:50:53 +010059#if HAVE_SYS_SELECT_H
60#include <sys/select.h>
61#endif
Steve Kondikae271bc2015-11-15 02:50:53 +010062
63#if USE_LIBPANEL
64#include <panel.h>
65#endif
66
67#if USE_LIBMENU
68#include <menu.h>
69#endif
70
71#if USE_LIBFORM
72#include <form.h>
73#endif
74
75#ifdef NCURSES_VERSION
76
77#define NCURSES_CONST_PARAM const void
78
79#ifdef TRACE
80static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
Steve Kondikae271bc2015-11-15 02:50:53 +010081#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#ifndef WACS_S3
110#ifdef CURSES_WACS_ARRAY
111#define WACS_S3 (&(CURSES_WACS_ARRAY['p'])) /* scan line 3 */
112#define WACS_S7 (&(CURSES_WACS_ARRAY['r'])) /* scan line 7 */
113#define WACS_LEQUAL (&(CURSES_WACS_ARRAY['y'])) /* less/equal */
114#define WACS_GEQUAL (&(CURSES_WACS_ARRAY['z'])) /* greater/equal */
115#define WACS_PI (&(CURSES_WACS_ARRAY['{'])) /* Pi */
116#define WACS_NEQUAL (&(CURSES_WACS_ARRAY['|'])) /* not equal */
117#define WACS_STERLING (&(CURSES_WACS_ARRAY['}'])) /* UK pound sign */
118#endif
119#endif
120
121#endif
122
123#if HAVE_WCSRTOMBS
124#define count_wchars(src, len, state) wcsrtombs(0, &src, len, state)
125#define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
126#define reset_wchars(state) init_mb(state)
127#elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
128#define count_wchars(src, len, state) wcstombs(0, src, len)
129#define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
130#define reset_wchars(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
131#define state_unused
132#endif
133
134#if HAVE_MBSRTOWCS
135#define count_mbytes(src, len, state) mbsrtowcs(0, &src, len, state)
136#define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
137#define reset_mbytes(state) init_mb(state)
138#elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
139#define count_mbytes(src, len, state) mbstowcs(0, src, len)
140#define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
141#define reset_mbytes(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
142#define state_unused
143#endif
144
micky3879b9f5e72025-07-08 18:04:53 -0400145#define ToggleAcs(temp,real) temp = ((temp == real) ? NULL : real)
Steve Kondikae271bc2015-11-15 02:50:53 +0100146
147#define P(string) printw("%s\n", string)
148
149#define BLANK ' ' /* this is the background character */
150
micky3879b9f5e72025-07-08 18:04:53 -0400151static int MaxColors; /* the actual number of colors we'll use */
152static int MinColors; /* the minimum color code */
153static bool UseColors; /* true if we use colors */
Steve Kondikae271bc2015-11-15 02:50:53 +0100154
155#undef max_pairs
156static int max_pairs; /* ...and the number of color pairs */
157
micky3879b9f5e72025-07-08 18:04:53 -0400158#if HAVE_COLOR_CONTENT
Steve Kondikae271bc2015-11-15 02:50:53 +0100159typedef struct {
160 NCURSES_COLOR_T red;
161 NCURSES_COLOR_T green;
162 NCURSES_COLOR_T blue;
163} RGB_DATA;
164
165static RGB_DATA *all_colors;
micky3879b9f5e72025-07-08 18:04:53 -0400166#endif
Steve Kondikae271bc2015-11-15 02:50:53 +0100167
168static void main_menu(bool);
micky3879b9f5e72025-07-08 18:04:53 -0400169static GCC_NORETURN void failed(const char *s);
Steve Kondikae271bc2015-11-15 02:50:53 +0100170
171static void
172failed(const char *s)
173{
174 perror(s);
175 endwin();
176 ExitProgram(EXIT_FAILURE);
177}
178
Steve Kondikae271bc2015-11-15 02:50:53 +0100179static void
180Repaint(void)
181{
182 touchwin(stdscr);
micky3879b9f5e72025-07-08 18:04:53 -0400183#if HAVE_CURSCR
Steve Kondikae271bc2015-11-15 02:50:53 +0100184 touchwin(curscr);
185 wrefresh(curscr);
micky3879b9f5e72025-07-08 18:04:53 -0400186#else
187 wrefresh(stdscr);
188#endif
Steve Kondikae271bc2015-11-15 02:50:53 +0100189}
190
191static bool
micky3879b9f5e72025-07-08 18:04:53 -0400192isQuit(int c, bool escape)
Steve Kondikae271bc2015-11-15 02:50:53 +0100193{
micky3879b9f5e72025-07-08 18:04:53 -0400194 return ((c) == QUIT || (escape && ((c) == ESCAPE)));
Steve Kondikae271bc2015-11-15 02:50:53 +0100195}
196#define case_QUIT QUIT: case ESCAPE
197
198/* Common function to allow ^T to toggle trace-mode in the middle of a test
199 * so that trace-files can be made smaller.
200 */
201static int
202wGetchar(WINDOW *win)
203{
204 int c;
205#ifdef TRACE
206 while ((c = wgetch(win)) == CTRL('T')) {
207 if (_nc_tracing) {
208 save_trace = _nc_tracing;
209 Trace(("TOGGLE-TRACING OFF"));
210 _nc_tracing = 0;
211 } else {
212 _nc_tracing = save_trace;
213 }
micky3879b9f5e72025-07-08 18:04:53 -0400214 curses_trace(_nc_tracing);
Steve Kondikae271bc2015-11-15 02:50:53 +0100215 if (_nc_tracing)
216 Trace(("TOGGLE-TRACING ON"));
217 }
218#else
219 c = wgetch(win);
220#endif
221 return c;
222}
223#define Getchar() wGetchar(stdscr)
224
micky3879b9f5e72025-07-08 18:04:53 -0400225#if USE_SOFTKEYS
Steve Kondikae271bc2015-11-15 02:50:53 +0100226/* replaces wgetnstr(), since we want to be able to edit values */
227static void
228wGetstring(WINDOW *win, char *buffer, int limit)
229{
micky3879b9f5e72025-07-08 18:04:53 -0400230 int y0, x0, x;
Steve Kondikae271bc2015-11-15 02:50:53 +0100231 bool done = FALSE;
232
233 echo();
234 getyx(win, y0, x0);
235 (void) wattrset(win, A_REVERSE);
236
237 x = (int) strlen(buffer);
238 while (!done) {
micky3879b9f5e72025-07-08 18:04:53 -0400239 int ch;
Steve Kondikae271bc2015-11-15 02:50:53 +0100240 if (x > (int) strlen(buffer))
241 x = (int) strlen(buffer);
242 wmove(win, y0, x0);
243 wprintw(win, "%-*s", limit, buffer);
244 wmove(win, y0, x0 + x);
245 switch (ch = wGetchar(win)) {
246 case '\n':
247 case KEY_ENTER:
248 done = TRUE;
249 break;
250 case CTRL('U'):
251 *buffer = '\0';
252 break;
253 case '\b':
254 case KEY_BACKSPACE:
255 case KEY_DC:
256 if (x > 0) {
257 int j;
258 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
259 ;
260 }
261 } else {
262 beep();
263 }
264 break;
265 case KEY_LEFT:
266 if (x > 0) {
267 --x;
268 } else {
269 flash();
270 }
271 break;
272 case KEY_RIGHT:
273 ++x;
274 break;
275 default:
276 if (!isprint(ch) || ch >= KEY_MIN) {
277 beep();
278 } else if ((int) strlen(buffer) < limit) {
279 int j;
280 for (j = (int) strlen(buffer) + 1; j > x; --j) {
281 buffer[j] = buffer[j - 1];
282 }
283 buffer[x++] = (char) ch;
284 } else {
285 flash();
286 }
287 }
288 }
289
290 wattroff(win, A_REVERSE);
291 wmove(win, y0, x0);
292 noecho();
293}
micky3879b9f5e72025-07-08 18:04:53 -0400294#endif
Steve Kondikae271bc2015-11-15 02:50:53 +0100295
296#if USE_WIDEC_SUPPORT
297static wchar_t
298fullwidth_digit(int ch)
299{
300 return (wchar_t) (ch + 0xff10 - '0');
301}
302
303static void
304make_fullwidth_text(wchar_t *target, const char *source)
305{
306 int ch;
307 while ((ch = *source++) != 0) {
308 *target++ = fullwidth_digit(ch);
309 }
310 *target = 0;
311}
312
313static void
314make_narrow_text(wchar_t *target, const char *source)
315{
316 int ch;
317 while ((ch = *source++) != 0) {
318 *target++ = (wchar_t) ch;
319 }
320 *target = 0;
321}
322
323#if USE_LIBPANEL
324static void
325make_fullwidth_digit(cchar_t *target, int digit)
326{
327 wchar_t source[2];
328
329 source[0] = fullwidth_digit(digit + '0');
330 source[1] = 0;
331 setcchar(target, source, A_NORMAL, 0, 0);
332}
333#endif
334
335static int
336wGet_wchar(WINDOW *win, wint_t *result)
337{
338 int c;
339#ifdef TRACE
340 while ((c = wget_wch(win, result)) == CTRL('T')) {
341 if (_nc_tracing) {
342 save_trace = _nc_tracing;
343 Trace(("TOGGLE-TRACING OFF"));
344 _nc_tracing = 0;
345 } else {
346 _nc_tracing = save_trace;
347 }
micky3879b9f5e72025-07-08 18:04:53 -0400348 curses_trace(_nc_tracing);
Steve Kondikae271bc2015-11-15 02:50:53 +0100349 if (_nc_tracing)
350 Trace(("TOGGLE-TRACING ON"));
351 }
352#else
353 c = wget_wch(win, result);
354#endif
355 return c;
356}
357#define Get_wchar(result) wGet_wchar(stdscr, result)
358
359/* replaces wgetn_wstr(), since we want to be able to edit values */
micky3879b9f5e72025-07-08 18:04:53 -0400360#if USE_SOFTKEYS
Steve Kondikae271bc2015-11-15 02:50:53 +0100361static void
362wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
363{
364 int y0, x0, x;
365 wint_t ch;
366 bool done = FALSE;
367 bool fkey = FALSE;
368
369 echo();
370 getyx(win, y0, x0);
371 (void) wattrset(win, A_REVERSE);
372
373 x = (int) wcslen(buffer);
374 while (!done) {
375 if (x > (int) wcslen(buffer))
376 x = (int) wcslen(buffer);
377
378 /* clear the "window' */
379 wmove(win, y0, x0);
380 wprintw(win, "%*s", limit, " ");
381
382 /* write the existing buffer contents */
383 wmove(win, y0, x0);
384 waddnwstr(win, buffer, limit);
385
386 /* positions the cursor past character 'x' */
387 wmove(win, y0, x0);
388 waddnwstr(win, buffer, x);
389
390 switch (wGet_wchar(win, &ch)) {
391 case KEY_CODE_YES:
392 fkey = TRUE;
393 switch (ch) {
394 case KEY_ENTER:
395 ch = '\n';
396 fkey = FALSE;
397 break;
398 case KEY_BACKSPACE:
399 case KEY_DC:
400 ch = '\b';
401 fkey = FALSE;
402 break;
403 case KEY_LEFT:
404 case KEY_RIGHT:
405 break;
406 default:
407 ch = (wint_t) -1;
408 break;
409 }
410 break;
411 case OK:
412 fkey = FALSE;
413 break;
414 default:
415 ch = (wint_t) -1;
416 fkey = TRUE;
417 break;
418 }
419
420 switch (ch) {
421 case '\n':
422 done = TRUE;
423 break;
424 case CTRL('U'):
425 *buffer = '\0';
426 break;
427 case '\b':
428 if (x > 0) {
429 int j;
430 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
431 ;
432 }
433 } else {
434 beep();
435 }
436 break;
437 case KEY_LEFT:
438 if (x > 0) {
439 --x;
440 } else {
441 beep();
442 }
443 break;
444 case KEY_RIGHT:
445 ++x;
446 break;
447 default:
448 if (fkey) {
449 beep();
450 } else if ((int) wcslen(buffer) < limit) {
451 int j;
452 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
453 buffer[j] = buffer[j - 1];
454 }
455 buffer[x++] = (wchar_t) ch;
456 } else {
457 beep();
458 }
459 }
460 }
461
462 wattroff(win, A_REVERSE);
463 wmove(win, y0, x0);
464 noecho();
465}
micky3879b9f5e72025-07-08 18:04:53 -0400466#endif /* USE_SOFTKEYS */
Steve Kondikae271bc2015-11-15 02:50:53 +0100467
micky3879b9f5e72025-07-08 18:04:53 -0400468#endif /* USE_WIDEC_SUPPORT */
Steve Kondikae271bc2015-11-15 02:50:53 +0100469
470static void
471Pause(void)
472{
473 move(LINES - 1, 0);
474 addstr("Press any key to continue... ");
475 (void) Getchar();
476}
477
478static void
479Cannot(const char *what)
480{
481 printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
482 Pause();
micky3879b9f5e72025-07-08 18:04:53 -0400483 endwin();
Steve Kondikae271bc2015-11-15 02:50:53 +0100484}
485
486static void
487ShellOut(bool message)
488{
489 if (message)
490 addstr("Shelling out...");
491 def_prog_mode();
492 endwin();
micky3879b9f5e72025-07-08 18:04:53 -0400493#ifdef _NC_WINDOWS
Steve Kondikae271bc2015-11-15 02:50:53 +0100494 system("cmd.exe");
495#else
496 IGNORE_RC(system("sh"));
497#endif
498 if (message)
499 addstr("returned from shellout.\n");
500 refresh();
501}
502
503#ifdef NCURSES_MOUSE_VERSION
504/*
505 * This function is the same as _tracemouse(), but we cannot count on that
506 * being available in the non-debug library.
507 */
508static const char *
509mouse_decode(MEVENT const *ep)
510{
511 static char buf[80 + (5 * 10) + (32 * 15)];
512
micky3879b9f5e72025-07-08 18:04:53 -0400513 (void) _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
514 "id %2d at (%2d, %2d, %d) state %4lx = {",
515 ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
Steve Kondikae271bc2015-11-15 02:50:53 +0100516
micky3879b9f5e72025-07-08 18:04:53 -0400517#define SHOW(m, s) \
518 if ((ep->bstate & m)==m) { \
519 _nc_STRCAT(buf, s, sizeof(buf)); \
520 _nc_STRCAT(buf, ", ", sizeof(buf)); \
521 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100522
523 SHOW(BUTTON1_RELEASED, "release-1");
524 SHOW(BUTTON1_PRESSED, "press-1");
525 SHOW(BUTTON1_CLICKED, "click-1");
526 SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
527 SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
528#if NCURSES_MOUSE_VERSION == 1
529 SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
530#endif
531
532 SHOW(BUTTON2_RELEASED, "release-2");
533 SHOW(BUTTON2_PRESSED, "press-2");
534 SHOW(BUTTON2_CLICKED, "click-2");
535 SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
536 SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
537#if NCURSES_MOUSE_VERSION == 1
538 SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
539#endif
540
541 SHOW(BUTTON3_RELEASED, "release-3");
542 SHOW(BUTTON3_PRESSED, "press-3");
543 SHOW(BUTTON3_CLICKED, "click-3");
544 SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
545 SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
546#if NCURSES_MOUSE_VERSION == 1
547 SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
548#endif
549
550 SHOW(BUTTON4_RELEASED, "release-4");
551 SHOW(BUTTON4_PRESSED, "press-4");
552 SHOW(BUTTON4_CLICKED, "click-4");
553 SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
554 SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
555#if NCURSES_MOUSE_VERSION == 1
556 SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
557#endif
558
559#if NCURSES_MOUSE_VERSION == 2
560 SHOW(BUTTON5_RELEASED, "release-5");
561 SHOW(BUTTON5_PRESSED, "press-5");
562 SHOW(BUTTON5_CLICKED, "click-5");
563 SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
564 SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
565#endif
566
567 SHOW(BUTTON_CTRL, "ctrl");
568 SHOW(BUTTON_SHIFT, "shift");
569 SHOW(BUTTON_ALT, "alt");
570 SHOW(ALL_MOUSE_EVENTS, "all-events");
571 SHOW(REPORT_MOUSE_POSITION, "position");
572
573#undef SHOW
574
575 if (buf[strlen(buf) - 1] == ' ')
576 buf[strlen(buf) - 2] = '\0';
micky3879b9f5e72025-07-08 18:04:53 -0400577 _nc_STRCAT(buf, "}", sizeof(buf));
Steve Kondikae271bc2015-11-15 02:50:53 +0100578 return (buf);
579}
580
581static void
582show_mouse(WINDOW *win)
583{
Steve Kondikae271bc2015-11-15 02:50:53 +0100584 MEVENT event;
585 bool outside;
586 bool show_loc;
587
588 getmouse(&event);
589 outside = !wenclose(win, event.y, event.x);
590
591 if (outside) {
592 (void) wstandout(win);
593 waddstr(win, "KEY_MOUSE");
594 (void) wstandend(win);
595 } else {
596 waddstr(win, "KEY_MOUSE");
597 }
598 wprintw(win, ", %s", mouse_decode(&event));
599
600 if (outside)
601 win = stdscr;
602
603 show_loc = wmouse_trafo(win, &event.y, &event.x, FALSE);
604
605 if (show_loc) {
micky3879b9f5e72025-07-08 18:04:53 -0400606 int y, x;
Steve Kondikae271bc2015-11-15 02:50:53 +0100607 getyx(win, y, x);
608 wmove(win, event.y, event.x);
609 waddch(win, '*');
610 wmove(win, y, x);
611 }
612
613 if (outside)
614 wnoutrefresh(win);
615}
616#endif /* NCURSES_MOUSE_VERSION */
617
618/****************************************************************************
619 *
620 * Character input test
621 *
622 ****************************************************************************/
623
624#define NUM_GETCH_FLAGS 256
625typedef bool GetchFlags[NUM_GETCH_FLAGS];
626
627static void
628setup_getch(WINDOW *win, GetchFlags flags)
629{
630 keypad(win, flags['k']); /* should be redundant, but for testing */
631 meta(win, flags['m']); /* force this to a known state */
632 if (flags['e'])
633 echo();
634 else
635 noecho();
636}
637
638static void
micky3879b9f5e72025-07-08 18:04:53 -0400639init_getch(WINDOW *win, GetchFlags flags, int delay)
Steve Kondikae271bc2015-11-15 02:50:53 +0100640{
641 memset(flags, FALSE, NUM_GETCH_FLAGS);
642 flags[UChar('k')] = (win == stdscr);
643 flags[UChar('m')] = TRUE;
micky3879b9f5e72025-07-08 18:04:53 -0400644 flags[UChar('t')] = (delay != 0);
Steve Kondikae271bc2015-11-15 02:50:53 +0100645
646 setup_getch(win, flags);
647}
648
micky3879b9f5e72025-07-08 18:04:53 -0400649static bool
650blocking_getch(GetchFlags flags, int delay)
651{
652 return ((delay < 0) && flags['t']);
653}
654
655#define ExitOnEscape() (flags[UChar('k')] && flags[UChar('t')])
656
Steve Kondikae271bc2015-11-15 02:50:53 +0100657static void
658wgetch_help(WINDOW *win, GetchFlags flags)
659{
660 static const char *help[] =
661 {
662 "e -- toggle echo mode"
663 ,"g -- triggers a getstr test"
664 ,"k -- toggle keypad/literal mode"
665 ,"m -- toggle meta (7-bit/8-bit) mode"
666 ,"^q -- quit"
micky3879b9f5e72025-07-08 18:04:53 -0400667 ,"s -- shell out"
668 ,"t -- toggle timeout"
Steve Kondikae271bc2015-11-15 02:50:53 +0100669 ,"w -- create a new window"
670#ifdef SIGTSTP
671 ,"z -- suspend this process"
672#endif
673 };
674 int y, x;
675 unsigned chk = ((SIZEOF(help) + 1) / 2);
676 unsigned n;
677
678 getyx(win, y, x);
679 move(0, 0);
680 printw("Type any key to see its %s value. Also:\n",
681 flags['k'] ? "keypad" : "literal");
682 for (n = 0; n < SIZEOF(help); ++n) {
micky3879b9f5e72025-07-08 18:04:53 -0400683 const char *msg = help[n];
Steve Kondikae271bc2015-11-15 02:50:53 +0100684 int row = 1 + (int) (n % chk);
685 int col = (n >= chk) ? COLS / 2 : 0;
micky3879b9f5e72025-07-08 18:04:53 -0400686 int flg = ((strstr(msg, "toggle") != 0)
687 && (flags[UChar(*msg)] != FALSE));
688 if (*msg == '^' && ExitOnEscape())
689 msg = "^[,^q -- quit";
Steve Kondikae271bc2015-11-15 02:50:53 +0100690 if (flg)
691 (void) standout();
micky3879b9f5e72025-07-08 18:04:53 -0400692 MvPrintw(row, col, "%s", msg);
Steve Kondikae271bc2015-11-15 02:50:53 +0100693 if (col == 0)
694 clrtoeol();
695 if (flg)
696 (void) standend();
697 }
698 wrefresh(stdscr);
699 wmove(win, y, x);
700}
701
702static void
703wgetch_wrap(WINDOW *win, int first_y)
704{
705 int last_y = getmaxy(win) - 1;
706 int y = getcury(win) + 1;
707
708 if (y >= last_y)
709 y = first_y;
710 wmove(win, y, 0);
711 wclrtoeol(win);
712}
713
714#if defined(KEY_RESIZE) && HAVE_WRESIZE
715typedef struct {
716 WINDOW *text;
717 WINDOW *frame;
718} WINSTACK;
719
720static WINSTACK *winstack = 0;
721static unsigned len_winstack = 0;
722
723static void
724forget_boxes(void)
725{
726 if (winstack != 0) {
727 free(winstack);
728 }
729 winstack = 0;
730 len_winstack = 0;
731}
732
733static void
734remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
735{
736 unsigned need = (level + 1) * 2;
737
738 assert(level < (unsigned) COLS);
739
740 if (winstack == 0) {
741 len_winstack = 20;
742 winstack = typeMalloc(WINSTACK, len_winstack);
743 } else if (need >= len_winstack) {
744 len_winstack = need;
745 winstack = typeRealloc(WINSTACK, len_winstack, winstack);
746 }
747 if (!winstack)
748 failed("remember_boxes");
749 winstack[level].text = txt_win;
750 winstack[level].frame = box_win;
751}
752
753#if USE_SOFTKEYS && (defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
754static void
755slk_repaint(void)
756{
757 /* this chunk is now done in resize_term() */
758 slk_touch();
759 slk_clear();
760 slk_noutrefresh();
761}
762
763#else
764#define slk_repaint() /* nothing */
765#endif
766
767#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
768/*
769 * For wgetch_test(), we create pairs of windows - one for a box, one for text.
770 * Resize both and paint the box in the parent.
771 */
772static void
micky3879b9f5e72025-07-08 18:04:53 -0400773resize_boxes(unsigned level, const WINDOW *const win)
Steve Kondikae271bc2015-11-15 02:50:53 +0100774{
775 unsigned n;
776 int base = 5;
777 int high = LINES - base;
778 int wide = COLS;
779
780 touchwin(stdscr);
781 wnoutrefresh(stdscr);
782
783 slk_repaint();
784
785 for (n = 0; n < level; ++n) {
786 wresize(winstack[n].frame, high, wide);
787 wresize(winstack[n].text, high - 2, wide - 2);
788 high -= 2;
789 wide -= 2;
790 werase(winstack[n].text);
791 box(winstack[n].frame, 0, 0);
792 wnoutrefresh(winstack[n].frame);
793 wprintw(winstack[n].text,
794 "size %dx%d\n",
795 getmaxy(winstack[n].text),
796 getmaxx(winstack[n].text));
797 wnoutrefresh(winstack[n].text);
798 if (winstack[n].text == win)
799 break;
800 }
801 doupdate();
802}
803#endif /* resize_boxes */
804#else
805#define forget_boxes() /* nothing */
806#define remember_boxes(level,text,frame) /* nothing */
807#endif
808
809/*
810 * Return-code is OK/ERR or a keyname.
811 */
812static const char *
813ok_keyname(int code)
814{
815 return ((code == OK) ? "OK" : ((code == ERR) ? "ERR" : keyname(code)));
816}
817
818static void
819wgetch_test(unsigned level, WINDOW *win, int delay)
820{
821 char buf[BUFSIZ];
822 int first_y, first_x;
Steve Kondikae271bc2015-11-15 02:50:53 +0100823 int incount = 0;
824 GetchFlags flags;
Steve Kondikae271bc2015-11-15 02:50:53 +0100825
micky3879b9f5e72025-07-08 18:04:53 -0400826 init_getch(win, flags, delay);
827 notimeout(win, FALSE);
Steve Kondikae271bc2015-11-15 02:50:53 +0100828 wtimeout(win, delay);
829 getyx(win, first_y, first_x);
830
831 wgetch_help(win, flags);
832 wsetscrreg(win, first_y, getmaxy(win) - 1);
833 scrollok(win, TRUE);
834
835 for (;;) {
micky3879b9f5e72025-07-08 18:04:53 -0400836 int c;
837
Steve Kondikae271bc2015-11-15 02:50:53 +0100838 while ((c = wGetchar(win)) == ERR) {
839 incount++;
micky3879b9f5e72025-07-08 18:04:53 -0400840 if (blocking_getch(flags, delay)) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100841 (void) wprintw(win, "%05d: input error", incount);
842 break;
843 } else {
844 (void) wprintw(win, "%05d: input timed out", incount);
845 }
846 wgetch_wrap(win, first_y);
847 }
micky3879b9f5e72025-07-08 18:04:53 -0400848 if (c == ERR && blocking_getch(flags, delay)) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100849 wprintw(win, "ERR");
850 wgetch_wrap(win, first_y);
micky3879b9f5e72025-07-08 18:04:53 -0400851 } else if (isQuit(c, ExitOnEscape())) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100852 break;
853 } else if (c == 'e') {
854 flags[UChar('e')] = !flags[UChar('e')];
855 setup_getch(win, flags);
856 wgetch_help(win, flags);
857 } else if (c == 'g') {
micky3879b9f5e72025-07-08 18:04:53 -0400858 waddstr(win, "wgetnstr test: ");
Steve Kondikae271bc2015-11-15 02:50:53 +0100859 echo();
860 c = wgetnstr(win, buf, sizeof(buf) - 1);
861 noecho();
862 wprintw(win, "I saw %d characters:\n\t`%s' (%s).",
863 (int) strlen(buf), buf,
864 ok_keyname(c));
865 wclrtoeol(win);
866 wgetch_wrap(win, first_y);
867 } else if (c == 'k') {
868 flags[UChar('k')] = !flags[UChar('k')];
869 setup_getch(win, flags);
870 wgetch_help(win, flags);
871 } else if (c == 'm') {
872 flags[UChar('m')] = !flags[UChar('m')];
873 setup_getch(win, flags);
874 wgetch_help(win, flags);
875 } else if (c == 's') {
876 ShellOut(TRUE);
micky3879b9f5e72025-07-08 18:04:53 -0400877 } else if (c == 't') {
878 notimeout(win, flags[UChar('t')]);
879 flags[UChar('t')] = !flags[UChar('t')];
880 wgetch_help(win, flags);
Steve Kondikae271bc2015-11-15 02:50:53 +0100881 } else if (c == 'w') {
882 int high = getmaxy(win) - 1 - first_y + 1;
883 int wide = getmaxx(win) - first_x;
884 int old_y, old_x;
885 int new_y = first_y + getbegy(win);
886 int new_x = first_x + getbegx(win);
887
888 getyx(win, old_y, old_x);
889 if (high > 2 && wide > 2) {
890 WINDOW *wb = newwin(high, wide, new_y, new_x);
891 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
892
893 box(wb, 0, 0);
894 wrefresh(wb);
895 wmove(wi, 0, 0);
896 remember_boxes(level, wi, wb);
897 wgetch_test(level + 1, wi, delay);
898 delwin(wi);
899 delwin(wb);
900
901 wgetch_help(win, flags);
902 wmove(win, old_y, old_x);
903 touchwin(win);
904 wrefresh(win);
905 doupdate();
906 }
907#ifdef SIGTSTP
908 } else if (c == 'z') {
909 kill(getpid(), SIGTSTP);
910#endif
911 } else {
912 wprintw(win, "Key pressed: %04o ", c);
913#ifdef NCURSES_MOUSE_VERSION
914 if (c == KEY_MOUSE) {
915 show_mouse(win);
916 } else
917#endif /* NCURSES_MOUSE_VERSION */
918 if (c >= KEY_MIN) {
919#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
920 if (c == KEY_RESIZE) {
921 resize_boxes(level, win);
922 }
923#endif
924 (void) waddstr(win, keyname(c));
925 } else if (c >= 0x80) {
926 unsigned c2 = (unsigned) c;
927#if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
928 /* at least Solaris SVR4 curses breaks unctrl(128), etc. */
929 c2 &= 0x7f;
930#endif
931 if (isprint(c))
932 (void) wprintw(win, "%c", UChar(c));
933 else if (c2 != UChar(c))
934 (void) wprintw(win, "M-%s", unctrl(c2));
935 else
936 (void) wprintw(win, "%s", unctrl(c2));
937 waddstr(win, " (high-half character)");
938 } else {
939 if (isprint(c))
940 (void) wprintw(win, "%c (ASCII printable character)", c);
941 else
942 (void) wprintw(win, "%s (ASCII control character)",
943 unctrl(UChar(c)));
944 }
945 wgetch_wrap(win, first_y);
946 }
947 }
948
949 wtimeout(win, -1);
950
951 if (!level)
micky3879b9f5e72025-07-08 18:04:53 -0400952 init_getch(win, flags, delay);
Steve Kondikae271bc2015-11-15 02:50:53 +0100953}
954
955static int
956begin_getch_test(void)
957{
958 char buf[BUFSIZ];
959 int delay;
960
961 refresh();
962
963#ifdef NCURSES_MOUSE_VERSION
964 mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, (mmask_t *) 0);
965#endif
966
967 (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
968 echo();
969 getnstr(buf, sizeof(buf) - 1);
970 noecho();
971 nonl();
972
973 if (isdigit(UChar(buf[0]))) {
974 delay = atoi(buf) * 100;
975 } else {
976 delay = -1;
977 }
978 raw();
micky3879b9f5e72025-07-08 18:04:53 -0400979 move(6, 0);
Steve Kondikae271bc2015-11-15 02:50:53 +0100980 return delay;
981}
982
983static void
984finish_getch_test(void)
985{
986#ifdef NCURSES_MOUSE_VERSION
987 mousemask(0, (mmask_t *) 0);
988#endif
989 erase();
990 noraw();
991 nl();
992 endwin();
993}
994
micky3879b9f5e72025-07-08 18:04:53 -0400995static int
996getch_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +0100997{
998 int delay = begin_getch_test();
999
1000 slk_restore();
1001 wgetch_test(0, stdscr, delay);
1002 forget_boxes();
1003 finish_getch_test();
1004 slk_clear();
micky3879b9f5e72025-07-08 18:04:53 -04001005 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01001006}
1007
1008#if USE_WIDEC_SUPPORT
1009/*
1010 * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
1011 * Resize both and paint the box in the parent.
1012 */
1013#if defined(KEY_RESIZE) && HAVE_WRESIZE
1014static void
micky3879b9f5e72025-07-08 18:04:53 -04001015resize_wide_boxes(unsigned level, const WINDOW *const win)
Steve Kondikae271bc2015-11-15 02:50:53 +01001016{
1017 unsigned n;
1018 int base = 5;
1019 int high = LINES - base;
1020 int wide = COLS;
1021
1022 touchwin(stdscr);
1023 wnoutrefresh(stdscr);
1024
1025 slk_repaint();
1026
1027 for (n = 0; n < level; ++n) {
1028 wresize(winstack[n].frame, high, wide);
1029 wresize(winstack[n].text, high - 2, wide - 2);
1030 high -= 2;
1031 wide -= 2;
1032 werase(winstack[n].text);
1033 box_set(winstack[n].frame, 0, 0);
1034 wnoutrefresh(winstack[n].frame);
1035 wprintw(winstack[n].text,
1036 "size %dx%d\n",
1037 getmaxy(winstack[n].text),
1038 getmaxx(winstack[n].text));
1039 wnoutrefresh(winstack[n].text);
1040 if (winstack[n].text == win)
1041 break;
1042 }
1043 doupdate();
1044}
1045#endif /* KEY_RESIZE */
1046
1047static char *
1048wcstos(const wchar_t *src)
1049{
1050 int need;
1051 char *result = 0;
1052 const wchar_t *tmp = src;
1053#ifndef state_unused
1054 mbstate_t state;
1055#endif
1056
1057 reset_wchars(state);
1058 if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
1059 unsigned have = (unsigned) need;
1060 if ((result = typeCalloc(char, have + 1)) != 0) {
1061 tmp = src;
1062 if (trans_wchars(result, tmp, have, &state) != have) {
1063 free(result);
1064 result = 0;
1065 }
1066 } else {
1067 failed("wcstos");
1068 }
1069 }
1070 return result;
1071}
1072
1073static void
1074wget_wch_test(unsigned level, WINDOW *win, int delay)
1075{
1076 wchar_t wchar_buf[BUFSIZ];
1077 wint_t wint_buf[BUFSIZ];
1078 int first_y, first_x;
1079 wint_t c;
1080 int incount = 0;
1081 GetchFlags flags;
Steve Kondikae271bc2015-11-15 02:50:53 +01001082 char *temp;
1083
micky3879b9f5e72025-07-08 18:04:53 -04001084 init_getch(win, flags, delay);
1085 notimeout(win, FALSE);
Steve Kondikae271bc2015-11-15 02:50:53 +01001086 wtimeout(win, delay);
1087 getyx(win, first_y, first_x);
1088
1089 wgetch_help(win, flags);
1090 wsetscrreg(win, first_y, getmaxy(win) - 1);
1091 scrollok(win, TRUE);
1092
1093 for (;;) {
micky3879b9f5e72025-07-08 18:04:53 -04001094 int code;
1095
Steve Kondikae271bc2015-11-15 02:50:53 +01001096 while ((code = wGet_wchar(win, &c)) == ERR) {
1097 incount++;
micky3879b9f5e72025-07-08 18:04:53 -04001098 if (blocking_getch(flags, delay)) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001099 (void) wprintw(win, "%05d: input error", incount);
1100 break;
1101 } else {
1102 (void) wprintw(win, "%05d: input timed out", incount);
1103 }
1104 wgetch_wrap(win, first_y);
1105 }
micky3879b9f5e72025-07-08 18:04:53 -04001106 if (code == ERR && blocking_getch(flags, delay)) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001107 wprintw(win, "ERR");
1108 wgetch_wrap(win, first_y);
micky3879b9f5e72025-07-08 18:04:53 -04001109 } else if (isQuit((int) c, ExitOnEscape())) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001110 break;
1111 } else if (c == 'e') {
1112 flags[UChar('e')] = !flags[UChar('e')];
1113 setup_getch(win, flags);
1114 wgetch_help(win, flags);
1115 } else if (c == 'g') {
micky3879b9f5e72025-07-08 18:04:53 -04001116 waddstr(win, "wgetn_str test: ");
Steve Kondikae271bc2015-11-15 02:50:53 +01001117 echo();
1118 code = wgetn_wstr(win, wint_buf, BUFSIZ - 1);
1119 noecho();
1120 if (code == ERR) {
1121 wprintw(win, "wgetn_wstr returns an error.");
1122 } else {
1123 int n;
1124 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1125 ;
1126 }
1127 if ((temp = wcstos(wchar_buf)) != 0) {
1128 wprintw(win, "I saw %d characters:\n\t`%s'.",
1129 (int) wcslen(wchar_buf), temp);
1130 free(temp);
1131 } else {
1132 wprintw(win, "I saw %d characters (cannot convert).",
1133 (int) wcslen(wchar_buf));
1134 }
1135 }
1136 wclrtoeol(win);
1137 wgetch_wrap(win, first_y);
1138 } else if (c == 'k') {
1139 flags[UChar('k')] = !flags[UChar('k')];
1140 setup_getch(win, flags);
1141 wgetch_help(win, flags);
1142 } else if (c == 'm') {
1143 flags[UChar('m')] = !flags[UChar('m')];
1144 setup_getch(win, flags);
1145 wgetch_help(win, flags);
1146 } else if (c == 's') {
1147 ShellOut(TRUE);
micky3879b9f5e72025-07-08 18:04:53 -04001148 } else if (c == 't') {
1149 notimeout(win, flags[UChar('t')]);
1150 flags[UChar('t')] = !flags[UChar('t')];
1151 wgetch_help(win, flags);
Steve Kondikae271bc2015-11-15 02:50:53 +01001152 } else if (c == 'w') {
1153 int high = getmaxy(win) - 1 - first_y + 1;
1154 int wide = getmaxx(win) - first_x;
1155 int old_y, old_x;
1156 int new_y = first_y + getbegy(win);
1157 int new_x = first_x + getbegx(win);
1158
1159 getyx(win, old_y, old_x);
1160 if (high > 2 && wide > 2) {
1161 WINDOW *wb = newwin(high, wide, new_y, new_x);
1162 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1163
1164 box_set(wb, 0, 0);
1165 wrefresh(wb);
1166 wmove(wi, 0, 0);
1167 remember_boxes(level, wi, wb);
1168 wget_wch_test(level + 1, wi, delay);
1169 delwin(wi);
1170 delwin(wb);
1171
1172 wgetch_help(win, flags);
1173 wmove(win, old_y, old_x);
1174 touchwin(win);
1175 wrefresh(win);
1176 }
1177#ifdef SIGTSTP
1178 } else if (c == 'z') {
1179 kill(getpid(), SIGTSTP);
1180#endif
1181 } else {
1182 wprintw(win, "Key pressed: %04o ", (int) c);
1183#ifdef NCURSES_MOUSE_VERSION
1184 if (c == KEY_MOUSE) {
1185 show_mouse(win);
1186 } else
1187#endif /* NCURSES_MOUSE_VERSION */
1188 if (code == KEY_CODE_YES) {
1189#if defined(KEY_RESIZE) && HAVE_WRESIZE
1190 if (c == KEY_RESIZE) {
1191 resize_wide_boxes(level, win);
1192 }
1193#endif
1194 (void) waddstr(win, keyname((wchar_t) c));
1195 } else {
1196 (void) waddstr(win, key_name((wchar_t) c));
1197 if (c < 256 && iscntrl(c)) {
1198 (void) wprintw(win, " (control character)");
1199 } else {
1200 (void) wprintw(win, " = %#x (printable character)",
1201 (unsigned) c);
1202 }
1203 }
1204 wgetch_wrap(win, first_y);
1205 }
1206 }
1207
1208 wtimeout(win, -1);
1209
1210 if (!level)
micky3879b9f5e72025-07-08 18:04:53 -04001211 init_getch(win, flags, delay);
Steve Kondikae271bc2015-11-15 02:50:53 +01001212}
1213
micky3879b9f5e72025-07-08 18:04:53 -04001214static int
1215x_getch_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01001216{
1217 int delay = begin_getch_test();
1218
1219 slk_restore();
1220 wget_wch_test(0, stdscr, delay);
1221 forget_boxes();
1222 finish_getch_test();
1223 slk_clear();
micky3879b9f5e72025-07-08 18:04:53 -04001224 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01001225}
1226#endif
1227
1228/****************************************************************************
1229 *
1230 * Character attributes test
1231 *
1232 ****************************************************************************/
1233
1234#if HAVE_SETUPTERM || HAVE_TGETENT
1235#define get_ncv() TIGETNUM("ncv","NC")
1236#define get_xmc() TIGETNUM("xmc","sg")
1237#else
1238#define get_ncv() -1
1239#define get_xmc() -1
1240#endif
1241
1242#if !HAVE_TERMATTRS
1243static chtype
1244my_termattrs(void)
1245{
1246 static int first = TRUE;
1247 static chtype result = 0;
1248
1249 if (first) {
1250#if !HAVE_TIGETSTR
1251 char buffer[4096];
1252 char parsed[4096];
1253 char *area_pointer = parsed;
1254
1255 tgetent(buffer, getenv("TERM"));
1256#endif
1257
1258 if (TIGETSTR("smso", "so"))
1259 result |= A_STANDOUT;
1260 if (TIGETSTR("smul", "us"))
1261 result |= A_UNDERLINE;
1262 if (TIGETSTR("rev", "mr"))
1263 result |= A_REVERSE;
1264 if (TIGETSTR("blink", "mb"))
1265 result |= A_BLINK;
1266 if (TIGETSTR("dim", "mh"))
1267 result |= A_DIM;
1268 if (TIGETSTR("bold", "md"))
1269 result |= A_BOLD;
1270 if (TIGETSTR("smacs", "ac"))
1271 result |= A_ALTCHARSET;
1272
1273 first = FALSE;
1274 }
1275 return result;
1276}
1277#define termattrs() my_termattrs()
1278#endif
1279
1280#define ATTRSTRING_1ST 32 /* ' ' */
1281#define ATTRSTRING_END 126 /* '~' */
1282
micky3879b9f5e72025-07-08 18:04:53 -04001283#define COLS_PRE_ATTRS 5
1284#define COLS_AFT_ATTRS 15
1285#define COL_ATTRSTRING (COLS_PRE_ATTRS + 17)
1286#define LEN_ATTRSTRING (COLS - (COL_ATTRSTRING + COLS_AFT_ATTRS))
Steve Kondikae271bc2015-11-15 02:50:53 +01001287#define MAX_ATTRSTRING (ATTRSTRING_END + 1 - ATTRSTRING_1ST)
1288
1289static char attr_test_string[MAX_ATTRSTRING + 1];
1290
1291static void
1292attr_legend(WINDOW *helpwin)
1293{
1294 int row = 1;
1295 int col = 1;
1296
1297 MvWPrintw(helpwin, row++, col,
1298 "ESC to exit.");
1299 MvWPrintw(helpwin, row++, col,
1300 "^L repaints.");
1301 ++row;
1302 MvWPrintw(helpwin, row++, col,
1303 "Modify the test strings:");
1304 MvWPrintw(helpwin, row++, col,
1305 " A digit sets gaps on each side of displayed attributes");
1306 MvWPrintw(helpwin, row++, col,
1307 " </> shifts the text left/right. ");
1308 ++row;
1309 MvWPrintw(helpwin, row++, col,
1310 "Toggles:");
micky3879b9f5e72025-07-08 18:04:53 -04001311 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001312 MvWPrintw(helpwin, row++, col,
micky3879b9f5e72025-07-08 18:04:53 -04001313 " f/F/b/B toggle foreground/background background color");
Steve Kondikae271bc2015-11-15 02:50:53 +01001314 MvWPrintw(helpwin, row++, col,
1315 " t/T toggle text/background color attribute");
1316 }
1317 MvWPrintw(helpwin, row++, col,
1318 " a/A toggle ACS (alternate character set) mapping");
1319 MvWPrintw(helpwin, row, col,
1320 " v/V toggle video attribute to combine with each line");
1321#if USE_WIDEC_SUPPORT
1322 MvWPrintw(helpwin, row, col,
1323 " w/W toggle normal/wide (double-width) test-characters");
1324#endif
1325}
1326
1327static void
1328show_color_attr(int fg, int bg, int tx)
1329{
micky3879b9f5e72025-07-08 18:04:53 -04001330 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001331 printw(" Colors (fg %d, bg %d", fg, bg);
1332 if (tx >= 0)
1333 printw(", text %d", tx);
1334 printw("),");
1335 }
1336}
1337
1338static bool
1339cycle_color_attr(int ch, NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg, NCURSES_COLOR_T *tx)
1340{
1341 bool error = FALSE;
1342
micky3879b9f5e72025-07-08 18:04:53 -04001343 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001344 switch (ch) {
1345 case 'f':
1346 *fg = (NCURSES_COLOR_T) (*fg + 1);
1347 break;
1348 case 'F':
1349 *fg = (NCURSES_COLOR_T) (*fg - 1);
1350 break;
1351 case 'b':
1352 *bg = (NCURSES_COLOR_T) (*bg + 1);
1353 break;
1354 case 'B':
1355 *bg = (NCURSES_COLOR_T) (*bg - 1);
1356 break;
1357 case 't':
1358 *tx = (NCURSES_COLOR_T) (*tx + 1);
1359 break;
1360 case 'T':
1361 *tx = (NCURSES_COLOR_T) (*tx - 1);
1362 break;
1363 default:
1364 beep();
1365 error = TRUE;
1366 break;
1367 }
1368 if (*fg >= COLORS)
micky3879b9f5e72025-07-08 18:04:53 -04001369 *fg = (NCURSES_COLOR_T) MinColors;
1370 if (*fg < MinColors)
Steve Kondikae271bc2015-11-15 02:50:53 +01001371 *fg = (NCURSES_COLOR_T) (COLORS - 1);
1372 if (*bg >= COLORS)
micky3879b9f5e72025-07-08 18:04:53 -04001373 *bg = (NCURSES_COLOR_T) MinColors;
1374 if (*bg < MinColors)
Steve Kondikae271bc2015-11-15 02:50:53 +01001375 *bg = (NCURSES_COLOR_T) (COLORS - 1);
1376 if (*tx >= COLORS)
1377 *tx = -1;
1378 if (*tx < -1)
1379 *tx = (NCURSES_COLOR_T) (COLORS - 1);
1380 } else {
1381 beep();
1382 error = TRUE;
1383 }
1384 return error;
1385}
1386
1387static void
1388adjust_attr_string(int adjust)
1389{
1390 char save = attr_test_string[0];
1391 int first = ((int) UChar(save)) + adjust;
Steve Kondikae271bc2015-11-15 02:50:53 +01001392
1393 if (first >= ATTRSTRING_1ST) {
micky3879b9f5e72025-07-08 18:04:53 -04001394 int j, k;
1395
Steve Kondikae271bc2015-11-15 02:50:53 +01001396 for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1397 if (k > ATTRSTRING_END)
1398 break;
1399 attr_test_string[j] = (char) k;
1400 if (((k + 1 - first) % 5) == 0) {
1401 if (++j >= MAX_ATTRSTRING)
1402 break;
1403 attr_test_string[j] = ' ';
1404 }
1405 }
1406 if ((LEN_ATTRSTRING - j) > 5) {
1407 attr_test_string[0] = save;
1408 adjust_attr_string(adjust - 1);
1409 } else {
1410 while (j < MAX_ATTRSTRING)
1411 attr_test_string[j++] = ' ';
1412 attr_test_string[j] = '\0';
1413 }
1414 }
1415}
1416
1417/*
1418 * Prefer the right-end of the string for starting, since that maps to the
1419 * VT100 line-drawing.
1420 */
1421static int
1422default_attr_string(void)
1423{
1424 int result = (ATTRSTRING_END - LEN_ATTRSTRING);
1425 result += (LEN_ATTRSTRING / 5);
1426 if (result < ATTRSTRING_1ST)
1427 result = ATTRSTRING_1ST;
1428 return result;
1429}
1430
1431static void
1432init_attr_string(void)
1433{
1434 attr_test_string[0] = (char) default_attr_string();
1435 adjust_attr_string(0);
1436}
1437
1438static int
1439show_attr(WINDOW *win, int row, int skip, bool arrow, chtype attr, const char *name)
1440{
1441 int ncv = get_ncv();
micky3879b9f5e72025-07-08 18:04:53 -04001442 chtype test = attr & (chtype) (~(A_ALTCHARSET | A_CHARTEXT));
Steve Kondikae271bc2015-11-15 02:50:53 +01001443
1444 if (arrow)
micky3879b9f5e72025-07-08 18:04:53 -04001445 MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1446 MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
Steve Kondikae271bc2015-11-15 02:50:53 +01001447 MvPrintw(row, COL_ATTRSTRING - 1, "|");
1448 if (skip)
1449 printw("%*s", skip, " ");
1450 /*
1451 * Just for testing, write text using the alternate character set one
1452 * character at a time (to pass its rendition directly), and use the
1453 * string operation for the other attributes.
1454 */
1455 wmove(win, 0, 0);
1456 werase(win);
1457 if (attr & A_ALTCHARSET) {
1458 const char *s;
Steve Kondikae271bc2015-11-15 02:50:53 +01001459
1460 for (s = attr_test_string; *s != '\0'; ++s) {
micky3879b9f5e72025-07-08 18:04:53 -04001461 chtype ch = UChar(*s);
Steve Kondikae271bc2015-11-15 02:50:53 +01001462 (void) waddch(win, ch | attr);
1463 }
1464 } else {
1465 (void) wattrset(win, AttrArg(attr, 0));
1466 (void) waddstr(win, attr_test_string);
1467 (void) wattroff(win, (int) attr);
1468 }
1469 if (skip)
1470 printw("%*s", skip, " ");
1471 MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1472 if (test != A_NORMAL) {
1473 if (!(termattrs() & test)) {
1474 printw(" (N/A)");
1475 } else {
1476 if (ncv > 0 && stdscr && (getbkgd(stdscr) & A_COLOR)) {
1477 static const chtype table[] =
1478 {
1479 A_STANDOUT,
1480 A_UNDERLINE,
1481 A_REVERSE,
1482 A_BLINK,
1483 A_DIM,
1484 A_BOLD,
1485#ifdef A_INVIS
1486 A_INVIS,
1487#endif
1488#ifdef A_ITALIC
1489 A_ITALIC,
1490#endif
1491 A_PROTECT,
1492 A_ALTCHARSET
1493 };
1494 unsigned n;
1495 bool found = FALSE;
1496 for (n = 0; n < SIZEOF(table); n++) {
1497 if ((table[n] & attr) != 0
1498 && ((1 << n) & ncv) != 0) {
1499 found = TRUE;
1500 break;
1501 }
1502 }
1503 if (found)
1504 printw(" (NCV)");
1505 }
micky3879b9f5e72025-07-08 18:04:53 -04001506 if ((termattrs() & test) != test) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001507 printw(" (Part)");
micky3879b9f5e72025-07-08 18:04:53 -04001508 }
Steve Kondikae271bc2015-11-15 02:50:53 +01001509 }
1510 }
1511 return row + 2;
1512}
1513
1514typedef struct {
micky3879b9f5e72025-07-08 18:04:53 -04001515 chtype attr;
Steve Kondikae271bc2015-11-15 02:50:53 +01001516 NCURSES_CONST char *name;
1517} ATTR_TBL;
1518/* *INDENT-OFF* */
1519static const ATTR_TBL attrs_to_test[] = {
1520 { A_STANDOUT, "STANDOUT" },
1521 { A_REVERSE, "REVERSE" },
1522 { A_BOLD, "BOLD" },
1523 { A_UNDERLINE, "UNDERLINE" },
1524 { A_DIM, "DIM" },
1525 { A_BLINK, "BLINK" },
1526 { A_PROTECT, "PROTECT" },
1527#ifdef A_INVIS
1528 { A_INVIS, "INVISIBLE" },
1529#endif
1530#ifdef A_ITALIC
1531 { A_ITALIC, "ITALIC" },
1532#endif
1533 { A_NORMAL, "NORMAL" },
1534};
1535/* *INDENT-ON* */
1536
1537static unsigned
1538init_attr_list(ATTR_TBL * target, attr_t attrs)
1539{
1540 unsigned result = 0;
1541 size_t n;
1542
1543 for (n = 0; n < SIZEOF(attrs_to_test); ++n) {
1544 attr_t test = attrs_to_test[n].attr;
1545 if (test == A_NORMAL || (test & attrs) != 0) {
1546 target[result++] = attrs_to_test[n];
1547 }
1548 }
1549 return result;
1550}
1551
micky3879b9f5e72025-07-08 18:04:53 -04001552#if USE_WIDEC_SUPPORT
1553typedef struct {
1554 attr_t attr;
1555 NCURSES_CONST char *name;
1556} W_ATTR_TBL;
1557/* *INDENT-OFF* */
1558static const W_ATTR_TBL w_attrs_to_test[] = {
1559 { WA_STANDOUT, "STANDOUT" },
1560 { WA_REVERSE, "REVERSE" },
1561 { WA_BOLD, "BOLD" },
1562 { WA_UNDERLINE, "UNDERLINE" },
1563 { WA_DIM, "DIM" },
1564 { WA_BLINK, "BLINK" },
1565 { WA_PROTECT, "PROTECT" },
1566#ifdef WA_INVIS
1567 { WA_INVIS, "INVISIBLE" },
1568#endif
1569#ifdef WA_ITALIC
1570 { WA_ITALIC, "ITALIC" },
1571#endif
1572 { WA_NORMAL, "NORMAL" },
1573};
1574/* *INDENT-ON* */
1575
1576static unsigned
1577init_w_attr_list(W_ATTR_TBL * target, attr_t attrs)
1578{
1579 unsigned result = 0;
1580 size_t n;
1581
1582 for (n = 0; n < SIZEOF(w_attrs_to_test); ++n) {
1583 attr_t test = w_attrs_to_test[n].attr;
1584 if (test == WA_NORMAL || (test & attrs) != 0) {
1585 target[result++] = w_attrs_to_test[n];
1586 }
1587 }
1588 return result;
1589}
1590#endif
1591
Steve Kondikae271bc2015-11-15 02:50:53 +01001592static bool
1593attr_getc(int *skip,
1594 NCURSES_COLOR_T *fg,
1595 NCURSES_COLOR_T *bg,
1596 NCURSES_COLOR_T *tx,
1597 int *ac,
1598 unsigned *kc,
1599 unsigned limit)
1600{
1601 bool result = TRUE;
1602 bool error = FALSE;
1603 WINDOW *helpwin;
1604
1605 do {
1606 int ch = Getchar();
1607
1608 error = FALSE;
1609 if (ch < 256 && isdigit(ch)) {
1610 *skip = (ch - '0');
1611 } else {
1612 switch (ch) {
1613 case CTRL('L'):
1614 Repaint();
1615 break;
micky3879b9f5e72025-07-08 18:04:53 -04001616 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +01001617 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1618 box(helpwin, 0, 0);
1619 attr_legend(helpwin);
1620 wGetchar(helpwin);
1621 delwin(helpwin);
1622 }
1623 break;
1624 case 'a':
1625 *ac = 0;
1626 break;
1627 case 'A':
1628 *ac = A_ALTCHARSET;
1629 break;
1630 case 'v':
1631 if (*kc == 0)
1632 *kc = limit - 1;
1633 else
1634 *kc -= 1;
1635 break;
1636 case 'V':
1637 *kc += 1;
1638 if (*kc >= limit)
1639 *kc = 0;
1640 break;
1641 case '<':
1642 adjust_attr_string(-1);
1643 break;
1644 case '>':
1645 adjust_attr_string(1);
1646 break;
1647 case case_QUIT:
1648 result = FALSE;
1649 break;
1650 default:
1651 error = cycle_color_attr(ch, fg, bg, tx);
1652 break;
1653 }
1654 }
1655 } while (error);
1656 return result;
1657}
1658
micky3879b9f5e72025-07-08 18:04:53 -04001659static int
1660attr_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01001661/* test text attributes */
1662{
1663 int n;
1664 int skip = get_xmc();
1665 NCURSES_COLOR_T fg = COLOR_BLACK; /* color pair 0 is special */
1666 NCURSES_COLOR_T bg = COLOR_BLACK;
1667 NCURSES_COLOR_T tx = -1;
1668 int ac = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01001669 WINDOW *my_wins[SIZEOF(attrs_to_test)];
1670 ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1671 unsigned my_size = init_attr_list(my_list, termattrs());
1672
1673 if (my_size > 1) {
micky3879b9f5e72025-07-08 18:04:53 -04001674 unsigned j, k;
1675
Steve Kondikae271bc2015-11-15 02:50:53 +01001676 for (j = 0; j < my_size; ++j) {
1677 my_wins[j] = subwin(stdscr,
1678 1, LEN_ATTRSTRING,
1679 2 + (int) (2 * j), COL_ATTRSTRING);
1680 scrollok(my_wins[j], FALSE);
1681 }
1682
1683 if (skip < 0)
1684 skip = 0;
1685
1686 n = skip; /* make it easy */
1687 k = my_size - 1;
1688 init_attr_string();
1689
1690 do {
1691 int row = 2;
1692 chtype normal = A_NORMAL | BLANK;
1693 chtype extras = (chtype) ac;
1694
micky3879b9f5e72025-07-08 18:04:53 -04001695 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001696 NCURSES_PAIRS_T pair = 0;
1697 if ((fg != COLOR_BLACK) || (bg != COLOR_BLACK)) {
1698 pair = 1;
1699 if (init_pair(pair, fg, bg) == ERR) {
1700 beep();
1701 } else {
1702 normal |= (chtype) COLOR_PAIR(pair);
1703 }
1704 }
1705 if (tx >= 0) {
1706 pair = 2;
1707 if (init_pair(pair, tx, bg) == ERR) {
1708 beep();
1709 } else {
1710 extras |= (chtype) COLOR_PAIR(pair);
1711 normal &= ~A_COLOR;
1712 }
1713 }
1714 }
1715 bkgd(normal);
1716 bkgdset(normal);
1717 erase();
1718
1719 box(stdscr, 0, 0);
1720 MvAddStr(0, 20, "Character attribute test display");
1721
1722 for (j = 0; j < my_size; ++j) {
1723 bool arrow = (j == k);
1724 row = show_attr(my_wins[j], row, n, arrow,
1725 normal |
1726 extras |
1727 my_list[j].attr |
1728 my_list[k].attr,
1729 my_list[j].name);
1730 }
1731
micky3879b9f5e72025-07-08 18:04:53 -04001732 MvPrintw(row, COLS_PRE_ATTRS,
Steve Kondikae271bc2015-11-15 02:50:53 +01001733 "This terminal does %shave the magic-cookie glitch",
1734 get_xmc() > -1 ? "" : "not ");
micky3879b9f5e72025-07-08 18:04:53 -04001735 MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
Steve Kondikae271bc2015-11-15 02:50:53 +01001736 show_color_attr(fg, bg, tx);
1737 printw(" ACS (%d)", ac != 0);
1738
1739 refresh();
1740 } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
1741
1742 bkgdset(A_NORMAL | BLANK);
1743 erase();
1744 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04001745 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01001746 } else {
1747 Cannot("does not support video attributes.");
micky3879b9f5e72025-07-08 18:04:53 -04001748 return ERR;
Steve Kondikae271bc2015-11-15 02:50:53 +01001749 }
1750}
1751
1752#if USE_WIDEC_SUPPORT
1753static bool use_fullwidth;
1754static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1755
1756#define FULL_LO 0xff00
1757#define FULL_HI 0xff5e
1758#define HALF_LO 0x20
1759
1760#define isFullWidth(ch) ((int)(ch) >= FULL_LO && (int)(ch) <= FULL_HI)
1761#define ToNormalWidth(ch) (wchar_t) (((int)(ch) - FULL_LO) + HALF_LO)
1762#define ToFullWidth(ch) (wchar_t) (((int)(ch) - HALF_LO) + FULL_LO)
1763
1764/*
1765 * Returns an ASCII code in [32..126]
1766 */
1767static wchar_t
1768normal_wchar(int ch)
1769{
1770 wchar_t result = (wchar_t) ch;
1771 if (isFullWidth(ch))
1772 result = ToNormalWidth(ch);
1773 return result;
1774}
1775
1776/*
1777 * Returns either an ASCII code in in [32..126] or full-width in
1778 * [0xff00..0xff5e], according to use_fullwidth setting.
1779 */
1780static wchar_t
1781target_wchar(int ch)
1782{
1783 wchar_t result = (wchar_t) ch;
1784 if (use_fullwidth) {
1785 if (!isFullWidth(ch))
1786 result = ToFullWidth(ch);
1787 } else {
1788 if (isFullWidth(ch))
1789 result = ToNormalWidth(ch);
1790 }
1791 return result;
1792}
1793
1794static void
1795wide_adjust_attr_string(int adjust)
1796{
1797 wchar_t save = wide_attr_test_string[0];
1798 int first = ((int) normal_wchar(save)) + adjust;
Steve Kondikae271bc2015-11-15 02:50:53 +01001799
1800 if (first >= ATTRSTRING_1ST) {
micky3879b9f5e72025-07-08 18:04:53 -04001801 int j, k;
1802
Steve Kondikae271bc2015-11-15 02:50:53 +01001803 for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1804 if (k > ATTRSTRING_END)
1805 break;
1806 wide_attr_test_string[j] = target_wchar(k);
1807 if (((k + 1 - first) % 5) == 0) {
1808 if (++j >= MAX_ATTRSTRING)
1809 break;
1810 wide_attr_test_string[j] = ' ';
1811 }
1812 }
1813 if ((LEN_ATTRSTRING - j) > 5) {
1814 wide_attr_test_string[0] = save;
1815 wide_adjust_attr_string(adjust - 1);
1816 } else {
1817 while (j < MAX_ATTRSTRING)
1818 wide_attr_test_string[j++] = ' ';
1819 wide_attr_test_string[j] = '\0';
1820 }
1821 }
1822}
1823
1824static void
1825wide_init_attr_string(void)
1826{
1827 use_fullwidth = FALSE;
1828 wide_attr_test_string[0] = (wchar_t) default_attr_string();
1829 wide_adjust_attr_string(0);
1830}
1831
1832static void
1833set_wide_background(NCURSES_PAIRS_T pair)
1834{
1835 cchar_t normal;
1836 wchar_t blank[2];
1837
1838 blank[0] = ' ';
1839 blank[1] = 0;
1840 setcchar(&normal, blank, A_NORMAL, pair, 0);
1841 bkgrnd(&normal);
1842 bkgrndset(&normal);
1843}
1844
1845static attr_t
1846get_wide_background(void)
1847{
micky3879b9f5e72025-07-08 18:04:53 -04001848 attr_t result = WA_NORMAL;
Steve Kondikae271bc2015-11-15 02:50:53 +01001849 attr_t attr;
1850 cchar_t ch;
1851 NCURSES_PAIRS_T pair;
Steve Kondikae271bc2015-11-15 02:50:53 +01001852
1853 memset(&ch, 0, sizeof(ch));
1854 if (getbkgrnd(&ch) != ERR) {
micky3879b9f5e72025-07-08 18:04:53 -04001855 wchar_t wch[CCHARW_MAX];
1856
Steve Kondikae271bc2015-11-15 02:50:53 +01001857 if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1858 result = attr;
1859 }
1860 }
1861 return result;
1862}
1863
1864static int
1865wide_show_attr(WINDOW *win,
1866 int row,
1867 int skip,
1868 bool arrow,
micky3879b9f5e72025-07-08 18:04:53 -04001869 attr_t attr,
Steve Kondikae271bc2015-11-15 02:50:53 +01001870 NCURSES_PAIRS_T pair,
1871 const char *name)
1872{
1873 int ncv = get_ncv();
micky3879b9f5e72025-07-08 18:04:53 -04001874 attr_t test = attr & ~WA_ALTCHARSET;
Steve Kondikae271bc2015-11-15 02:50:53 +01001875
1876 if (arrow)
micky3879b9f5e72025-07-08 18:04:53 -04001877 MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1878 MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
Steve Kondikae271bc2015-11-15 02:50:53 +01001879 MvPrintw(row, COL_ATTRSTRING - 1, "|");
1880 if (skip)
1881 printw("%*s", skip, " ");
1882
1883 /*
1884 * Just for testing, write text using the alternate character set one
1885 * character at a time (to pass its rendition directly), and use the
1886 * string operation for the other attributes.
1887 */
1888 wmove(win, 0, 0);
1889 werase(win);
1890 if (attr & WA_ALTCHARSET) {
1891 const wchar_t *s;
1892 cchar_t ch;
1893
1894 for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1895 wchar_t fill[2];
1896 fill[0] = *s;
1897 fill[1] = L'\0';
1898 setcchar(&ch, fill, attr, pair, 0);
1899 (void) wadd_wch(win, &ch);
1900 }
1901 } else {
1902 attr_t old_attr = 0;
1903 NCURSES_PAIRS_T old_pair = 0;
1904
1905 (void) (wattr_get) (win, &old_attr, &old_pair, 0);
1906 (void) wattr_set(win, attr, pair, 0);
1907 (void) waddwstr(win, wide_attr_test_string);
1908 (void) wattr_set(win, old_attr, old_pair, 0);
1909 }
1910 if (skip)
1911 printw("%*s", skip, " ");
1912 MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1913 if (test != A_NORMAL) {
1914 if (!(term_attrs() & test)) {
1915 printw(" (N/A)");
1916 } else {
1917 if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1918 static const attr_t table[] =
1919 {
1920 WA_STANDOUT,
1921 WA_UNDERLINE,
1922 WA_REVERSE,
1923 WA_BLINK,
1924 WA_DIM,
1925 WA_BOLD,
1926 WA_INVIS,
1927 WA_PROTECT,
1928 WA_ALTCHARSET
1929 };
1930 unsigned n;
1931 bool found = FALSE;
1932 for (n = 0; n < SIZEOF(table); n++) {
1933 if ((table[n] & attr) != 0
1934 && ((1 << n) & ncv) != 0) {
1935 found = TRUE;
1936 break;
1937 }
1938 }
1939 if (found)
1940 printw(" (NCV)");
1941 }
micky3879b9f5e72025-07-08 18:04:53 -04001942 if ((term_attrs() & test) != test) {
Steve Kondikae271bc2015-11-15 02:50:53 +01001943 printw(" (Part)");
micky3879b9f5e72025-07-08 18:04:53 -04001944 }
Steve Kondikae271bc2015-11-15 02:50:53 +01001945 }
1946 }
1947 return row + 2;
1948}
1949
1950static bool
1951wide_attr_getc(int *skip,
1952 NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg,
1953 NCURSES_COLOR_T *tx, int *ac,
1954 unsigned *kc, unsigned limit)
1955{
1956 bool result = TRUE;
1957 bool error = FALSE;
1958 WINDOW *helpwin;
1959
1960 do {
1961 int ch = Getchar();
1962
1963 error = FALSE;
1964 if (ch < 256 && isdigit(ch)) {
1965 *skip = (ch - '0');
1966 } else {
1967 switch (ch) {
1968 case CTRL('L'):
1969 Repaint();
1970 break;
micky3879b9f5e72025-07-08 18:04:53 -04001971 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +01001972 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1973 box_set(helpwin, 0, 0);
1974 attr_legend(helpwin);
1975 wGetchar(helpwin);
1976 delwin(helpwin);
1977 }
1978 break;
1979 case 'a':
1980 *ac = 0;
1981 break;
1982 case 'A':
1983 *ac = A_ALTCHARSET;
1984 break;
1985 case 'v':
1986 if (*kc == 0)
1987 *kc = limit - 1;
1988 else
1989 *kc -= 1;
1990 break;
1991 case 'V':
1992 *kc += 1;
1993 if (*kc >= limit)
1994 *kc = 0;
1995 break;
1996 case 'w':
1997 use_fullwidth = FALSE;
1998 wide_adjust_attr_string(0);
1999 break;
2000 case 'W':
2001 use_fullwidth = TRUE;
2002 wide_adjust_attr_string(0);
2003 break;
2004 case '<':
2005 wide_adjust_attr_string(-1);
2006 break;
2007 case '>':
2008 wide_adjust_attr_string(1);
2009 break;
2010 case case_QUIT:
2011 result = FALSE;
2012 break;
2013 default:
2014 error = cycle_color_attr(ch, fg, bg, tx);
2015 break;
2016 }
2017 }
2018 } while (error);
2019 return result;
2020}
2021
micky3879b9f5e72025-07-08 18:04:53 -04002022static int
2023x_attr_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01002024/* test text attributes using wide-character calls */
2025{
2026 int n;
2027 int skip = get_xmc();
2028 NCURSES_COLOR_T fg = COLOR_BLACK; /* color pair 0 is special */
2029 NCURSES_COLOR_T bg = COLOR_BLACK;
2030 NCURSES_COLOR_T tx = -1;
2031 int ac = 0;
micky3879b9f5e72025-07-08 18:04:53 -04002032 W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
2033 WINDOW *my_wins[SIZEOF(w_attrs_to_test)];
2034 unsigned my_size = init_w_attr_list(my_list, term_attrs());
Steve Kondikae271bc2015-11-15 02:50:53 +01002035
2036 if (my_size > 1) {
micky3879b9f5e72025-07-08 18:04:53 -04002037 unsigned j, k;
2038
Steve Kondikae271bc2015-11-15 02:50:53 +01002039 for (j = 0; j < my_size; ++j) {
2040 my_wins[j] = subwin(stdscr,
2041 1, LEN_ATTRSTRING,
2042 2 + (int) (2 * j), COL_ATTRSTRING);
2043 scrollok(my_wins[j], FALSE);
2044 }
2045
2046 if (skip < 0)
2047 skip = 0;
2048
2049 n = skip; /* make it easy */
2050 k = my_size - 1;
2051 wide_init_attr_string();
2052
2053 do {
2054 int row = 2;
2055 NCURSES_PAIRS_T pair = 0;
2056 NCURSES_PAIRS_T extras = 0;
2057
micky3879b9f5e72025-07-08 18:04:53 -04002058 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01002059 pair = (NCURSES_PAIRS_T) (fg != COLOR_BLACK || bg != COLOR_BLACK);
2060 if (pair != 0) {
2061 pair = 1;
2062 if (init_pair(pair, fg, bg) == ERR) {
2063 beep();
2064 }
2065 }
2066 extras = pair;
2067 if (tx >= 0) {
2068 extras = 2;
2069 if (init_pair(extras, tx, bg) == ERR) {
2070 beep();
2071 }
2072 }
2073 }
2074 set_wide_background(pair);
2075 erase();
2076
2077 box_set(stdscr, 0, 0);
2078 MvAddStr(0, 20, "Character attribute test display");
2079
2080 for (j = 0; j < my_size; ++j) {
2081 row = wide_show_attr(my_wins[j], row, n, (j == k),
2082 ((attr_t) ac |
2083 my_list[j].attr |
2084 my_list[k].attr),
2085 extras,
2086 my_list[j].name);
2087 }
2088
micky3879b9f5e72025-07-08 18:04:53 -04002089 MvPrintw(row, COLS_PRE_ATTRS,
Steve Kondikae271bc2015-11-15 02:50:53 +01002090 "This terminal does %shave the magic-cookie glitch",
2091 get_xmc() > -1 ? "" : "not ");
micky3879b9f5e72025-07-08 18:04:53 -04002092 MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
Steve Kondikae271bc2015-11-15 02:50:53 +01002093 show_color_attr(fg, bg, tx);
2094 printw(" ACS (%d)", ac != 0);
2095
2096 refresh();
2097 } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
2098
2099 set_wide_background(0);
2100 erase();
2101 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04002102 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01002103 } else {
2104 Cannot("does not support extended video attributes.");
micky3879b9f5e72025-07-08 18:04:53 -04002105 return ERR;
Steve Kondikae271bc2015-11-15 02:50:53 +01002106 }
2107}
2108#endif
2109
2110/****************************************************************************
2111 *
2112 * Color support tests
2113 *
2114 ****************************************************************************/
2115
2116static NCURSES_CONST char *the_color_names[] =
2117{
2118 "black",
2119 "red",
2120 "green",
2121 "yellow",
2122 "blue",
2123 "magenta",
2124 "cyan",
2125 "white",
2126 "BLACK",
2127 "RED",
2128 "GREEN",
2129 "YELLOW",
2130 "BLUE",
2131 "MAGENTA",
2132 "CYAN",
2133 "WHITE"
2134};
2135
2136static void
micky3879b9f5e72025-07-08 18:04:53 -04002137show_color_name(int y, int x, int color, bool wide, int zoom)
Steve Kondikae271bc2015-11-15 02:50:53 +01002138{
2139 if (move(y, x) != ERR) {
2140 char temp[80];
2141 int width = 8;
2142
micky3879b9f5e72025-07-08 18:04:53 -04002143 if (wide || zoom) {
2144 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2145 "%02d", color);
2146 if (wide)
2147 width = 4;
2148 if ((int) strlen(temp) >= width) {
2149 int pwr2 = 0;
2150 while ((1 << pwr2) < color)
2151 ++pwr2;
2152 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2153 width > 4 ? "2^%d" : "^%d", pwr2);
2154 }
Steve Kondikae271bc2015-11-15 02:50:53 +01002155 } else if (color >= 8) {
micky3879b9f5e72025-07-08 18:04:53 -04002156 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2157 "[%02d]", color);
Steve Kondikae271bc2015-11-15 02:50:53 +01002158 } else if (color < 0) {
micky3879b9f5e72025-07-08 18:04:53 -04002159 _nc_STRCPY(temp, "default", sizeof(temp));
Steve Kondikae271bc2015-11-15 02:50:53 +01002160 } else {
micky3879b9f5e72025-07-08 18:04:53 -04002161 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2162 "%.*s", 16, the_color_names[color]);
Steve Kondikae271bc2015-11-15 02:50:53 +01002163 }
2164 printw("%-*.*s", width, width, temp);
2165 }
2166}
2167
2168static void
2169color_legend(WINDOW *helpwin, bool wide)
2170{
2171 int row = 1;
2172 int col = 1;
2173
2174 MvWPrintw(helpwin, row++, col,
2175 "ESC to exit.");
2176 ++row;
2177 MvWPrintw(helpwin, row++, col,
2178 "Use up/down arrow to scroll through the display if it is");
2179 MvWPrintw(helpwin, row++, col,
2180 "longer than one screen. Control/N and Control/P can be used");
2181 MvWPrintw(helpwin, row++, col,
2182 "in place of up/down arrow. Use pageup/pagedown to scroll a");
2183 MvWPrintw(helpwin, row++, col,
2184 "full screen; control/B and control/F can be used here.");
2185 ++row;
2186 MvWPrintw(helpwin, row++, col,
2187 "Toggles:");
2188 MvWPrintw(helpwin, row++, col,
2189 " a/A toggle altcharset off/on");
2190 MvWPrintw(helpwin, row++, col,
2191 " b/B toggle bold off/on");
2192 if (has_colors()) {
2193 MvWPrintw(helpwin, row++, col,
2194 " c/C cycle used-colors through 8,16,...,COLORS");
2195 }
2196 MvWPrintw(helpwin, row++, col,
2197 " n/N toggle text/number on/off");
2198 MvWPrintw(helpwin, row++, col,
2199 " r/R toggle reverse on/off");
2200 MvWPrintw(helpwin, row++, col,
micky3879b9f5e72025-07-08 18:04:53 -04002201 " w/W switch width between 4/8 columns");
2202 MvWPrintw(helpwin, row++, col,
2203 " z/Z zoom out (or in)");
Steve Kondikae271bc2015-11-15 02:50:53 +01002204#if USE_WIDEC_SUPPORT
2205 if (wide) {
2206 MvWPrintw(helpwin, row++, col,
2207 "Wide characters:");
2208 MvWPrintw(helpwin, row, col,
2209 " x/X toggle text between ASCII and wide-character");
2210 }
2211#else
2212 (void) wide;
2213#endif
2214}
2215
2216#define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
2217
2218static int
2219color_cycle(int current, int step)
2220{
2221 int result = current;
2222 if (step < 0) {
2223 if (current <= 8) {
2224 result = COLORS;
2225 } else {
2226 result = 8;
2227 if ((result * 2) > COLORS) {
2228 result = COLORS;
2229 } else {
2230 while ((result * 2) < current) {
2231 result *= 2;
2232 }
2233 }
2234 }
2235 } else {
2236 if (current >= COLORS) {
2237 result = 8;
2238 } else {
2239 result *= 2;
2240 }
2241 if (result > COLORS)
2242 result = COLORS;
2243 }
2244 return result;
2245}
2246
2247/* generate a color test pattern */
micky3879b9f5e72025-07-08 18:04:53 -04002248static int
2249color_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01002250{
2251 NCURSES_PAIRS_T i;
2252 int top = 0, width;
2253 int base_row = 0;
2254 int grid_top = top + 3;
2255 int page_size = (LINES - grid_top);
2256 int pairs_max;
2257 int colors_max = COLORS;
micky3879b9f5e72025-07-08 18:04:53 -04002258 int col_limit;
Steve Kondikae271bc2015-11-15 02:50:53 +01002259 int row_limit;
2260 int per_row;
micky3879b9f5e72025-07-08 18:04:53 -04002261 char *numbered = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01002262 const char *hello;
2263 bool done = FALSE;
2264 bool opt_acsc = FALSE;
2265 bool opt_bold = FALSE;
2266 bool opt_revs = FALSE;
2267 bool opt_nums = FALSE;
2268 bool opt_wide = FALSE;
micky3879b9f5e72025-07-08 18:04:53 -04002269 int opt_zoom = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01002270 WINDOW *helpwin;
2271
micky3879b9f5e72025-07-08 18:04:53 -04002272 if (!UseColors) {
2273 Cannot("does not support color.");
2274 return ERR;
2275 }
2276
2277 numbered = typeCalloc(char, COLS + 1);
2278 done = ((COLS < 16) || (numbered == 0));
2279
2280 /*
2281 * Because the number of colors is usually a power of two, we also use
2282 * a power of two for the number of colors shown per line (to be tidy).
2283 */
2284 for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2285
2286 reloop:
Steve Kondikae271bc2015-11-15 02:50:53 +01002287 while (!done) {
2288 int shown = 0;
micky3879b9f5e72025-07-08 18:04:53 -04002289 int zoom_size = (1 << opt_zoom);
2290 int colors_max1 = colors_max / zoom_size;
2291 double colors_max2 = (double) colors_max1 * (double) colors_max1;
Steve Kondikae271bc2015-11-15 02:50:53 +01002292
2293 pairs_max = PAIR_NUMBER(A_COLOR) + 1;
micky3879b9f5e72025-07-08 18:04:53 -04002294 if (colors_max2 <= COLOR_PAIRS) {
2295 int limit = (colors_max1 - MinColors) * (colors_max1 - MinColors);
Steve Kondikae271bc2015-11-15 02:50:53 +01002296 if (pairs_max > limit)
2297 pairs_max = limit;
Steve Kondikae271bc2015-11-15 02:50:53 +01002298 }
micky3879b9f5e72025-07-08 18:04:53 -04002299 if (pairs_max > COLOR_PAIRS)
2300 pairs_max = COLOR_PAIRS;
2301 if (pairs_max < colors_max1)
2302 pairs_max = colors_max1;
Steve Kondikae271bc2015-11-15 02:50:53 +01002303
2304 /* this assumes an 80-column line */
2305 if (opt_wide) {
2306 width = 4;
2307 hello = "Test";
micky3879b9f5e72025-07-08 18:04:53 -04002308 per_row = (col_limit / ((colors_max1 > 8) ? width : 8));
Steve Kondikae271bc2015-11-15 02:50:53 +01002309 } else {
2310 width = 8;
2311 hello = "Hello";
micky3879b9f5e72025-07-08 18:04:53 -04002312 per_row = (col_limit / width);
Steve Kondikae271bc2015-11-15 02:50:53 +01002313 }
micky3879b9f5e72025-07-08 18:04:53 -04002314 per_row -= MinColors;
Steve Kondikae271bc2015-11-15 02:50:53 +01002315
2316 row_limit = (pairs_max + per_row - 1) / per_row;
2317
2318 move(0, 0);
2319 (void) printw("There are %d color pairs and %d colors",
2320 pairs_max, COLORS);
micky3879b9f5e72025-07-08 18:04:53 -04002321 if (colors_max1 != COLORS)
2322 (void) printw(" (using %d colors)", colors_max1);
2323 if (MinColors)
Steve Kondikae271bc2015-11-15 02:50:53 +01002324 (void) addstr(" besides 'default'");
micky3879b9f5e72025-07-08 18:04:53 -04002325 if (opt_zoom)
2326 (void) printw(" zoom:%d", opt_zoom);
Steve Kondikae271bc2015-11-15 02:50:53 +01002327
2328 clrtobot();
2329 MvPrintw(top + 1, 0,
2330 "%dx%d matrix of foreground/background colors, bold *%s*\n",
2331 row_limit,
2332 per_row,
2333 opt_bold ? "on" : "off");
2334
2335 /* show color names/numbers across the top */
micky3879b9f5e72025-07-08 18:04:53 -04002336 for (i = 0; i < per_row; i++) {
2337 show_color_name(top + 2,
2338 (i + 1) * width,
2339 (int) i * zoom_size + MinColors,
2340 opt_wide,
2341 opt_zoom);
2342 }
Steve Kondikae271bc2015-11-15 02:50:53 +01002343
2344 /* show a grid of colors, with color names/ numbers on the left */
2345 for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
2346 int row = grid_top + (i / per_row) - base_row;
2347 int col = (i % per_row + 1) * width;
2348 NCURSES_PAIRS_T pair = i;
2349
micky3879b9f5e72025-07-08 18:04:53 -04002350 if ((i / per_row) > row_limit)
2351 break;
2352
2353#define InxToFG(i) (int)((((unsigned long)(i) * (unsigned long)zoom_size) % (unsigned long)(colors_max1 - MinColors)) + (unsigned long)MinColors)
2354#define InxToBG(i) (int)((((unsigned long)(i) * (unsigned long)zoom_size) / (unsigned long)(colors_max1 - MinColors)) + (unsigned long)MinColors)
Steve Kondikae271bc2015-11-15 02:50:53 +01002355 if (row >= 0 && move(row, col) != ERR) {
micky3879b9f5e72025-07-08 18:04:53 -04002356 NCURSES_COLOR_T fg = (NCURSES_COLOR_T) InxToFG(i);
2357 NCURSES_COLOR_T bg = (NCURSES_COLOR_T) InxToBG(i);
Steve Kondikae271bc2015-11-15 02:50:53 +01002358
2359 init_pair(pair, fg, bg);
2360 attron(COLOR_PAIR(pair));
2361 if (opt_acsc)
2362 attron(A_ALTCHARSET);
2363 if (opt_bold)
2364 attron(A_BOLD);
2365 if (opt_revs)
2366 attron(A_REVERSE);
2367
2368 if (opt_nums) {
micky3879b9f5e72025-07-08 18:04:53 -04002369 _nc_SPRINTF(numbered, _nc_SLIMIT((size_t) (COLS + 1))
2370 "{%02X}", (int) i);
Steve Kondikae271bc2015-11-15 02:50:53 +01002371 hello = numbered;
2372 }
2373 printw("%-*.*s", width, width, hello);
2374 (void) attrset(A_NORMAL);
2375
micky3879b9f5e72025-07-08 18:04:53 -04002376 if ((i % per_row) == 0 && InxToFG(i) == MinColors) {
2377 show_color_name(row, 0,
2378 InxToBG(i),
2379 opt_wide,
2380 opt_zoom);
Steve Kondikae271bc2015-11-15 02:50:53 +01002381 }
2382 ++shown;
2383 } else if (shown) {
2384 break;
2385 }
2386 }
2387
2388 switch (wGetchar(stdscr)) {
2389 case 'a':
2390 opt_acsc = FALSE;
2391 break;
2392 case 'A':
2393 opt_acsc = TRUE;
2394 break;
2395 case 'b':
2396 opt_bold = FALSE;
2397 break;
2398 case 'B':
2399 opt_bold = TRUE;
2400 break;
2401 case 'c':
2402 colors_max = color_cycle(colors_max, -1);
2403 break;
2404 case 'C':
2405 colors_max = color_cycle(colors_max, 1);
2406 break;
2407 case 'n':
2408 opt_nums = FALSE;
2409 break;
2410 case 'N':
2411 opt_nums = TRUE;
2412 break;
2413 case 'r':
2414 opt_revs = FALSE;
2415 break;
2416 case 'R':
2417 opt_revs = TRUE;
2418 break;
2419 case case_QUIT:
2420 done = TRUE;
2421 continue;
2422 case 'w':
2423 set_color_test(opt_wide, FALSE);
2424 break;
2425 case 'W':
2426 set_color_test(opt_wide, TRUE);
2427 break;
micky3879b9f5e72025-07-08 18:04:53 -04002428 case 'z':
2429 if (opt_zoom <= 0) {
2430 beep();
2431 } else {
2432 --opt_zoom;
2433 goto reloop;
2434 }
2435 break;
2436 case 'Z':
2437 if ((1 << opt_zoom) >= colors_max) {
2438 beep();
2439 } else {
2440 ++opt_zoom;
2441 goto reloop;
2442 }
2443 break;
Steve Kondikae271bc2015-11-15 02:50:53 +01002444 case CTRL('p'):
2445 case KEY_UP:
2446 if (base_row <= 0) {
2447 beep();
2448 } else {
2449 base_row -= 1;
2450 }
2451 break;
2452 case CTRL('n'):
2453 case KEY_DOWN:
2454 if (base_row + page_size >= row_limit) {
2455 beep();
2456 } else {
2457 base_row += 1;
2458 }
2459 break;
2460 case CTRL('b'):
2461 case KEY_PREVIOUS:
2462 case KEY_PPAGE:
2463 if (base_row <= 0) {
2464 beep();
2465 } else {
2466 base_row -= (page_size - 1);
2467 if (base_row < 0)
2468 base_row = 0;
2469 }
2470 break;
2471 case CTRL('f'):
2472 case KEY_NEXT:
2473 case KEY_NPAGE:
2474 if (base_row + page_size >= row_limit) {
2475 beep();
2476 } else {
2477 base_row += page_size - 1;
2478 if (base_row + page_size >= row_limit) {
2479 base_row = row_limit - page_size - 1;
2480 }
2481 }
2482 break;
micky3879b9f5e72025-07-08 18:04:53 -04002483 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +01002484 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2485 box(helpwin, 0, 0);
2486 color_legend(helpwin, FALSE);
2487 wGetchar(helpwin);
2488 delwin(helpwin);
2489 }
2490 break;
2491 default:
2492 beep();
2493 continue;
2494 }
2495 }
2496
2497 erase();
2498 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04002499
2500 free(numbered);
2501 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01002502}
2503
2504#if USE_WIDEC_SUPPORT
micky3879b9f5e72025-07-08 18:04:53 -04002505
2506#if USE_EXTENDED_COLOR
2507#define InitExtendedPair(p,f,g) init_extended_pair((p),(f),(g))
2508#define ExtendedColorSet(p) color_set((NCURSES_PAIRS_T) (p), &(p))
2509#define EXTENDED_PAIRS_T int
2510#else
2511#define InitExtendedPair(p,f,g) init_pair((NCURSES_PAIRS_T) (p),(NCURSES_COLOR_T)(f),(NCURSES_COLOR_T)(g))
2512#define ExtendedColorSet(p) color_set((NCURSES_PAIRS_T) (p), NULL)
2513#define EXTENDED_PAIRS_T NCURSES_PAIRS_T
2514#endif
2515
Steve Kondikae271bc2015-11-15 02:50:53 +01002516/* generate a color test pattern */
micky3879b9f5e72025-07-08 18:04:53 -04002517static int
2518x_color_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01002519{
micky3879b9f5e72025-07-08 18:04:53 -04002520 long i;
Steve Kondikae271bc2015-11-15 02:50:53 +01002521 int top = 0, width;
2522 int base_row = 0;
2523 int grid_top = top + 3;
2524 int page_size = (LINES - grid_top);
micky3879b9f5e72025-07-08 18:04:53 -04002525 int pairs_max;
Steve Kondikae271bc2015-11-15 02:50:53 +01002526 int colors_max = COLORS;
micky3879b9f5e72025-07-08 18:04:53 -04002527 int col_limit;
Steve Kondikae271bc2015-11-15 02:50:53 +01002528 int row_limit;
2529 int per_row;
micky3879b9f5e72025-07-08 18:04:53 -04002530 char *numbered = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01002531 const char *hello;
2532 bool done = FALSE;
2533 bool opt_acsc = FALSE;
2534 bool opt_bold = FALSE;
2535 bool opt_revs = FALSE;
2536 bool opt_wide = FALSE;
2537 bool opt_nums = FALSE;
2538 bool opt_xchr = FALSE;
micky3879b9f5e72025-07-08 18:04:53 -04002539 int opt_zoom = 0;
2540 wchar_t *buffer = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01002541 WINDOW *helpwin;
2542
micky3879b9f5e72025-07-08 18:04:53 -04002543 if (!UseColors) {
2544 Cannot("does not support color.");
2545 return ERR;
2546 }
2547 numbered = typeCalloc(char, COLS + 1);
2548 buffer = typeCalloc(wchar_t, COLS + 1);
2549 done = ((COLS < 16) || (numbered == 0) || (buffer == 0));
2550
2551 /*
2552 * Because the number of colors is usually a power of two, we also use
2553 * a power of two for the number of colors shown per line (to be tidy).
2554 */
2555 for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2556
2557 reloop:
Steve Kondikae271bc2015-11-15 02:50:53 +01002558 while (!done) {
2559 int shown = 0;
micky3879b9f5e72025-07-08 18:04:53 -04002560 int zoom_size = (1 << opt_zoom);
2561 int colors_max1 = colors_max / zoom_size;
2562 double colors_max2 = (double) colors_max1 * (double) colors_max1;
Steve Kondikae271bc2015-11-15 02:50:53 +01002563
micky3879b9f5e72025-07-08 18:04:53 -04002564 pairs_max = ((unsigned) (-1)) / 2;
2565 if (colors_max2 <= COLOR_PAIRS) {
2566 int limit = (colors_max1 - MinColors) * (colors_max1 - MinColors);
Steve Kondikae271bc2015-11-15 02:50:53 +01002567 if (pairs_max > limit)
2568 pairs_max = limit;
Steve Kondikae271bc2015-11-15 02:50:53 +01002569 }
micky3879b9f5e72025-07-08 18:04:53 -04002570 if (pairs_max > COLOR_PAIRS)
2571 pairs_max = COLOR_PAIRS;
2572 if (pairs_max < colors_max1)
2573 pairs_max = colors_max1;
Steve Kondikae271bc2015-11-15 02:50:53 +01002574
Steve Kondikae271bc2015-11-15 02:50:53 +01002575 if (opt_wide) {
2576 width = 4;
2577 hello = "Test";
micky3879b9f5e72025-07-08 18:04:53 -04002578 per_row = (col_limit / ((colors_max1 > 8) ? width : 8));
Steve Kondikae271bc2015-11-15 02:50:53 +01002579 } else {
2580 width = 8;
2581 hello = "Hello";
micky3879b9f5e72025-07-08 18:04:53 -04002582 per_row = (col_limit / width);
Steve Kondikae271bc2015-11-15 02:50:53 +01002583 }
micky3879b9f5e72025-07-08 18:04:53 -04002584 per_row -= MinColors;
Steve Kondikae271bc2015-11-15 02:50:53 +01002585
2586 if (opt_xchr) {
2587 make_fullwidth_text(buffer, hello);
2588 width *= 2;
2589 per_row /= 2;
2590 } else {
2591 make_narrow_text(buffer, hello);
2592 }
2593
2594 row_limit = (pairs_max + per_row - 1) / per_row;
2595
2596 move(0, 0);
2597 (void) printw("There are %d color pairs and %d colors",
2598 pairs_max, COLORS);
micky3879b9f5e72025-07-08 18:04:53 -04002599 if (colors_max1 != COLORS)
2600 (void) printw(" (using %d colors)", colors_max1);
2601 if (MinColors)
Steve Kondikae271bc2015-11-15 02:50:53 +01002602 (void) addstr(" besides 'default'");
micky3879b9f5e72025-07-08 18:04:53 -04002603 if (opt_zoom)
2604 (void) printw(" zoom:%d", opt_zoom);
Steve Kondikae271bc2015-11-15 02:50:53 +01002605
2606 clrtobot();
2607 MvPrintw(top + 1, 0,
2608 "%dx%d matrix of foreground/background colors, bold *%s*\n",
2609 row_limit,
2610 per_row,
2611 opt_bold ? "on" : "off");
2612
2613 /* show color names/numbers across the top */
micky3879b9f5e72025-07-08 18:04:53 -04002614 for (i = 0; i < per_row; i++) {
2615 show_color_name(top + 2,
2616 ((int) i + 1) * width,
2617 (int) i * zoom_size + MinColors,
2618 opt_wide,
2619 opt_zoom);
2620 }
Steve Kondikae271bc2015-11-15 02:50:53 +01002621
2622 /* show a grid of colors, with color names/ numbers on the left */
2623 for (i = (base_row * per_row); i < pairs_max; i++) {
micky3879b9f5e72025-07-08 18:04:53 -04002624 int row = grid_top + ((int) i / per_row) - base_row;
2625 int col = ((int) i % per_row + 1) * width;
2626 int pair = (int) i;
2627
2628 if ((i / per_row) > row_limit)
2629 break;
Steve Kondikae271bc2015-11-15 02:50:53 +01002630
2631 if (row >= 0 && move(row, col) != ERR) {
micky3879b9f5e72025-07-08 18:04:53 -04002632 InitExtendedPair(pair, InxToFG(i), InxToBG(i));
2633 (void) ExtendedColorSet(pair);
Steve Kondikae271bc2015-11-15 02:50:53 +01002634 if (opt_acsc)
micky3879b9f5e72025-07-08 18:04:53 -04002635 attr_on(WA_ALTCHARSET, NULL);
Steve Kondikae271bc2015-11-15 02:50:53 +01002636 if (opt_bold)
micky3879b9f5e72025-07-08 18:04:53 -04002637 attr_on(WA_BOLD, NULL);
Steve Kondikae271bc2015-11-15 02:50:53 +01002638 if (opt_revs)
micky3879b9f5e72025-07-08 18:04:53 -04002639 attr_on(WA_REVERSE, NULL);
Steve Kondikae271bc2015-11-15 02:50:53 +01002640
2641 if (opt_nums) {
micky3879b9f5e72025-07-08 18:04:53 -04002642 _nc_SPRINTF(numbered,
2643 _nc_SLIMIT((size_t) (COLS + 1) * sizeof(wchar_t))
2644 "{%02X}", (unsigned) i);
Steve Kondikae271bc2015-11-15 02:50:53 +01002645 if (opt_xchr) {
2646 make_fullwidth_text(buffer, numbered);
2647 } else {
2648 make_narrow_text(buffer, numbered);
2649 }
2650 }
2651 addnwstr(buffer, width);
2652 (void) attr_set(A_NORMAL, 0, NULL);
2653
micky3879b9f5e72025-07-08 18:04:53 -04002654 if ((i % per_row) == 0 && InxToFG(i) == MinColors) {
2655 show_color_name(row, 0,
2656 InxToBG(i),
2657 opt_wide,
2658 opt_zoom);
Steve Kondikae271bc2015-11-15 02:50:53 +01002659 }
2660 ++shown;
2661 } else if (shown) {
2662 break;
2663 }
2664 }
2665
2666 switch (wGetchar(stdscr)) {
2667 case 'a':
2668 opt_acsc = FALSE;
2669 break;
2670 case 'A':
2671 opt_acsc = TRUE;
2672 break;
2673 case 'b':
2674 opt_bold = FALSE;
2675 break;
2676 case 'B':
2677 opt_bold = TRUE;
2678 break;
2679 case 'c':
2680 colors_max = color_cycle(colors_max, -1);
2681 break;
2682 case 'C':
2683 colors_max = color_cycle(colors_max, 1);
2684 break;
2685 case 'n':
2686 opt_nums = FALSE;
2687 break;
2688 case 'N':
2689 opt_nums = TRUE;
2690 break;
2691 case 'r':
2692 opt_revs = FALSE;
2693 break;
2694 case 'R':
2695 opt_revs = TRUE;
2696 break;
2697 case case_QUIT:
2698 done = TRUE;
2699 continue;
2700 case 'w':
2701 set_color_test(opt_wide, FALSE);
2702 break;
2703 case 'W':
2704 set_color_test(opt_wide, TRUE);
2705 break;
2706 case 'x':
2707 opt_xchr = FALSE;
2708 break;
2709 case 'X':
2710 opt_xchr = TRUE;
2711 break;
micky3879b9f5e72025-07-08 18:04:53 -04002712 case 'z':
2713 if (opt_zoom <= 0) {
2714 beep();
2715 } else {
2716 --opt_zoom;
2717 goto reloop;
2718 }
2719 break;
2720 case 'Z':
2721 if ((1 << opt_zoom) >= colors_max) {
2722 beep();
2723 } else {
2724 ++opt_zoom;
2725 goto reloop;
2726 }
2727 break;
Steve Kondikae271bc2015-11-15 02:50:53 +01002728 case CTRL('p'):
2729 case KEY_UP:
2730 if (base_row <= 0) {
2731 beep();
2732 } else {
2733 base_row -= 1;
2734 }
2735 break;
2736 case CTRL('n'):
2737 case KEY_DOWN:
2738 if (base_row + page_size >= row_limit) {
2739 beep();
2740 } else {
2741 base_row += 1;
2742 }
2743 break;
2744 case CTRL('b'):
2745 case KEY_PREVIOUS:
2746 case KEY_PPAGE:
2747 if (base_row <= 0) {
2748 beep();
2749 } else {
2750 base_row -= (page_size - 1);
2751 if (base_row < 0)
2752 base_row = 0;
2753 }
2754 break;
2755 case CTRL('f'):
2756 case KEY_NEXT:
2757 case KEY_NPAGE:
2758 if (base_row + page_size >= row_limit) {
2759 beep();
2760 } else {
2761 base_row += page_size - 1;
2762 if (base_row + page_size >= row_limit) {
2763 base_row = row_limit - page_size - 1;
2764 }
2765 }
2766 break;
micky3879b9f5e72025-07-08 18:04:53 -04002767 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +01002768 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2769 box(helpwin, 0, 0);
2770 color_legend(helpwin, TRUE);
2771 wGetchar(helpwin);
2772 delwin(helpwin);
2773 }
2774 break;
2775 default:
2776 beep();
2777 continue;
2778 }
2779 }
2780
2781 erase();
2782 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04002783
2784 free(numbered);
2785 free(buffer);
2786 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01002787}
2788#endif /* USE_WIDEC_SUPPORT */
2789
micky3879b9f5e72025-07-08 18:04:53 -04002790#if HAVE_COLOR_CONTENT
Steve Kondikae271bc2015-11-15 02:50:53 +01002791static void
2792change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
2793{
2794 NCURSES_COLOR_T red, green, blue;
2795
2796 color_content(current, &red, &green, &blue);
2797
2798 switch (field) {
2799 case 0:
2800 red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
2801 break;
2802 case 1:
2803 green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
2804 break;
2805 case 2:
2806 blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
2807 break;
2808 }
2809
2810 if (init_color(current, red, green, blue) == ERR)
2811 beep();
2812}
2813
2814static void
micky3879b9f5e72025-07-08 18:04:53 -04002815reset_all_colors(void)
Steve Kondikae271bc2015-11-15 02:50:53 +01002816{
2817 NCURSES_PAIRS_T c;
2818
2819 for (c = 0; c < COLORS; ++c)
2820 init_color(c,
2821 all_colors[c].red,
2822 all_colors[c].green,
2823 all_colors[c].blue);
2824}
2825
micky3879b9f5e72025-07-08 18:04:53 -04002826#define okCOLOR(n) ((n) >= 0 && (n) < MaxColors)
2827#define okRGB(n) ((n) >= 0 && (n) <= 1000)
2828#define DecodeRGB(n) (NCURSES_COLOR_T) ((n * 1000) / 0xffff)
Steve Kondikae271bc2015-11-15 02:50:53 +01002829
2830static void
micky3879b9f5e72025-07-08 18:04:53 -04002831init_all_colors(bool xterm_colors, char *palette_file)
2832{
2833 NCURSES_PAIRS_T cp;
2834 all_colors = typeMalloc(RGB_DATA, (unsigned) MaxColors);
2835 if (!all_colors)
2836 failed("all_colors");
2837 for (cp = 0; cp < MaxColors; ++cp) {
2838 color_content(cp,
2839 &all_colors[cp].red,
2840 &all_colors[cp].green,
2841 &all_colors[cp].blue);
2842 }
2843 /* xterm and compatible terminals can read results of an OSC string
2844 * asking for the current color palette.
2845 */
2846 if (xterm_colors) {
2847 int n;
2848 char result[BUFSIZ];
2849 int check_n;
2850 unsigned check_r, check_g, check_b;
2851
2852 raw();
2853 noecho();
2854
2855 for (n = 0; n < MaxColors; ++n) {
2856 int got;
2857
2858 fprintf(stderr, "\033]4;%d;?\007", n);
2859 got = (int) read(0, result, sizeof(result) - 1);
2860 if (got < 0)
2861 break;
2862 result[got] = '\0';
2863 if (sscanf(result, "\033]4;%d;rgb:%x/%x/%x\007",
2864 &check_n,
2865 &check_r,
2866 &check_g,
2867 &check_b) == 4 &&
2868 check_n == n) {
2869 all_colors[n].red = DecodeRGB(check_r);
2870 all_colors[n].green = DecodeRGB(check_g);
2871 all_colors[n].blue = DecodeRGB(check_b);
2872 } else {
2873 break;
2874 }
2875 }
2876 reset_prog_mode();
2877 }
2878 if (palette_file != 0) {
2879 FILE *fp = fopen(palette_file, "r");
2880 if (fp != 0) {
2881 char buffer[BUFSIZ];
2882 int red, green, blue;
2883 int scale = 1000;
2884 int c;
2885 while (fgets(buffer, sizeof(buffer), fp) != 0) {
2886 if (sscanf(buffer, "scale:%d", &c) == 1) {
2887 scale = c;
2888 if (scale < 100)
2889 scale = 100;
2890 if (scale > 1000)
2891 scale = 1000;
2892 } else if (sscanf(buffer, "%d:%d %d %d",
2893 &c,
2894 &red,
2895 &green,
2896 &blue) == 4
2897 && okCOLOR(c)
2898 && okRGB(red)
2899 && okRGB(green)
2900 && okRGB(blue)) {
2901#define Scaled(n) (NCURSES_COLOR_T) (((n) * 1000) / scale)
2902 all_colors[c].red = Scaled(red);
2903 all_colors[c].green = Scaled(green);
2904 all_colors[c].blue = Scaled(blue);
2905 }
2906 }
2907 fclose(fp);
2908 }
2909 }
2910}
2911
2912#define scaled_rgb(n) ((255 * (n)) / 1000)
2913
2914static int
2915color_edit(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01002916/* display the color test pattern, without trying to edit colors */
2917{
2918 int i;
micky3879b9f5e72025-07-08 18:04:53 -04002919 int current;
2920 int this_c, value, field;
Steve Kondikae271bc2015-11-15 02:50:53 +01002921 int last_c;
micky3879b9f5e72025-07-08 18:04:53 -04002922 int top_color;
2923 int page_size;
Steve Kondikae271bc2015-11-15 02:50:53 +01002924
micky3879b9f5e72025-07-08 18:04:53 -04002925 if (!UseColors) {
2926 Cannot("does not support color.");
2927 return ERR;
2928 } else if (!can_change_color()) {
2929 Cannot("has hardwired color values.");
2930 return ERR;
2931 }
Steve Kondikae271bc2015-11-15 02:50:53 +01002932
micky3879b9f5e72025-07-08 18:04:53 -04002933 reset_all_colors();
2934#ifdef KEY_RESIZE
2935 retry:
2936#endif
2937 current = 0;
2938 this_c = 0;
2939 value = 0;
2940 field = 0;
2941 top_color = 0;
2942 page_size = (LINES - 6);
2943 erase();
2944
2945 for (i = 0; i < MaxColors; i++)
Steve Kondikae271bc2015-11-15 02:50:53 +01002946 init_pair((NCURSES_PAIRS_T) i,
2947 (NCURSES_COLOR_T) COLOR_WHITE,
2948 (NCURSES_COLOR_T) i);
2949
2950 MvPrintw(LINES - 2, 0, "Number: %d", value);
2951
2952 do {
2953 NCURSES_COLOR_T red, green, blue;
2954
2955 attron(A_BOLD);
2956 MvAddStr(0, 20, "Color RGB Value Editing");
2957 attroff(A_BOLD);
2958
2959 for (i = (NCURSES_COLOR_T) top_color;
2960 (i - top_color < page_size)
micky3879b9f5e72025-07-08 18:04:53 -04002961 && (i < MaxColors); i++) {
Steve Kondikae271bc2015-11-15 02:50:53 +01002962 char numeric[80];
2963
micky3879b9f5e72025-07-08 18:04:53 -04002964 _nc_SPRINTF(numeric, _nc_SLIMIT(sizeof(numeric)) "[%d]", i);
Steve Kondikae271bc2015-11-15 02:50:53 +01002965 MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2966 (i == current ? '>' : ' '),
2967 (i < (int) SIZEOF(the_color_names)
2968 ? the_color_names[i] : numeric));
2969 (void) attrset(AttrArg(COLOR_PAIR(i), 0));
2970 addstr(" ");
2971 (void) attrset(A_NORMAL);
2972
2973 color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
2974 addstr(" R = ");
2975 if (current == i && field == 0)
2976 attron(A_STANDOUT);
2977 printw("%04d", (int) red);
2978 if (current == i && field == 0)
2979 (void) attrset(A_NORMAL);
2980 addstr(", G = ");
2981 if (current == i && field == 1)
2982 attron(A_STANDOUT);
2983 printw("%04d", (int) green);
2984 if (current == i && field == 1)
2985 (void) attrset(A_NORMAL);
2986 addstr(", B = ");
2987 if (current == i && field == 2)
2988 attron(A_STANDOUT);
2989 printw("%04d", (int) blue);
2990 if (current == i && field == 2)
2991 (void) attrset(A_NORMAL);
2992 (void) attrset(A_NORMAL);
2993 printw(" ( %3d %3d %3d )",
2994 (int) scaled_rgb(red),
2995 (int) scaled_rgb(green),
2996 (int) scaled_rgb(blue));
2997 }
2998
2999 MvAddStr(LINES - 3, 0,
3000 "Use up/down to select a color, left/right to change fields.");
3001 MvAddStr(LINES - 2, 0,
3002 "Modify field by typing nnn=, nnn-, or nnn+. ? for help.");
3003
3004 move(2 + current - top_color, 0);
3005
3006 last_c = this_c;
3007 this_c = Getchar();
3008 if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
3009 value = 0;
3010
3011 switch (this_c) {
micky3879b9f5e72025-07-08 18:04:53 -04003012#ifdef KEY_RESIZE
3013 case KEY_RESIZE:
3014 move(0, 0);
3015 goto retry;
3016#endif
3017 case '!':
3018 ShellOut(FALSE);
3019 /* FALLTHRU */
3020 case CTRL('r'):
3021 endwin();
3022 refresh();
3023 break;
3024 case CTRL('l'):
3025 refresh();
3026 break;
Steve Kondikae271bc2015-11-15 02:50:53 +01003027 case CTRL('b'):
3028 case KEY_PPAGE:
3029 if (current > 0)
3030 current -= (page_size - 1);
3031 else
3032 beep();
3033 break;
3034
3035 case CTRL('f'):
3036 case KEY_NPAGE:
micky3879b9f5e72025-07-08 18:04:53 -04003037 if (current < (MaxColors - 1))
Steve Kondikae271bc2015-11-15 02:50:53 +01003038 current += (page_size - 1);
3039 else
3040 beep();
3041 break;
3042
3043 case CTRL('p'):
3044 case KEY_UP:
micky3879b9f5e72025-07-08 18:04:53 -04003045 current = (current == 0 ? (MaxColors - 1) : current - 1);
Steve Kondikae271bc2015-11-15 02:50:53 +01003046 break;
3047
3048 case CTRL('n'):
3049 case KEY_DOWN:
micky3879b9f5e72025-07-08 18:04:53 -04003050 current = (current == (MaxColors - 1) ? 0 : current + 1);
Steve Kondikae271bc2015-11-15 02:50:53 +01003051 break;
3052
micky3879b9f5e72025-07-08 18:04:53 -04003053 case '\t':
Steve Kondikae271bc2015-11-15 02:50:53 +01003054 case KEY_RIGHT:
3055 field = (field == 2 ? 0 : field + 1);
3056 break;
3057
micky3879b9f5e72025-07-08 18:04:53 -04003058 case KEY_BTAB:
Steve Kondikae271bc2015-11-15 02:50:53 +01003059 case KEY_LEFT:
3060 field = (field == 0 ? 2 : field - 1);
3061 break;
3062
3063 case '0':
3064 case '1':
3065 case '2':
3066 case '3':
3067 case '4':
3068 case '5':
3069 case '6':
3070 case '7':
3071 case '8':
3072 case '9':
3073 value = value * 10 + (this_c - '0');
3074 break;
3075
3076 case '+':
3077 change_color((NCURSES_PAIRS_T) current, field, value, 1);
3078 break;
3079
3080 case '-':
3081 change_color((NCURSES_PAIRS_T) current, field, -value, 1);
3082 break;
3083
3084 case '=':
3085 change_color((NCURSES_PAIRS_T) current, field, value, 0);
3086 break;
3087
micky3879b9f5e72025-07-08 18:04:53 -04003088 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +01003089 erase();
3090 P(" RGB Value Editing Help");
3091 P("");
3092 P("You are in the RGB value editor. Use the arrow keys to select one of");
3093 P("the fields in one of the RGB triples of the current colors; the one");
3094 P("currently selected will be reverse-video highlighted.");
3095 P("");
3096 P("To change a field, enter the digits of the new value; they are echoed");
3097 P("as entered. Finish by typing `='. The change will take effect instantly.");
3098 P("To increment or decrement a value, use the same procedure, but finish");
3099 P("with a `+' or `-'.");
3100 P("");
micky3879b9f5e72025-07-08 18:04:53 -04003101 P("Use `!' to shell-out, ^R or ^L to repaint the screen.");
3102 P("");
Steve Kondikae271bc2015-11-15 02:50:53 +01003103 P("Press 'm' to invoke the top-level menu with the current color settings.");
3104 P("To quit, do ESC");
3105
3106 Pause();
3107 erase();
3108 break;
3109
3110 case 'm':
3111 endwin();
3112 main_menu(FALSE);
micky3879b9f5e72025-07-08 18:04:53 -04003113 for (i = 0; i < MaxColors; i++)
Steve Kondikae271bc2015-11-15 02:50:53 +01003114 init_pair((NCURSES_PAIRS_T) i,
3115 (NCURSES_COLOR_T) COLOR_WHITE,
3116 (NCURSES_COLOR_T) i);
3117 refresh();
3118 break;
3119
3120 case case_QUIT:
3121 break;
3122
3123 default:
3124 beep();
3125 break;
3126 }
3127
3128 if (current < 0)
3129 current = 0;
micky3879b9f5e72025-07-08 18:04:53 -04003130 if (current >= MaxColors)
3131 current = MaxColors - 1;
Steve Kondikae271bc2015-11-15 02:50:53 +01003132 if (current < top_color)
3133 top_color = current;
3134 if (current - top_color >= page_size)
3135 top_color = current - (page_size - 1);
3136
3137 MvPrintw(LINES - 1, 0, "Number: %d", value);
3138 clrtoeol();
3139 } while
micky3879b9f5e72025-07-08 18:04:53 -04003140 (!isQuit(this_c, TRUE));
Steve Kondikae271bc2015-11-15 02:50:53 +01003141
3142 erase();
3143
3144 /*
3145 * ncurses does not reset each color individually when calling endwin().
3146 */
micky3879b9f5e72025-07-08 18:04:53 -04003147 reset_all_colors();
Steve Kondikae271bc2015-11-15 02:50:53 +01003148
3149 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04003150 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01003151}
micky3879b9f5e72025-07-08 18:04:53 -04003152#endif /* HAVE_COLOR_CONTENT */
Steve Kondikae271bc2015-11-15 02:50:53 +01003153
3154/****************************************************************************
3155 *
3156 * Alternate character-set stuff
3157 *
3158 ****************************************************************************/
3159static bool
micky3879b9f5e72025-07-08 18:04:53 -04003160cycle_attr(int ch, unsigned *at_code, attr_t *attr, ATTR_TBL * list, unsigned limit)
Steve Kondikae271bc2015-11-15 02:50:53 +01003161{
3162 bool result = TRUE;
3163
3164 switch (ch) {
3165 case 'v':
3166 if ((*at_code += 1) >= limit)
3167 *at_code = 0;
3168 break;
3169 case 'V':
3170 if (*at_code == 0)
3171 *at_code = limit - 1;
3172 else
3173 *at_code -= 1;
3174 break;
3175 default:
3176 result = FALSE;
3177 break;
3178 }
3179 if (result)
3180 *attr = list[*at_code].attr;
3181 return result;
3182}
3183
micky3879b9f5e72025-07-08 18:04:53 -04003184#if USE_WIDEC_SUPPORT
3185static bool
3186cycle_w_attr(int ch, unsigned *at_code, attr_t *attr, W_ATTR_TBL * list, unsigned limit)
3187{
3188 bool result = TRUE;
3189
3190 switch (ch) {
3191 case 'v':
3192 if ((*at_code += 1) >= limit)
3193 *at_code = 0;
3194 break;
3195 case 'V':
3196 if (*at_code == 0)
3197 *at_code = limit - 1;
3198 else
3199 *at_code -= 1;
3200 break;
3201 default:
3202 result = FALSE;
3203 break;
3204 }
3205 if (result)
3206 *attr = list[*at_code].attr;
3207 return result;
3208}
3209#endif
3210
Steve Kondikae271bc2015-11-15 02:50:53 +01003211static bool
3212cycle_colors(int ch, int *fg, int *bg, NCURSES_PAIRS_T *pair)
3213{
3214 bool result = FALSE;
3215
micky3879b9f5e72025-07-08 18:04:53 -04003216 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003217 result = TRUE;
3218 switch (ch) {
3219 case 'F':
3220 if ((*fg -= 1) < 0)
3221 *fg = COLORS - 1;
3222 break;
3223 case 'f':
3224 if ((*fg += 1) >= COLORS)
3225 *fg = 0;
3226 break;
3227 case 'B':
3228 if ((*bg -= 1) < 0)
3229 *bg = COLORS - 1;
3230 break;
3231 case 'b':
3232 if ((*bg += 1) >= COLORS)
3233 *bg = 0;
3234 break;
3235 default:
3236 result = FALSE;
3237 break;
3238 }
3239 if (result) {
3240 *pair = (NCURSES_PAIRS_T) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
3241 if (*pair != 0) {
3242 *pair = 1;
3243 if (init_pair(*pair,
3244 (NCURSES_COLOR_T) *fg,
3245 (NCURSES_COLOR_T) *bg) == ERR) {
3246 result = FALSE;
3247 }
3248 }
3249 }
3250 }
3251 return result;
3252}
3253
3254/****************************************************************************
3255 *
3256 * Soft-key label test
3257 *
3258 ****************************************************************************/
3259
3260#if USE_SOFTKEYS
3261
3262#define SLK_HELP 17
3263#define SLK_WORK (SLK_HELP + 3)
3264
3265static void
3266slk_help(void)
3267{
3268 static const char *table[] =
3269 {
3270 "Available commands are:"
3271 ,""
3272 ,"^L -- repaint this message and activate soft keys"
3273 ,"a/d -- activate/disable soft keys"
3274 ,"c -- set centered format for labels"
3275 ,"l -- set left-justified format for labels"
3276 ,"r -- set right-justified format for labels"
3277 ,"[12345678] -- set label; labels are numbered 1 through 8"
3278 ,"e -- erase stdscr (should not erase labels)"
3279 ,"s -- test scrolling of shortened screen"
3280 ,"v/V -- cycle through video attributes"
3281#if HAVE_SLK_COLOR
3282 ,"F/f/B/b -- cycle through foreground/background colors"
3283#endif
3284 ,"ESC -- return to main menu"
3285 ,""
3286 ,"Note: if activating the soft keys causes your terminal to scroll up"
3287 ,"one line, your terminal auto-scrolls when anything is written to the"
3288 ,"last screen position. The ncurses code does not yet handle this"
3289 ,"gracefully."
3290 };
3291 unsigned j;
3292
3293 move(2, 0);
3294 for (j = 0; j < SIZEOF(table); ++j) {
3295 P(table[j]);
3296 }
3297 refresh();
3298}
3299
3300#if HAVE_SLK_COLOR
3301static void
3302call_slk_color(int fg, int bg)
3303{
3304 init_pair(1, (NCURSES_COLOR_T) bg, (NCURSES_COLOR_T) fg);
3305 slk_color(1);
3306 MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
3307 clrtoeol();
3308 slk_touch();
3309 slk_noutrefresh();
3310 refresh();
3311}
3312#endif
3313
micky3879b9f5e72025-07-08 18:04:53 -04003314static int
3315slk_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01003316/* exercise the soft keys */
3317{
3318 int c, fmt = 1;
3319 char buf[9];
3320 char *s;
micky3879b9f5e72025-07-08 18:04:53 -04003321 attr_t attr = A_NORMAL;
Steve Kondikae271bc2015-11-15 02:50:53 +01003322 unsigned at_code = 0;
3323#if HAVE_SLK_COLOR
3324 int fg = COLOR_BLACK;
3325 int bg = COLOR_WHITE;
3326 NCURSES_PAIRS_T pair = 0;
3327#endif
3328 ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3329 unsigned my_size = init_attr_list(my_list, termattrs());
3330
3331 c = CTRL('l');
3332#if HAVE_SLK_COLOR
micky3879b9f5e72025-07-08 18:04:53 -04003333 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003334 call_slk_color(fg, bg);
3335 }
3336#endif
3337
3338 do {
3339 move(0, 0);
3340 switch (c) {
3341 case CTRL('l'):
3342 erase();
3343 attron(A_BOLD);
3344 MvAddStr(0, 20, "Soft Key Exerciser");
3345 attroff(A_BOLD);
3346
3347 slk_help();
3348 /* fall through */
3349
3350 case 'a':
3351 slk_restore();
3352 break;
3353
3354 case 'e':
3355 wclear(stdscr);
3356 break;
3357
3358 case 's':
3359 MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3360 while ((c = Getchar()) != 'Q' && (c != ERR))
micky3879b9f5e72025-07-08 18:04:53 -04003361 AddCh(c);
Steve Kondikae271bc2015-11-15 02:50:53 +01003362 break;
3363
3364 case 'd':
3365 slk_clear();
3366 break;
3367
3368 case 'l':
3369 fmt = 0;
3370 break;
3371
3372 case 'c':
3373 fmt = 1;
3374 break;
3375
3376 case 'r':
3377 fmt = 2;
3378 break;
3379
3380 case '1':
3381 case '2':
3382 case '3':
3383 case '4':
3384 case '5':
3385 case '6':
3386 case '7':
3387 case '8':
3388 MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
micky3879b9f5e72025-07-08 18:04:53 -04003389 _nc_STRCPY(buf, "", sizeof(buf));
Steve Kondikae271bc2015-11-15 02:50:53 +01003390 if ((s = slk_label(c - '0')) != 0) {
micky3879b9f5e72025-07-08 18:04:53 -04003391 _nc_STRNCPY(buf, s, (size_t) 8);
Steve Kondikae271bc2015-11-15 02:50:53 +01003392 }
3393 wGetstring(stdscr, buf, 8);
3394 slk_set((c - '0'), buf, fmt);
3395 slk_refresh();
3396 move(SLK_WORK, 0);
3397 clrtobot();
3398 break;
3399
3400 case case_QUIT:
3401 goto done;
3402
3403#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3404 case KEY_RESIZE:
3405 wnoutrefresh(stdscr);
3406 break;
3407#endif
3408
3409 default:
3410 if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
micky3879b9f5e72025-07-08 18:04:53 -04003411 slk_attrset((chtype) attr);
Steve Kondikae271bc2015-11-15 02:50:53 +01003412 slk_touch();
3413 slk_noutrefresh();
3414 break;
3415 }
3416#if HAVE_SLK_COLOR
3417 if (cycle_colors(c, &fg, &bg, &pair)) {
micky3879b9f5e72025-07-08 18:04:53 -04003418 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003419 call_slk_color(fg, bg);
3420 } else {
3421 beep();
3422 }
3423 break;
3424 }
3425#endif
3426 beep();
3427 break;
3428 }
micky3879b9f5e72025-07-08 18:04:53 -04003429 } while (!isQuit(c = Getchar(), TRUE));
Steve Kondikae271bc2015-11-15 02:50:53 +01003430
3431 done:
3432 slk_clear();
3433 erase();
3434 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04003435 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01003436}
3437
3438#if USE_WIDEC_SUPPORT
3439#define SLKLEN 8
micky3879b9f5e72025-07-08 18:04:53 -04003440static int
3441x_slk_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01003442/* exercise the soft keys */
3443{
3444 int c, fmt = 1;
3445 wchar_t buf[SLKLEN + 1];
3446 char *s;
micky3879b9f5e72025-07-08 18:04:53 -04003447 attr_t attr = WA_NORMAL;
Steve Kondikae271bc2015-11-15 02:50:53 +01003448 unsigned at_code = 0;
3449 int fg = COLOR_BLACK;
3450 int bg = COLOR_WHITE;
3451 NCURSES_PAIRS_T pair = 0;
micky3879b9f5e72025-07-08 18:04:53 -04003452 W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
3453 unsigned my_size = init_w_attr_list(my_list, term_attrs());
Steve Kondikae271bc2015-11-15 02:50:53 +01003454
3455 c = CTRL('l');
micky3879b9f5e72025-07-08 18:04:53 -04003456 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003457 call_slk_color(fg, bg);
3458 }
3459 do {
3460 move(0, 0);
3461 switch (c) {
3462 case CTRL('l'):
3463 erase();
3464 attr_on(WA_BOLD, NULL);
3465 MvAddStr(0, 20, "Soft Key Exerciser");
3466 attr_off(WA_BOLD, NULL);
3467
3468 slk_help();
3469 /* fall through */
3470
3471 case 'a':
3472 slk_restore();
3473 break;
3474
3475 case 'e':
3476 wclear(stdscr);
3477 break;
3478
3479 case 's':
3480 MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3481 while ((c = Getchar()) != 'Q' && (c != ERR))
micky3879b9f5e72025-07-08 18:04:53 -04003482 AddCh(c);
Steve Kondikae271bc2015-11-15 02:50:53 +01003483 break;
3484
3485 case 'd':
3486 slk_clear();
3487 break;
3488
3489 case 'l':
3490 fmt = 0;
3491 break;
3492
3493 case 'c':
3494 fmt = 1;
3495 break;
3496
3497 case 'r':
3498 fmt = 2;
3499 break;
3500
3501 case '1':
3502 case '2':
3503 case '3':
3504 case '4':
3505 case '5':
3506 case '6':
3507 case '7':
3508 case '8':
3509 MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3510 *buf = 0;
3511 if ((s = slk_label(c - '0')) != 0) {
3512 char *temp = strdup(s);
3513 size_t used = strlen(temp);
3514 size_t want = SLKLEN;
Steve Kondikae271bc2015-11-15 02:50:53 +01003515#ifndef state_unused
3516 mbstate_t state;
3517#endif
3518
3519 buf[0] = L'\0';
3520 while (want > 0 && used != 0) {
micky3879b9f5e72025-07-08 18:04:53 -04003521 size_t test;
Steve Kondikae271bc2015-11-15 02:50:53 +01003522 const char *base = s;
micky3879b9f5e72025-07-08 18:04:53 -04003523
Steve Kondikae271bc2015-11-15 02:50:53 +01003524 reset_mbytes(state);
3525 test = count_mbytes(base, 0, &state);
3526 if (test == (size_t) -1) {
3527 temp[--used] = 0;
3528 } else if (test > want) {
3529 temp[--used] = 0;
3530 } else {
3531 reset_mbytes(state);
3532 trans_mbytes(buf, base, want, &state);
3533 break;
3534 }
3535 }
3536 free(temp);
3537 }
3538 wGet_wstring(stdscr, buf, SLKLEN);
3539 slk_wset((c - '0'), buf, fmt);
3540 slk_refresh();
3541 move(SLK_WORK, 0);
3542 clrtobot();
3543 break;
3544
3545 case case_QUIT:
3546 goto done;
3547
3548 case 'F':
micky3879b9f5e72025-07-08 18:04:53 -04003549 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003550 fg = (NCURSES_COLOR_T) ((fg + 1) % COLORS);
3551 call_slk_color(fg, bg);
3552 }
3553 break;
3554 case 'B':
micky3879b9f5e72025-07-08 18:04:53 -04003555 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003556 bg = (NCURSES_COLOR_T) ((bg + 1) % COLORS);
3557 call_slk_color(fg, bg);
3558 }
3559 break;
3560#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3561 case KEY_RESIZE:
3562 wnoutrefresh(stdscr);
3563 break;
3564#endif
3565 default:
micky3879b9f5e72025-07-08 18:04:53 -04003566 if (cycle_w_attr(c, &at_code, &attr, my_list, my_size)) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003567 slk_attr_set(attr, (NCURSES_COLOR_T) (fg || bg), NULL);
3568 slk_touch();
3569 slk_noutrefresh();
3570 break;
3571 }
3572#if HAVE_SLK_COLOR
3573 if (cycle_colors(c, &fg, &bg, &pair)) {
micky3879b9f5e72025-07-08 18:04:53 -04003574 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003575 call_slk_color(fg, bg);
3576 } else {
3577 beep();
3578 }
3579 break;
3580 }
3581#endif
3582 beep();
3583 break;
3584 }
micky3879b9f5e72025-07-08 18:04:53 -04003585 } while (!isQuit(c = Getchar(), TRUE));
Steve Kondikae271bc2015-11-15 02:50:53 +01003586
3587 done:
3588 slk_clear();
3589 erase();
3590 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04003591 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01003592}
3593#endif
3594#endif /* SLK_INIT */
3595
3596static void
3597show_256_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3598{
3599 unsigned first = 0;
3600 unsigned last = 255;
3601 unsigned code;
3602 int count;
3603
3604 erase();
3605 attron(A_BOLD);
3606 MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3607 first, last);
3608 attroff(A_BOLD);
3609 refresh();
3610
3611 for (code = first; code <= last; ++code) {
3612 int row = (int) (2 + (code / 16));
3613 int col = (int) (5 * (code % 16));
3614 IGNORE_RC(mvaddch(row, col, colored_chtype(code, attr, pair)));
3615 for (count = 1; count < repeat; ++count) {
micky3879b9f5e72025-07-08 18:04:53 -04003616 AddCh(colored_chtype(code, attr, pair));
Steve Kondikae271bc2015-11-15 02:50:53 +01003617 }
3618 }
3619
3620}
3621
3622/*
3623 * Show a slice of 32 characters, allowing those to be repeated up to the
3624 * screen's width.
3625 *
3626 * ISO 6429: codes 0x80 to 0x9f may be control characters that cause the
3627 * terminal to perform functions. The remaining codes can be graphic.
3628 */
3629static void
3630show_upper_chars(int base, int pagesize, int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3631{
3632 unsigned code;
3633 unsigned first = (unsigned) base;
3634 unsigned last = first + (unsigned) pagesize - 2;
3635 bool C1 = (first == 128);
3636 int reply;
3637
3638 erase();
3639 attron(A_BOLD);
3640 MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3641 C1 ? "C1" : "GR", first, last);
3642 attroff(A_BOLD);
3643 refresh();
3644
3645 for (code = first; code <= last; code++) {
3646 int count = repeat;
3647 int row = 2 + ((int) (code - first) % (pagesize / 2));
3648 int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3649 char tmp[80];
micky3879b9f5e72025-07-08 18:04:53 -04003650 _nc_SPRINTF(tmp, _nc_SLIMIT(sizeof(tmp)) "%3u (0x%x)", code, code);
Steve Kondikae271bc2015-11-15 02:50:53 +01003651 MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3652
3653 do {
3654 if (C1)
3655 nodelay(stdscr, TRUE);
3656 echochar(colored_chtype(code, attr, pair));
3657 if (C1) {
3658 /* (yes, this _is_ crude) */
3659 while ((reply = Getchar()) != ERR) {
micky3879b9f5e72025-07-08 18:04:53 -04003660 AddCh(UChar(reply));
Steve Kondikae271bc2015-11-15 02:50:53 +01003661 napms(10);
3662 }
3663 nodelay(stdscr, FALSE);
3664 }
3665 } while (--count > 0);
3666 }
3667}
3668
3669#define PC_COLS 4
3670
3671static void
3672show_pc_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3673{
3674 unsigned code;
3675
3676 erase();
3677 attron(A_BOLD);
3678 MvPrintw(0, 20, "Display of PC Character Codes");
3679 attroff(A_BOLD);
3680 refresh();
3681
3682 for (code = 0; code < 16; ++code) {
3683 MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3684 }
3685 for (code = 0; code < 256; code++) {
3686 int count = repeat;
3687 int row = 3 + (int) (code / 16) + (code >= 128);
3688 int col = 8 + (int) (code % 16) * PC_COLS;
3689 if ((code % 16) == 0)
3690 MvPrintw(row, 0, "0x%02x:", code);
3691 move(row, col);
3692 do {
3693 switch (code) {
3694 case '\n':
3695 case '\r':
3696 case '\b':
3697 case '\f':
3698 case '\033':
3699 case 0x9b:
3700 /*
3701 * Skip the ones that do not work.
3702 */
3703 break;
3704 default:
micky3879b9f5e72025-07-08 18:04:53 -04003705 AddCh(colored_chtype(code, A_ALTCHARSET | attr, pair));
Steve Kondikae271bc2015-11-15 02:50:53 +01003706 break;
3707 }
3708 } while (--count > 0);
3709 }
3710}
3711
3712static void
3713show_box_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3714{
3715 (void) repeat;
3716
3717 attr |= (attr_t) COLOR_PAIR(pair);
3718
3719 erase();
3720 attron(A_BOLD);
3721 MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3722 attroff(A_BOLD);
3723 refresh();
3724 /* *INDENT-OFF* */
3725 wborder(stdscr,
3726 colored_chtype(ACS_VLINE, attr, pair),
3727 colored_chtype(ACS_VLINE, attr, pair),
3728 colored_chtype(ACS_HLINE, attr, pair),
3729 colored_chtype(ACS_HLINE, attr, pair),
3730 colored_chtype(ACS_ULCORNER, attr, pair),
3731 colored_chtype(ACS_URCORNER, attr, pair),
3732 colored_chtype(ACS_LLCORNER, attr, pair),
3733 colored_chtype(ACS_LRCORNER, attr, pair));
3734 MvHLine(LINES / 2, 0, colored_chtype(ACS_HLINE, attr, pair), COLS);
3735 MvVLine(0, COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3736 MvAddCh(0, COLS / 2, colored_chtype(ACS_TTEE, attr, pair));
3737 MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS, attr, pair));
3738 MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE, attr, pair));
3739 MvAddCh(LINES / 2, 0, colored_chtype(ACS_LTEE, attr, pair));
3740 MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE, attr, pair));
3741 /* *INDENT-ON* */
Steve Kondikae271bc2015-11-15 02:50:53 +01003742}
3743
3744static int
3745show_1_acs(int n, int repeat, const char *name, chtype code)
3746{
3747 const int height = 16;
3748 int row = 2 + (n % height);
3749 int col = (n / height) * COLS / 2;
3750
3751 MvPrintw(row, col, "%*s : ", COLS / 4, name);
3752 do {
micky3879b9f5e72025-07-08 18:04:53 -04003753 AddCh(code);
Steve Kondikae271bc2015-11-15 02:50:53 +01003754 } while (--repeat > 0);
3755 return n + 1;
3756}
3757
3758static void
3759show_acs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3760/* display the ACS character set */
3761{
3762 int n;
3763
3764#define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3765
3766 erase();
3767 attron(A_BOLD);
3768 MvAddStr(0, 20, "Display of the ACS Character Set");
3769 attroff(A_BOLD);
3770 refresh();
3771
3772 n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3773 n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3774 n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3775 n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3776
3777 n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3778 n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3779 n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3780 n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3781
3782 n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3783 n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3784
3785 /*
3786 * HPUX's ACS definitions are broken here. Just give up.
3787 */
3788#if !(defined(__hpux) && !defined(NCURSES_VERSION))
3789 n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3790 n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3791 n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3792 n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3793
3794 n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3795 n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3796 n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3797 n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3798 n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3799 n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3800 n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3801 n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3802 n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3803
3804 n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3805 n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3806 n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3807
3808 n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3809 n = show_1_acs(n, repeat, BOTH(ACS_PI));
3810 n = show_1_acs(n, repeat, BOTH(ACS_S1));
3811 n = show_1_acs(n, repeat, BOTH(ACS_S3));
3812 n = show_1_acs(n, repeat, BOTH(ACS_S7));
3813 (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3814#endif
micky3879b9f5e72025-07-08 18:04:53 -04003815#undef BOTH
Steve Kondikae271bc2015-11-15 02:50:53 +01003816}
3817
micky3879b9f5e72025-07-08 18:04:53 -04003818static int
3819acs_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01003820{
3821 int c = 'a';
3822 int pagesize = 32;
3823 char *term = getenv("TERM");
3824 const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3825 ? "p=PC, "
3826 : "");
micky3879b9f5e72025-07-08 18:04:53 -04003827 attr_t attr = A_NORMAL;
Steve Kondikae271bc2015-11-15 02:50:53 +01003828 int digit = 0;
3829 int repeat = 1;
3830 int fg = COLOR_BLACK;
3831 int bg = COLOR_BLACK;
3832 unsigned at_code = 0;
3833 NCURSES_PAIRS_T pair = 0;
3834 void (*last_show_acs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3835 ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3836 unsigned my_size = init_attr_list(my_list, termattrs());
3837
3838 do {
3839 switch (c) {
3840 case CTRL('L'):
3841 Repaint();
3842 break;
3843 case 'a':
3844 ToggleAcs(last_show_acs, show_acs_chars);
3845 break;
3846 case 'p':
3847 if (*pch_kludge)
3848 ToggleAcs(last_show_acs, show_pc_chars);
3849 else
3850 beep();
3851 break;
3852 case 'w':
3853 if (pagesize == 32) {
3854 pagesize = 256;
3855 } else {
3856 pagesize = 32;
3857 }
3858 break;
3859 case 'x':
3860 ToggleAcs(last_show_acs, show_box_chars);
3861 break;
3862 case '0':
3863 case '1':
3864 case '2':
3865 case '3':
3866 digit = (c - '0');
3867 last_show_acs = 0;
3868 break;
3869 case '-':
3870 if (digit > 0) {
3871 --digit;
3872 last_show_acs = 0;
3873 } else {
3874 beep();
3875 }
3876 break;
3877 case '+':
3878 if (digit < 3) {
3879 ++digit;
3880 last_show_acs = 0;
3881 } else {
3882 beep();
3883 }
3884 break;
3885 case '>':
3886 if (repeat < (COLS / 4))
3887 ++repeat;
3888 break;
3889 case '<':
3890 if (repeat > 1)
3891 --repeat;
3892 break;
3893 default:
3894 if (cycle_attr(c, &at_code, &attr, my_list, my_size)
3895 || cycle_colors(c, &fg, &bg, &pair)) {
3896 break;
3897 } else {
3898 beep();
3899 }
3900 break;
3901 }
3902 if (pagesize != 32) {
3903 show_256_chars(repeat, attr, pair);
3904 } else if (last_show_acs != 0) {
3905 last_show_acs(repeat, attr, pair);
3906 } else {
3907 show_upper_chars(digit * pagesize + 128, pagesize, repeat, attr, pair);
3908 }
3909
3910 MvPrintw(LINES - 3, 0,
3911 "Note: ANSI terminals may not display C1 characters.");
3912 MvPrintw(LINES - 2, 0,
3913 "Select: a=ACS, w=all x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3914 pch_kludge);
micky3879b9f5e72025-07-08 18:04:53 -04003915 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01003916 MvPrintw(LINES - 1, 0,
3917 "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3918 my_list[at_code].name,
3919 fg, bg);
3920 } else {
3921 MvPrintw(LINES - 1, 0,
3922 "v/V cycles through video attributes (%s).",
3923 my_list[at_code].name);
3924 }
3925 refresh();
micky3879b9f5e72025-07-08 18:04:53 -04003926 } while (!isQuit(c = Getchar(), TRUE));
Steve Kondikae271bc2015-11-15 02:50:53 +01003927
3928 Pause();
3929 erase();
3930 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04003931 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01003932}
3933
3934#if USE_WIDEC_SUPPORT
3935static cchar_t *
3936merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, NCURSES_PAIRS_T pair)
3937{
Steve Kondikae271bc2015-11-15 02:50:53 +01003938
3939 *dst = *src;
3940 do {
micky3879b9f5e72025-07-08 18:04:53 -04003941 int count;
Steve Kondikae271bc2015-11-15 02:50:53 +01003942 TEST_CCHAR(src, count, {
3943 attr |= (test_attrs & A_ALTCHARSET);
3944 setcchar(dst, test_wch, attr, pair, NULL);
micky3879b9f5e72025-07-08 18:04:53 -04003945 }, {
Steve Kondikae271bc2015-11-15 02:50:53 +01003946 ;
3947 });
3948 } while (0);
3949 return dst;
3950}
3951
3952/*
3953 * Header/legend take up no more than 8 lines, leaving 16 lines on a 24-line
3954 * display. If there are no repeats, we could normally display 16 lines of 64
3955 * characters (1024 total). However, taking repeats and double-width cells
3956 * into account, use 256 characters for the page.
3957 */
3958static void
micky3879b9f5e72025-07-08 18:04:53 -04003959show_paged_widechars(unsigned base,
3960 unsigned pagesize,
Steve Kondikae271bc2015-11-15 02:50:53 +01003961 int repeat,
3962 int space,
3963 attr_t attr,
3964 NCURSES_PAIRS_T pair)
3965{
micky3879b9f5e72025-07-08 18:04:53 -04003966 unsigned first = base * pagesize;
3967 unsigned last = first + pagesize - 1;
Steve Kondikae271bc2015-11-15 02:50:53 +01003968 int per_line = 16;
3969 cchar_t temp;
3970 wchar_t code;
3971 wchar_t codes[10];
3972
3973 erase();
3974 attron(A_BOLD);
3975 MvPrintw(0, 20, "Display of Character Codes %#x to %#x", first, last);
3976 attroff(A_BOLD);
3977
micky3879b9f5e72025-07-08 18:04:53 -04003978 for (code = (wchar_t) first; code <= (wchar_t) last; code++) {
3979 int row = (2 + (int) (code - (wchar_t) first) / per_line);
Steve Kondikae271bc2015-11-15 02:50:53 +01003980 int col = 5 * ((int) code % per_line);
3981 int count;
3982
3983 memset(&codes, 0, sizeof(codes));
3984 codes[0] = code;
3985 setcchar(&temp, codes, attr, pair, 0);
3986 move(row, col);
3987 if (wcwidth(code) == 0 && code != 0) {
micky3879b9f5e72025-07-08 18:04:53 -04003988 AddCh((chtype) space |
Steve Kondikae271bc2015-11-15 02:50:53 +01003989 (A_REVERSE ^ attr) |
3990 (attr_t) COLOR_PAIR(pair));
3991 }
3992 add_wch(&temp);
3993 for (count = 1; count < repeat; ++count) {
3994 add_wch(&temp);
3995 }
3996 }
3997}
3998
3999static void
micky3879b9f5e72025-07-08 18:04:53 -04004000show_upper_widechars(unsigned first, int repeat, int space, attr_t attr, NCURSES_PAIRS_T pair)
Steve Kondikae271bc2015-11-15 02:50:53 +01004001{
4002 cchar_t temp;
4003 wchar_t code;
micky3879b9f5e72025-07-08 18:04:53 -04004004 unsigned last = first + 31;
Steve Kondikae271bc2015-11-15 02:50:53 +01004005
4006 erase();
4007 attron(A_BOLD);
4008 MvPrintw(0, 20, "Display of Character Codes %d to %d", first, last);
4009 attroff(A_BOLD);
4010
micky3879b9f5e72025-07-08 18:04:53 -04004011 for (code = (wchar_t) first; code <= (wchar_t) last; code++) {
4012 int row = 2 + ((int) (code - (wchar_t) first) % 16);
4013 int col = ((int) (code - (wchar_t) first) / 16) * COLS / 2;
Steve Kondikae271bc2015-11-15 02:50:53 +01004014 wchar_t codes[10];
4015 char tmp[80];
4016 int count = repeat;
Steve Kondikae271bc2015-11-15 02:50:53 +01004017
micky3879b9f5e72025-07-08 18:04:53 -04004018 _nc_SPRINTF(tmp, _nc_SLIMIT(sizeof(tmp))
4019 "%3ld (0x%lx)", (long) code, (long) code);
Steve Kondikae271bc2015-11-15 02:50:53 +01004020 MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
4021
4022 memset(&codes, 0, sizeof(codes));
4023 codes[0] = code;
4024 setcchar(&temp, codes, attr, pair, 0);
4025
4026 do {
micky3879b9f5e72025-07-08 18:04:53 -04004027 int y, x;
4028
Steve Kondikae271bc2015-11-15 02:50:53 +01004029 /*
4030 * Give non-spacing characters something to combine with. If we
4031 * don't, they'll bunch up in a heap on the space after the ":".
4032 * Mark them with reverse-video to make them simpler to find on
4033 * the display.
4034 */
4035 if (wcwidth(code) == 0) {
micky3879b9f5e72025-07-08 18:04:53 -04004036 AddCh((chtype) space |
Steve Kondikae271bc2015-11-15 02:50:53 +01004037 (A_REVERSE ^ attr) |
4038 (attr_t) COLOR_PAIR(pair));
4039 }
4040 /*
4041 * This uses echo_wchar(), for comparison with the normal 'f'
4042 * test (and to make a test-case for echo_wchar()). The screen
4043 * may flicker because the erase() at the top of the function
4044 * is met by the builtin refresh() in echo_wchar().
4045 */
4046 echo_wchar(&temp);
4047 /*
4048 * The repeat-count may make text wrap - avoid that.
4049 */
4050 getyx(stdscr, y, x);
4051 (void) y;
4052 if (x >= col + (COLS / 2) - 2)
4053 break;
4054 } while (--count > 0);
4055 }
4056}
4057
4058static int
4059show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
4060{
4061 const int height = 16;
4062 int row = 2 + (n % height);
4063 int col = (n / height) * COLS / 2;
4064
4065 MvPrintw(row, col, "%*s : ", COLS / 4, name);
4066 while (--repeat >= 0) {
4067 add_wch(code);
4068 }
4069 return n + 1;
4070}
4071
4072#define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
4073
4074static void
4075show_wacs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4076/* display the wide-ACS character set */
4077{
4078 cchar_t temp;
4079
4080 int n;
4081
4082/*#define BOTH2(name) #name, &(name) */
4083#define BOTH2(name) #name, MERGE_ATTR(name)
4084
4085 erase();
4086 attron(A_BOLD);
4087 MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4088 attroff(A_BOLD);
4089 refresh();
4090
4091 n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
4092 n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
4093 n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
4094 n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
4095
4096 n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
4097 n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
4098 n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
4099 n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
4100
4101 n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
4102 n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
4103
4104 n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
4105 n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
4106 n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
4107 n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
4108
4109 n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
4110 n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
4111 n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
4112 n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
4113 n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
4114 n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
4115 n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
4116 n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
4117 n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
4118
4119#ifdef CURSES_WACS_ARRAY
4120 n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
4121 n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
4122 n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
4123
4124 n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
4125 n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
4126 n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
4127 n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
4128 n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
4129 (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
4130#endif
4131}
4132
4133#ifdef WACS_D_PLUS
4134static void
4135show_wacs_chars_double(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4136/* display the wide-ACS character set */
4137{
4138 cchar_t temp;
4139
4140 int n;
4141
4142/*#define BOTH2(name) #name, &(name) */
4143#define BOTH2(name) #name, MERGE_ATTR(name)
4144
4145 erase();
4146 attron(A_BOLD);
4147 MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4148 attroff(A_BOLD);
4149 refresh();
4150
4151 n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
4152 n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
4153 n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
4154 n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
4155
4156 n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
4157 n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
4158 n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
4159 n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
4160
4161 n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
4162 n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
4163
4164 n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
4165 n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
4166 n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
4167 n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
4168
4169 n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
4170 n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
4171 n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
4172 n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
4173 n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
4174 n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
4175 n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
4176 n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
micky3879b9f5e72025-07-08 18:04:53 -04004177 n = show_1_wacs(n, repeat, BOTH2(WACS_D_PLUS));
Steve Kondikae271bc2015-11-15 02:50:53 +01004178
4179#ifdef CURSES_WACS_ARRAY
4180 n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
4181 n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
4182 n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
4183
4184 n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
4185 n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
4186 n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
4187 n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
4188 n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
4189 (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
4190#endif
4191}
4192#endif
4193
4194#ifdef WACS_T_PLUS
4195static void
4196show_wacs_chars_thick(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4197/* display the wide-ACS character set */
4198{
4199 cchar_t temp;
4200
4201 int n;
4202
4203/*#define BOTH2(name) #name, &(name) */
4204#define BOTH2(name) #name, MERGE_ATTR(name)
4205
4206 erase();
4207 attron(A_BOLD);
4208 MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4209 attroff(A_BOLD);
4210 refresh();
4211
4212 n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
4213 n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
4214 n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
4215 n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
4216
4217 n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
4218 n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
4219 n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
4220 n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
4221
4222 n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
4223 n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
4224
4225 n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
4226 n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
4227 n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
4228 n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
4229
4230 n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
4231 n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
4232 n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
4233 n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
4234 n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
4235 n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
4236 n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
4237 n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
micky3879b9f5e72025-07-08 18:04:53 -04004238 n = show_1_wacs(n, repeat, BOTH2(WACS_T_PLUS));
Steve Kondikae271bc2015-11-15 02:50:53 +01004239
4240#ifdef CURSES_WACS_ARRAY
4241 n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
4242 n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
4243 n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
4244
4245 n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
4246 n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
4247 n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
4248 n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
4249 n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
4250 (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
4251#endif
4252}
4253#endif
4254
4255#undef MERGE_ATTR
4256
4257#define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
4258
4259static void
4260show_wbox_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4261{
4262 cchar_t temp[8];
4263
4264 (void) repeat;
4265 erase();
4266 attron(A_BOLD);
4267 MvAddStr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
4268 attroff(A_BOLD);
4269 refresh();
4270
4271 wborder_set(stdscr,
4272 MERGE_ATTR(0, WACS_VLINE),
4273 MERGE_ATTR(1, WACS_VLINE),
4274 MERGE_ATTR(2, WACS_HLINE),
4275 MERGE_ATTR(3, WACS_HLINE),
4276 MERGE_ATTR(4, WACS_ULCORNER),
4277 MERGE_ATTR(5, WACS_URCORNER),
4278 MERGE_ATTR(6, WACS_LLCORNER),
4279 MERGE_ATTR(7, WACS_LRCORNER));
4280 /* *INDENT-OFF* */
4281 (void) mvhline_set(LINES / 2, 0, MERGE_ATTR(0, WACS_HLINE), COLS);
4282 (void) mvvline_set(0, COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
4283 (void) mvadd_wch(0, COLS / 2, MERGE_ATTR(0, WACS_TTEE));
4284 (void) mvadd_wch(LINES / 2, COLS / 2, MERGE_ATTR(0, WACS_PLUS));
4285 (void) mvadd_wch(LINES - 1, COLS / 2, MERGE_ATTR(0, WACS_BTEE));
4286 (void) mvadd_wch(LINES / 2, 0, MERGE_ATTR(0, WACS_LTEE));
4287 (void) mvadd_wch(LINES / 2, COLS - 1, MERGE_ATTR(0, WACS_RTEE));
4288 /* *INDENT-ON* */
Steve Kondikae271bc2015-11-15 02:50:53 +01004289}
4290
4291#undef MERGE_ATTR
4292
4293static int
4294show_2_wacs(int n, const char *name, const char *code, attr_t attr, NCURSES_PAIRS_T pair)
4295{
4296 const int height = 16;
4297 int row = 2 + (n % height);
4298 int col = (n / height) * COLS / 2;
4299 char temp[80];
4300
4301 MvPrintw(row, col, "%*s : ", COLS / 4, name);
4302 (void) attr_set(attr, pair, 0);
micky3879b9f5e72025-07-08 18:04:53 -04004303 _nc_STRNCPY(temp, code, 20);
4304 addstr(temp);
Steve Kondikae271bc2015-11-15 02:50:53 +01004305 (void) attr_set(A_NORMAL, 0, 0);
4306 return n + 1;
4307}
4308
4309#define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
4310
4311static void
4312show_utf8_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4313{
4314 int n;
4315
4316 (void) repeat;
4317 erase();
4318 attron(A_BOLD);
4319 MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4320 attroff(A_BOLD);
4321 refresh();
4322 /* *INDENT-OFF* */
4323 n = SHOW_UTF8(0, "WACS_ULCORNER", "\342\224\214");
4324 n = SHOW_UTF8(n, "WACS_URCORNER", "\342\224\220");
4325 n = SHOW_UTF8(n, "WACS_LLCORNER", "\342\224\224");
4326 n = SHOW_UTF8(n, "WACS_LRCORNER", "\342\224\230");
4327
4328 n = SHOW_UTF8(n, "WACS_LTEE", "\342\224\234");
4329 n = SHOW_UTF8(n, "WACS_RTEE", "\342\224\244");
4330 n = SHOW_UTF8(n, "WACS_TTEE", "\342\224\254");
4331 n = SHOW_UTF8(n, "WACS_BTEE", "\342\224\264");
4332
4333 n = SHOW_UTF8(n, "WACS_HLINE", "\342\224\200");
4334 n = SHOW_UTF8(n, "WACS_VLINE", "\342\224\202");
4335
4336 n = SHOW_UTF8(n, "WACS_LARROW", "\342\206\220");
4337 n = SHOW_UTF8(n, "WACS_RARROW", "\342\206\222");
4338 n = SHOW_UTF8(n, "WACS_UARROW", "\342\206\221");
4339 n = SHOW_UTF8(n, "WACS_DARROW", "\342\206\223");
4340
4341 n = SHOW_UTF8(n, "WACS_BLOCK", "\342\226\256");
4342 n = SHOW_UTF8(n, "WACS_BOARD", "\342\226\222");
4343 n = SHOW_UTF8(n, "WACS_LANTERN", "\342\230\203");
4344 n = SHOW_UTF8(n, "WACS_BULLET", "\302\267");
4345 n = SHOW_UTF8(n, "WACS_CKBOARD", "\342\226\222");
4346 n = SHOW_UTF8(n, "WACS_DEGREE", "\302\260");
4347 n = SHOW_UTF8(n, "WACS_DIAMOND", "\342\227\206");
4348 n = SHOW_UTF8(n, "WACS_PLMINUS", "\302\261");
4349 n = SHOW_UTF8(n, "WACS_PLUS", "\342\224\274");
4350 n = SHOW_UTF8(n, "WACS_GEQUAL", "\342\211\245");
4351 n = SHOW_UTF8(n, "WACS_NEQUAL", "\342\211\240");
4352 n = SHOW_UTF8(n, "WACS_LEQUAL", "\342\211\244");
4353
4354 n = SHOW_UTF8(n, "WACS_STERLING", "\302\243");
4355 n = SHOW_UTF8(n, "WACS_PI", "\317\200");
4356 n = SHOW_UTF8(n, "WACS_S1", "\342\216\272");
4357 n = SHOW_UTF8(n, "WACS_S3", "\342\216\273");
4358 n = SHOW_UTF8(n, "WACS_S7", "\342\216\274");
4359 (void) SHOW_UTF8(n, "WACS_S9", "\342\216\275");
4360 /* *INDENT-ON* */
Steve Kondikae271bc2015-11-15 02:50:53 +01004361}
4362
4363/* display the wide-ACS character set */
micky3879b9f5e72025-07-08 18:04:53 -04004364static int
4365x_acs_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01004366{
4367 int c = 'a';
micky3879b9f5e72025-07-08 18:04:53 -04004368 unsigned digit = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01004369 int repeat = 1;
4370 int space = ' ';
micky3879b9f5e72025-07-08 18:04:53 -04004371 unsigned pagesize = 32;
4372 attr_t attr = WA_NORMAL;
Steve Kondikae271bc2015-11-15 02:50:53 +01004373 int fg = COLOR_BLACK;
4374 int bg = COLOR_BLACK;
4375 unsigned at_code = 0;
4376 NCURSES_PAIRS_T pair = 0;
4377 void (*last_show_wacs) (int, attr_t, NCURSES_PAIRS_T) = 0;
micky3879b9f5e72025-07-08 18:04:53 -04004378 W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
4379 unsigned my_size = init_w_attr_list(my_list, term_attrs());
4380 char at_page[20];
4381 bool pending_code = FALSE;
Steve Kondikae271bc2015-11-15 02:50:53 +01004382
micky3879b9f5e72025-07-08 18:04:53 -04004383 at_page[0] = '\0';
Steve Kondikae271bc2015-11-15 02:50:53 +01004384 do {
4385 switch (c) {
4386 case CTRL('L'):
4387 Repaint();
4388 break;
4389 case 'a':
4390 ToggleAcs(last_show_wacs, show_wacs_chars);
4391 break;
4392#ifdef WACS_D_PLUS
4393 case 'd':
4394 ToggleAcs(last_show_wacs, show_wacs_chars_double);
4395 break;
4396#endif
4397#ifdef WACS_T_PLUS
4398 case 't':
4399 ToggleAcs(last_show_wacs, show_wacs_chars_thick);
4400 break;
4401#endif
4402 case 'w':
4403 if (pagesize == 32) {
4404 pagesize = 256;
4405 } else {
4406 pagesize = 32;
4407 }
4408 break;
4409 case 'x':
4410 ToggleAcs(last_show_wacs, show_wbox_chars);
4411 break;
4412 case 'u':
4413 ToggleAcs(last_show_wacs, show_utf8_chars);
4414 break;
micky3879b9f5e72025-07-08 18:04:53 -04004415 case '@':
4416 pending_code = !pending_code;
4417 if (pending_code) {
4418 _nc_SPRINTF(at_page, _nc_SLIMIT(sizeof(at_page)) "%02x", digit);
4419 } else if (at_page[0] != '\0') {
4420 sscanf(at_page, "%x", &digit);
4421 }
4422 break;
Steve Kondikae271bc2015-11-15 02:50:53 +01004423 default:
micky3879b9f5e72025-07-08 18:04:53 -04004424 if (pending_code && isxdigit(c)) {
4425 size_t len = strlen(at_page);
4426 if (len && at_page[0] == '0') {
4427 memmove(at_page, at_page + 1, len--);
4428 }
4429 if (len < sizeof(at_page) - 1) {
4430 at_page[len++] = (char) c;
4431 at_page[len] = '\0';
4432 }
4433 } else if (pending_code
4434 && (c == '\b' || c == KEY_BACKSPACE || c == KEY_DC)) {
4435 size_t len = strlen(at_page);
4436 if (len)
4437 at_page[--len] = '\0';
4438 } else if (c < 256 && isdigit(c)) {
4439 digit = (unsigned) (c - '0');
Steve Kondikae271bc2015-11-15 02:50:53 +01004440 last_show_wacs = 0;
4441 } else if (c == '+') {
4442 ++digit;
micky3879b9f5e72025-07-08 18:04:53 -04004443 _nc_SPRINTF(at_page, _nc_SLIMIT(sizeof(at_page)) "%02x", digit);
Steve Kondikae271bc2015-11-15 02:50:53 +01004444 last_show_wacs = 0;
4445 } else if (c == '-' && digit > 0) {
4446 --digit;
micky3879b9f5e72025-07-08 18:04:53 -04004447 _nc_SPRINTF(at_page, _nc_SLIMIT(sizeof(at_page)) "%02x",
4448 UChar(digit));
Steve Kondikae271bc2015-11-15 02:50:53 +01004449 last_show_wacs = 0;
4450 } else if (c == '>' && repeat < (COLS / 4)) {
4451 ++repeat;
4452 } else if (c == '<' && repeat > 1) {
4453 --repeat;
4454 } else if (c == '_') {
4455 space = (space == ' ') ? '_' : ' ';
4456 last_show_wacs = 0;
micky3879b9f5e72025-07-08 18:04:53 -04004457 } else if (cycle_w_attr(c, &at_code, &attr, my_list, my_size)
Steve Kondikae271bc2015-11-15 02:50:53 +01004458 || cycle_colors(c, &fg, &bg, &pair)) {
4459 if (last_show_wacs != 0)
4460 break;
4461 } else {
4462 beep();
4463 break;
4464 }
4465 break;
4466 }
4467 if (pagesize != 32) {
4468 show_paged_widechars(digit, pagesize, repeat, space, attr, pair);
4469 } else if (last_show_wacs != 0) {
4470 last_show_wacs(repeat, attr, pair);
4471 } else {
4472 show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
4473 }
4474
4475 MvPrintw(LINES - 4, 0,
micky3879b9f5e72025-07-08 18:04:53 -04004476 "Select: a/d/t WACS, w=%d/page, @",
4477 pagesize);
4478 printw("%s",
4479 pending_code ? at_page : "page");
4480 addstr(", x=box, u UTF-8, ^L repaint");
Steve Kondikae271bc2015-11-15 02:50:53 +01004481 MvPrintw(LINES - 3, 2,
4482 "0-9,+/- non-ASCII, </> repeat, _ space, ESC=quit");
micky3879b9f5e72025-07-08 18:04:53 -04004483 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01004484 MvPrintw(LINES - 2, 2,
4485 "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
4486 my_list[at_code].name,
4487 fg, bg);
4488 } else {
4489 MvPrintw(LINES - 2, 2,
4490 "v/V cycles through video attributes (%s).",
4491 my_list[at_code].name);
4492 }
4493 refresh();
micky3879b9f5e72025-07-08 18:04:53 -04004494 } while (!isQuit(c = Getchar(), TRUE));
Steve Kondikae271bc2015-11-15 02:50:53 +01004495
4496 Pause();
4497 erase();
4498 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04004499 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01004500}
4501
4502#endif
4503
4504/*
4505 * Graphic-rendition test (adapted from vttest)
4506 */
micky3879b9f5e72025-07-08 18:04:53 -04004507static int
4508sgr_attr_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01004509{
4510 int pass;
4511
4512 for (pass = 0; pass < 2; pass++) {
4513 chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
4514
4515 /* Use non-default colors if possible to exercise bce a little */
micky3879b9f5e72025-07-08 18:04:53 -04004516 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01004517 init_pair(1, COLOR_WHITE, COLOR_BLUE);
4518 normal |= (chtype) COLOR_PAIR(1);
4519 }
4520 bkgdset(normal);
4521 erase();
4522 MvPrintw(1, 20, "Graphic rendition test pattern:");
4523
4524 MvPrintw(4, 1, "vanilla");
4525
4526#define set_sgr(mask) bkgdset((normal^(mask)));
4527 set_sgr(A_BOLD);
4528 MvPrintw(4, 40, "bold");
4529
4530 set_sgr(A_UNDERLINE);
4531 MvPrintw(6, 6, "underline");
4532
4533 set_sgr(A_BOLD | A_UNDERLINE);
4534 MvPrintw(6, 45, "bold underline");
4535
4536 set_sgr(A_BLINK);
4537 MvPrintw(8, 1, "blink");
4538
4539 set_sgr(A_BLINK | A_BOLD);
4540 MvPrintw(8, 40, "bold blink");
4541
4542 set_sgr(A_UNDERLINE | A_BLINK);
4543 MvPrintw(10, 6, "underline blink");
4544
4545 set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
4546 MvPrintw(10, 45, "bold underline blink");
4547
4548 set_sgr(A_REVERSE);
4549 MvPrintw(12, 1, "negative");
4550
4551 set_sgr(A_BOLD | A_REVERSE);
4552 MvPrintw(12, 40, "bold negative");
4553
4554 set_sgr(A_UNDERLINE | A_REVERSE);
4555 MvPrintw(14, 6, "underline negative");
4556
4557 set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
4558 MvPrintw(14, 45, "bold underline negative");
4559
4560 set_sgr(A_BLINK | A_REVERSE);
4561 MvPrintw(16, 1, "blink negative");
4562
4563 set_sgr(A_BOLD | A_BLINK | A_REVERSE);
4564 MvPrintw(16, 40, "bold blink negative");
4565
4566 set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
4567 MvPrintw(18, 6, "underline blink negative");
4568
4569 set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
4570 MvPrintw(18, 45, "bold underline blink negative");
4571
4572 bkgdset(normal);
4573 MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
4574 "Light");
4575 clrtoeol();
4576 Pause();
4577 }
4578
4579 bkgdset(A_NORMAL | BLANK);
4580 erase();
4581 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04004582 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01004583}
4584
4585/****************************************************************************
4586 *
4587 * Windows and scrolling tester.
4588 *
4589 ****************************************************************************/
4590
4591#define BOTLINES 4 /* number of line stolen from screen bottom */
4592
4593typedef struct {
4594 int y, x;
4595} pair;
4596
4597#define FRAME struct frame
4598FRAME
4599{
4600 FRAME *next, *last;
4601 bool do_scroll;
4602 bool do_keypad;
4603 WINDOW *wind;
4604};
4605
micky3879b9f5e72025-07-08 18:04:53 -04004606#if defined(NCURSES_VERSION) && NCURSES_EXT_FUNCS
4607#if (NCURSES_VERSION_PATCH < 20070331)
Steve Kondikae271bc2015-11-15 02:50:53 +01004608#define is_keypad(win) (win)->_use_keypad
4609#define is_scrollok(win) (win)->_scroll
Steve Kondikae271bc2015-11-15 02:50:53 +01004610#endif
4611#else
4612#define is_keypad(win) FALSE
4613#define is_scrollok(win) FALSE
4614#endif
4615
4616static WINDOW *
4617frame_win(FRAME * curp)
4618{
4619 return (curp != 0) ? curp->wind : stdscr;
4620}
4621
4622/* We need to know if these flags are actually set, so don't look in FRAME.
4623 * These names are known to work with SVr4 curses as well as ncurses. The
4624 * _use_keypad name does not work with Solaris 8.
4625 */
4626static bool
4627HaveKeypad(FRAME * curp)
4628{
4629 WINDOW *win = frame_win(curp);
4630 (void) win;
4631 return is_keypad(win);
4632}
4633
4634static bool
4635HaveScroll(FRAME * curp)
4636{
4637 WINDOW *win = frame_win(curp);
4638 (void) win;
4639 return is_scrollok(win);
4640}
4641
4642static void
4643newwin_legend(FRAME * curp)
4644{
micky3879b9f5e72025-07-08 18:04:53 -04004645#define DATA(num, name) { name, num }
Steve Kondikae271bc2015-11-15 02:50:53 +01004646 static const struct {
4647 const char *msg;
4648 int code;
4649 } legend[] = {
micky3879b9f5e72025-07-08 18:04:53 -04004650 DATA(0, "^C = create window"),
4651 DATA(0, "^N = next window"),
4652 DATA(0, "^P = previous window"),
4653 DATA(0, "^F = scroll forward"),
4654 DATA(0, "^B = scroll backward"),
4655 DATA(1, "^K = keypad(%s)"),
4656 DATA(2, "^S = scrollok(%s)"),
4657 DATA(0, "^W = save window"),
4658 DATA(0, "^R = restore window"),
Steve Kondikae271bc2015-11-15 02:50:53 +01004659#if HAVE_WRESIZE
micky3879b9f5e72025-07-08 18:04:53 -04004660 DATA(0, "^X = resize"),
Steve Kondikae271bc2015-11-15 02:50:53 +01004661#endif
micky3879b9f5e72025-07-08 18:04:53 -04004662 DATA(3, "^Q%s = exit")
Steve Kondikae271bc2015-11-15 02:50:53 +01004663 };
micky3879b9f5e72025-07-08 18:04:53 -04004664#undef DATA
Steve Kondikae271bc2015-11-15 02:50:53 +01004665 size_t n;
Steve Kondikae271bc2015-11-15 02:50:53 +01004666 bool do_keypad = HaveKeypad(curp);
4667 bool do_scroll = HaveScroll(curp);
4668 char buf[BUFSIZ];
4669
4670 move(LINES - 4, 0);
micky3879b9f5e72025-07-08 18:04:53 -04004671
Steve Kondikae271bc2015-11-15 02:50:53 +01004672 for (n = 0; n < SIZEOF(legend); n++) {
micky3879b9f5e72025-07-08 18:04:53 -04004673 int x;
4674
Steve Kondikae271bc2015-11-15 02:50:53 +01004675 switch (legend[n].code) {
4676 default:
micky3879b9f5e72025-07-08 18:04:53 -04004677 _nc_STRCPY(buf, legend[n].msg, sizeof(buf));
Steve Kondikae271bc2015-11-15 02:50:53 +01004678 break;
4679 case 1:
micky3879b9f5e72025-07-08 18:04:53 -04004680 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4681 legend[n].msg, do_keypad ? "yes" : "no");
Steve Kondikae271bc2015-11-15 02:50:53 +01004682 break;
4683 case 2:
micky3879b9f5e72025-07-08 18:04:53 -04004684 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4685 legend[n].msg, do_scroll ? "yes" : "no");
Steve Kondikae271bc2015-11-15 02:50:53 +01004686 break;
4687 case 3:
micky3879b9f5e72025-07-08 18:04:53 -04004688 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4689 legend[n].msg, do_keypad ? "/ESC" : "");
Steve Kondikae271bc2015-11-15 02:50:53 +01004690 break;
4691 }
4692 x = getcurx(stdscr);
4693 addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4694 addstr(buf);
4695 }
4696 clrtoeol();
4697}
4698
4699static void
4700transient(FRAME * curp, NCURSES_CONST char *msg)
4701{
4702 newwin_legend(curp);
4703 if (msg) {
4704 MvAddStr(LINES - 1, 0, msg);
4705 refresh();
4706 napms(1000);
4707 }
4708
4709 move(LINES - 1, 0);
4710 printw("%s characters are echoed, window should %sscroll.",
4711 HaveKeypad(curp) ? "Non-arrow" : "All other",
4712 HaveScroll(curp) ? "" : "not ");
4713 clrtoeol();
4714}
4715
4716static void
4717newwin_report(FRAME * curp)
4718/* report on the cursor's current position, then restore it */
4719{
4720 WINDOW *win = frame_win(curp);
4721 int y, x;
4722
4723 if (win != stdscr)
4724 transient(curp, (char *) 0);
4725 getyx(win, y, x);
4726 move(LINES - 1, COLS - 17);
4727 printw("Y = %2d X = %2d", y, x);
4728 if (win != stdscr)
4729 refresh();
4730 else
4731 wmove(win, y, x);
4732}
4733
4734static pair *
4735selectcell(int uli, int ulj, int lri, int lrj)
4736/* arrows keys move cursor, return location at current on non-arrow key */
4737{
4738 static pair res; /* result cell */
4739 int si = lri - uli + 1; /* depth of the select area */
4740 int sj = lrj - ulj + 1; /* width of the select area */
4741 int i = 0, j = 0; /* offsets into the select area */
4742
4743 res.y = uli;
4744 res.x = ulj;
4745 for (;;) {
4746 move(uli + i, ulj + j);
4747 newwin_report((FRAME *) 0);
4748
4749 switch (Getchar()) {
4750 case KEY_UP:
4751 i += si - 1;
4752 break;
4753 case KEY_DOWN:
4754 i++;
4755 break;
4756 case KEY_LEFT:
4757 j += sj - 1;
4758 break;
4759 case KEY_RIGHT:
4760 j++;
4761 break;
4762 case case_QUIT:
4763 return ((pair *) 0);
4764#ifdef NCURSES_MOUSE_VERSION
4765 case KEY_MOUSE:
4766 {
4767 MEVENT event;
4768
4769 getmouse(&event);
4770 if (event.y > uli && event.x > ulj) {
4771 i = event.y - uli;
4772 j = event.x - ulj;
4773 } else {
4774 beep();
4775 break;
4776 }
4777 }
Steve Kondikae271bc2015-11-15 02:50:53 +01004778#endif
micky3879b9f5e72025-07-08 18:04:53 -04004779 /* FALLTHRU */
Steve Kondikae271bc2015-11-15 02:50:53 +01004780 default:
4781 res.y = uli + i;
4782 res.x = ulj + j;
4783 return (&res);
4784 }
4785 i %= si;
4786 j %= sj;
4787 }
4788}
4789
4790static void
4791outerbox(pair ul, pair lr, bool onoff)
4792/* draw or erase a box *outside* the given pair of corners */
4793{
4794 MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4795 MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4796 MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4797 MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4798 move(ul.y - 1, ul.x);
4799 hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4800 move(ul.y, ul.x - 1);
4801 vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4802 move(lr.y + 1, ul.x);
4803 hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4804 move(ul.y, lr.x + 1);
4805 vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4806}
4807
4808static WINDOW *
4809getwindow(void)
4810/* Ask user for a window definition */
4811{
4812 WINDOW *rwindow;
4813 pair ul, lr, *tmp;
4814
4815 move(0, 0);
4816 clrtoeol();
4817 addstr("Use arrows to move cursor, anything else to mark corner 1");
4818 refresh();
4819 if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4820 return ((WINDOW *) 0);
4821 memcpy(&ul, tmp, sizeof(pair));
4822 MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4823 move(0, 0);
4824 clrtoeol();
4825 addstr("Use arrows to move cursor, anything else to mark corner 2");
4826 refresh();
4827 if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4828 (pair *) 0)
4829 return ((WINDOW *) 0);
4830 memcpy(&lr, tmp, sizeof(pair));
4831
4832 rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4833
4834 outerbox(ul, lr, TRUE);
4835 refresh();
4836
4837 if (rwindow != 0)
4838 wrefresh(rwindow);
4839
4840 move(0, 0);
4841 clrtoeol();
4842 return (rwindow);
4843}
4844
4845static void
4846newwin_move(FRAME * curp, int dy, int dx)
4847{
4848 WINDOW *win = frame_win(curp);
4849 int cur_y, cur_x;
4850 int max_y, max_x;
4851
4852 getyx(win, cur_y, cur_x);
4853 getmaxyx(win, max_y, max_x);
4854 if ((cur_x += dx) < 0)
4855 cur_x = 0;
4856 else if (cur_x >= max_x)
4857 cur_x = max_x - 1;
4858 if ((cur_y += dy) < 0)
4859 cur_y = 0;
4860 else if (cur_y >= max_y)
4861 cur_y = max_y - 1;
4862 wmove(win, cur_y, cur_x);
4863}
4864
4865static FRAME *
4866delete_framed(FRAME * fp, bool showit)
4867{
4868 FRAME *np = 0;
4869
4870 if (fp != 0) {
4871 fp->last->next = fp->next;
4872 fp->next->last = fp->last;
4873
4874 if (showit) {
4875 werase(fp->wind);
4876 wrefresh(fp->wind);
4877 }
4878 delwin(fp->wind);
4879
micky3879b9f5e72025-07-08 18:04:53 -04004880 np = (fp == fp->next) ? NULL : fp->next;
Steve Kondikae271bc2015-11-15 02:50:53 +01004881 free(fp);
4882 }
4883 return np;
4884}
4885
micky3879b9f5e72025-07-08 18:04:53 -04004886static int
4887scroll_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01004888/* Demonstrate windows */
4889{
4890 int c;
4891 FRAME *current = (FRAME *) 0, *neww;
4892 WINDOW *usescr;
4893#if HAVE_PUTWIN && HAVE_GETWIN
4894 FILE *fp;
4895#endif
4896
4897#define DUMPFILE "screendump"
4898
4899#ifdef NCURSES_MOUSE_VERSION
4900 mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4901#endif
4902 c = CTRL('C');
4903 raw();
4904 do {
4905 transient((FRAME *) 0, (char *) 0);
4906 switch (c) {
4907 case CTRL('C'):
4908 if ((neww = typeCalloc(FRAME, (size_t) 1)) == 0) {
micky3879b9f5e72025-07-08 18:04:53 -04004909 failed("scroll_test");
Steve Kondikae271bc2015-11-15 02:50:53 +01004910 goto breakout;
4911 }
4912 if ((neww->wind = getwindow()) == (WINDOW *) 0) {
micky3879b9f5e72025-07-08 18:04:53 -04004913 failed("scroll_test");
Steve Kondikae271bc2015-11-15 02:50:53 +01004914 free(neww);
4915 goto breakout;
4916 }
4917
4918 if (current == 0) { /* First element, */
4919 neww->next = neww; /* so point it at itself */
4920 neww->last = neww;
4921 } else {
4922 neww->next = current->next;
4923 neww->last = current;
4924 neww->last->next = neww;
4925 neww->next->last = neww;
4926 }
4927 current = neww;
4928 /* SVr4 curses sets the keypad on all newly-created windows to
4929 * false. Someone reported that PDCurses makes new windows inherit
4930 * this flag. Remove the following 'keypad()' call to test this
4931 */
4932 keypad(current->wind, TRUE);
4933 current->do_keypad = HaveKeypad(current);
4934 current->do_scroll = HaveScroll(current);
4935 break;
4936
4937 case CTRL('N'): /* go to next window */
4938 if (current)
4939 current = current->next;
4940 break;
4941
4942 case CTRL('P'): /* go to previous window */
4943 if (current)
4944 current = current->last;
4945 break;
4946
4947 case CTRL('F'): /* scroll current window forward */
4948 if (current)
4949 wscrl(frame_win(current), 1);
4950 break;
4951
4952 case CTRL('B'): /* scroll current window backwards */
4953 if (current)
4954 wscrl(frame_win(current), -1);
4955 break;
4956
4957 case CTRL('K'): /* toggle keypad mode for current */
4958 if (current) {
4959 current->do_keypad = !current->do_keypad;
4960 keypad(current->wind, current->do_keypad);
4961 }
4962 break;
4963
4964 case CTRL('S'):
4965 if (current) {
4966 current->do_scroll = !current->do_scroll;
4967 scrollok(current->wind, current->do_scroll);
4968 }
4969 break;
4970
4971#if HAVE_PUTWIN && HAVE_GETWIN
4972 case CTRL('W'): /* save and delete window */
4973 if ((current != 0) && (current == current->next)) {
4974 transient(current, "Will not save/delete ONLY window");
4975 break;
4976 } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4977 transient(current, "Can't open screen dump file");
4978 } else {
micky3879b9f5e72025-07-08 18:04:53 -04004979 int rc = putwin(frame_win(current), fp);
Steve Kondikae271bc2015-11-15 02:50:53 +01004980 (void) fclose(fp);
4981
micky3879b9f5e72025-07-08 18:04:53 -04004982 if (rc == OK) {
4983 current = delete_framed(current, TRUE);
4984 } else {
4985 transient(current, "Can't write screen dump file");
4986 }
Steve Kondikae271bc2015-11-15 02:50:53 +01004987 }
4988 break;
4989
4990 case CTRL('R'): /* restore window */
4991 if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4992 transient(current, "Can't open screen dump file");
4993 } else {
4994 if ((neww = typeCalloc(FRAME, (size_t) 1)) != 0) {
4995
4996 neww->next = current ? current->next : 0;
4997 neww->last = current;
4998 if (neww->last != 0)
4999 neww->last->next = neww;
5000 if (neww->next != 0)
5001 neww->next->last = neww;
5002
5003 neww->wind = getwin(fp);
5004
5005 wrefresh(neww->wind);
5006 } else {
micky3879b9f5e72025-07-08 18:04:53 -04005007 failed("scroll_test");
Steve Kondikae271bc2015-11-15 02:50:53 +01005008 }
5009 (void) fclose(fp);
5010 }
5011 break;
5012#endif
5013
5014#if HAVE_WRESIZE
5015 case CTRL('X'): /* resize window */
5016 if (current) {
5017 pair *tmp, ul, lr;
micky3879b9f5e72025-07-08 18:04:53 -04005018 int mx, my;
Steve Kondikae271bc2015-11-15 02:50:53 +01005019
5020 move(0, 0);
5021 clrtoeol();
5022 addstr("Use arrows to move cursor, anything else to mark new corner");
5023 refresh();
5024
5025 getbegyx(current->wind, ul.y, ul.x);
5026
5027 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
5028 if (tmp == (pair *) 0) {
5029 beep();
5030 break;
5031 }
5032
5033 getmaxyx(current->wind, lr.y, lr.x);
5034 lr.y += (ul.y - 1);
5035 lr.x += (ul.x - 1);
5036 outerbox(ul, lr, FALSE);
5037 wnoutrefresh(stdscr);
5038
5039 /* strictly cosmetic hack for the test */
5040 getmaxyx(current->wind, my, mx);
5041 if (my > tmp->y - ul.y) {
5042 getyx(current->wind, lr.y, lr.x);
5043 wmove(current->wind, tmp->y - ul.y + 1, 0);
5044 wclrtobot(current->wind);
5045 wmove(current->wind, lr.y, lr.x);
5046 }
micky3879b9f5e72025-07-08 18:04:53 -04005047 if (mx > tmp->x - ul.x) {
5048 int i;
Steve Kondikae271bc2015-11-15 02:50:53 +01005049 for (i = 0; i < my; i++) {
5050 wmove(current->wind, i, tmp->x - ul.x + 1);
5051 wclrtoeol(current->wind);
5052 }
micky3879b9f5e72025-07-08 18:04:53 -04005053 }
Steve Kondikae271bc2015-11-15 02:50:53 +01005054 wnoutrefresh(current->wind);
5055
5056 memcpy(&lr, tmp, sizeof(pair));
5057 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
5058
5059 getbegyx(current->wind, ul.y, ul.x);
5060 getmaxyx(current->wind, lr.y, lr.x);
5061 lr.y += (ul.y - 1);
5062 lr.x += (ul.x - 1);
5063 outerbox(ul, lr, TRUE);
5064 wnoutrefresh(stdscr);
5065
5066 wnoutrefresh(current->wind);
5067 move(0, 0);
5068 clrtoeol();
5069 doupdate();
5070 }
5071 break;
5072#endif /* HAVE_WRESIZE */
5073
Steve Kondikae271bc2015-11-15 02:50:53 +01005074 case KEY_UP:
5075 newwin_move(current, -1, 0);
5076 break;
5077 case KEY_DOWN:
5078 newwin_move(current, 1, 0);
5079 break;
5080 case KEY_LEFT:
5081 newwin_move(current, 0, -1);
5082 break;
5083 case KEY_RIGHT:
5084 newwin_move(current, 0, 1);
5085 break;
5086
5087 case KEY_BACKSPACE:
5088 /* FALLTHROUGH */
5089 case KEY_DC:
5090 {
5091 int y, x;
5092 getyx(frame_win(current), y, x);
5093 if (--x < 0) {
5094 if (--y < 0)
5095 break;
5096 x = getmaxx(frame_win(current)) - 1;
5097 }
5098 (void) mvwdelch(frame_win(current), y, x);
5099 }
5100 break;
5101
5102 case '\r':
5103 c = '\n';
5104 /* FALLTHROUGH */
5105
5106 default:
5107 if (current)
5108 waddch(current->wind, (chtype) c);
5109 else
5110 beep();
5111 break;
5112 }
5113 newwin_report(current);
5114 usescr = frame_win(current);
5115 wrefresh(usescr);
5116 } while
micky3879b9f5e72025-07-08 18:04:53 -04005117 (!isQuit(c = wGetchar(usescr), TRUE)
Steve Kondikae271bc2015-11-15 02:50:53 +01005118 && (c != ERR));
5119
5120 breakout:
5121 while (current != 0)
5122 current = delete_framed(current, FALSE);
5123
5124 scrollok(stdscr, TRUE); /* reset to driver's default */
5125#ifdef NCURSES_MOUSE_VERSION
5126 mousemask(0, (mmask_t *) 0);
5127#endif
5128 noraw();
5129 erase();
5130 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04005131 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01005132}
5133
5134/****************************************************************************
5135 *
5136 * Panels tester
5137 *
5138 ****************************************************************************/
5139
5140#if USE_LIBPANEL
5141static int nap_msec = 1;
5142
5143static NCURSES_CONST char *mod[] =
5144{
5145 "test ",
5146 "TEST ",
5147 "(**) ",
5148 "*()* ",
5149 "<--> ",
5150 "LAST "
5151};
5152
5153/*+-------------------------------------------------------------------------
5154 wait_a_while(msec)
5155--------------------------------------------------------------------------*/
5156static void
5157wait_a_while(int msec GCC_UNUSED)
5158{
5159#if HAVE_NAPMS
5160 if (nap_msec == 1)
5161 wGetchar(stdscr);
5162 else
5163 napms(nap_msec);
5164#else
5165 if (nap_msec == 1)
5166 wGetchar(stdscr);
5167 else if (msec > 1000)
5168 sleep((unsigned) msec / 1000);
5169 else
5170 sleep(1);
5171#endif
5172} /* end of wait_a_while */
5173
5174/*+-------------------------------------------------------------------------
5175 saywhat(text)
5176--------------------------------------------------------------------------*/
5177static void
5178saywhat(NCURSES_CONST char *text)
5179{
5180 wmove(stdscr, LINES - 1, 0);
5181 wclrtoeol(stdscr);
5182 if (text != 0 && *text != '\0') {
5183 waddstr(stdscr, text);
5184 waddstr(stdscr, "; ");
5185 }
5186 waddstr(stdscr, "press any key to continue");
5187} /* end of saywhat */
5188
5189/*+-------------------------------------------------------------------------
5190 mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
5191--------------------------------------------------------------------------*/
5192static PANEL *
5193mkpanel(NCURSES_COLOR_T color, int rows, int cols, int tly, int tlx)
5194{
5195 WINDOW *win;
5196 PANEL *pan = 0;
5197
5198 if ((win = newwin(rows, cols, tly, tlx)) != 0) {
5199 if ((pan = new_panel(win)) == 0) {
5200 delwin(win);
micky3879b9f5e72025-07-08 18:04:53 -04005201 } else if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01005202 NCURSES_COLOR_T fg = (NCURSES_COLOR_T) ((color == COLOR_BLUE)
5203 ? COLOR_WHITE
5204 : COLOR_BLACK);
5205 NCURSES_COLOR_T bg = color;
5206
5207 init_pair(color, fg, bg);
5208 wbkgdset(win, (attr_t) (COLOR_PAIR(color) | ' '));
5209 } else {
5210 wbkgdset(win, A_BOLD | ' ');
5211 }
5212 }
5213 return pan;
5214} /* end of mkpanel */
5215
5216/*+-------------------------------------------------------------------------
5217 rmpanel(pan)
5218--------------------------------------------------------------------------*/
5219static void
micky3879b9f5e72025-07-08 18:04:53 -04005220rmpanel(PANEL *pan)
Steve Kondikae271bc2015-11-15 02:50:53 +01005221{
5222 WINDOW *win = panel_window(pan);
5223 del_panel(pan);
5224 delwin(win);
5225} /* end of rmpanel */
5226
5227/*+-------------------------------------------------------------------------
5228 pflush()
5229--------------------------------------------------------------------------*/
5230static void
5231pflush(void)
5232{
5233 update_panels();
5234 doupdate();
5235} /* end of pflush */
5236
5237/*+-------------------------------------------------------------------------
5238 fill_panel(win)
5239--------------------------------------------------------------------------*/
5240static void
5241init_panel(WINDOW *win)
5242{
5243 register int y, x;
5244
5245 for (y = 0; y < LINES - 1; y++) {
5246 for (x = 0; x < COLS; x++)
5247 wprintw(win, "%d", (y + x) % 10);
5248 }
5249}
5250
5251static void
micky3879b9f5e72025-07-08 18:04:53 -04005252fill_panel(PANEL *pan)
Steve Kondikae271bc2015-11-15 02:50:53 +01005253{
5254 WINDOW *win = panel_window(pan);
5255 const char *userptr = (const char *) panel_userptr(pan);
5256 int num = (userptr && *userptr) ? userptr[1] : '?';
5257 int y, x;
5258
5259 wmove(win, 1, 1);
5260 wprintw(win, "-pan%c-", num);
5261 wclrtoeol(win);
5262 box(win, 0, 0);
5263 for (y = 2; y < getmaxy(win) - 1; y++) {
5264 for (x = 1; x < getmaxx(win) - 1; x++) {
5265 wmove(win, y, x);
5266 waddch(win, UChar(num));
5267 }
5268 }
5269}
5270
5271#if USE_WIDEC_SUPPORT
5272static void
5273init_wide_panel(WINDOW *win)
5274{
5275 int digit;
5276 cchar_t temp[10];
5277
5278 for (digit = 0; digit < 10; ++digit)
5279 make_fullwidth_digit(&temp[digit], digit);
5280
5281 do {
5282 int y, x;
5283 getyx(stdscr, y, x);
5284 digit = (y + x / 2) % 10;
5285 } while (wadd_wch(win, &temp[digit]) != ERR);
5286}
5287
5288static void
micky3879b9f5e72025-07-08 18:04:53 -04005289fill_wide_panel(PANEL *pan)
Steve Kondikae271bc2015-11-15 02:50:53 +01005290{
5291 WINDOW *win = panel_window(pan);
5292 const char *userptr = (const char *) panel_userptr(pan);
5293 int num = (userptr && *userptr) ? userptr[1] : '?';
5294 int y, x;
5295
5296 wmove(win, 1, 1);
5297 wprintw(win, "-pan%c-", num);
5298 wclrtoeol(win);
5299 box(win, 0, 0);
5300 for (y = 2; y < getmaxy(win) - 1; y++) {
5301 for (x = 1; x < getmaxx(win) - 1; x++) {
5302 wmove(win, y, x);
5303 waddch(win, UChar(num));
5304 }
5305 }
5306}
5307#endif
5308
5309#define MAX_PANELS 5
5310
5311static void
micky3879b9f5e72025-07-08 18:04:53 -04005312canned_panel(PANEL *px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
Steve Kondikae271bc2015-11-15 02:50:53 +01005313{
5314 int which = cmd[1] - '0';
5315
5316 saywhat(cmd);
5317 switch (*cmd) {
5318 case 'h':
5319 hide_panel(px[which]);
5320 break;
5321 case 's':
5322 show_panel(px[which]);
5323 break;
5324 case 't':
5325 top_panel(px[which]);
5326 break;
5327 case 'b':
5328 bottom_panel(px[which]);
5329 break;
5330 case 'd':
5331 rmpanel(px[which]);
5332 break;
5333 }
5334 pflush();
5335 wait_a_while(nap_msec);
5336}
5337
micky3879b9f5e72025-07-08 18:04:53 -04005338static int
Steve Kondikae271bc2015-11-15 02:50:53 +01005339demo_panels(void (*InitPanel) (WINDOW *), void (*FillPanel) (PANEL *))
5340{
5341 int count;
5342 int itmp;
5343 PANEL *px[MAX_PANELS + 1];
5344
5345 scrollok(stdscr, FALSE); /* we don't want stdscr to scroll! */
5346 refresh();
5347
5348 InitPanel(stdscr);
5349 for (count = 0; count < 5; count++) {
5350 px[1] = mkpanel(COLOR_RED,
5351 LINES / 2 - 2,
5352 COLS / 8 + 1,
5353 0,
5354 0);
5355 set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
5356
5357 px[2] = mkpanel(COLOR_GREEN,
5358 LINES / 2 + 1,
5359 COLS / 7,
5360 LINES / 4,
5361 COLS / 10);
5362 set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
5363
5364 px[3] = mkpanel(COLOR_YELLOW,
5365 LINES / 4,
5366 COLS / 10,
5367 LINES / 2,
5368 COLS / 9);
5369 set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
5370
5371 px[4] = mkpanel(COLOR_BLUE,
5372 LINES / 2 - 2,
5373 COLS / 8,
5374 LINES / 2 - 2,
5375 COLS / 3);
5376 set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
5377
5378 px[5] = mkpanel(COLOR_MAGENTA,
5379 LINES / 2 - 2,
5380 COLS / 8,
5381 LINES / 2,
5382 COLS / 2 - 2);
5383 set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
5384
5385 FillPanel(px[1]);
5386 FillPanel(px[2]);
5387 FillPanel(px[3]);
5388 FillPanel(px[4]);
5389 FillPanel(px[5]);
5390
5391 hide_panel(px[4]);
5392 hide_panel(px[5]);
5393 pflush();
5394 saywhat("");
5395 wait_a_while(nap_msec);
5396
5397 saywhat("h3 s1 s2 s4 s5");
5398 move_panel(px[1], 0, 0);
5399 hide_panel(px[3]);
5400 show_panel(px[1]);
5401 show_panel(px[2]);
5402 show_panel(px[4]);
5403 show_panel(px[5]);
5404 pflush();
5405 wait_a_while(nap_msec);
5406
5407 canned_panel(px, "s1");
5408 canned_panel(px, "s2");
5409
5410 saywhat("m2");
5411 move_panel(px[2], LINES / 3 + 1, COLS / 8);
5412 pflush();
5413 wait_a_while(nap_msec);
5414
5415 canned_panel(px, "s3");
5416
5417 saywhat("m3");
5418 move_panel(px[3], LINES / 4 + 1, COLS / 15);
5419 pflush();
5420 wait_a_while(nap_msec);
5421
5422 canned_panel(px, "b3");
5423 canned_panel(px, "s4");
5424 canned_panel(px, "s5");
5425 canned_panel(px, "t3");
5426 canned_panel(px, "t1");
5427 canned_panel(px, "t2");
5428 canned_panel(px, "t3");
5429 canned_panel(px, "t4");
5430
5431 for (itmp = 0; itmp < 6; itmp++) {
5432 WINDOW *w4 = panel_window(px[4]);
5433 WINDOW *w5 = panel_window(px[5]);
5434
5435 saywhat("m4");
5436 wmove(w4, LINES / 8, 1);
5437 waddstr(w4, mod[itmp]);
5438 move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5439 wmove(w5, LINES / 6, 1);
5440 waddstr(w5, mod[itmp]);
5441 pflush();
5442 wait_a_while(nap_msec);
5443
5444 saywhat("m5");
5445 wmove(w4, LINES / 6, 1);
5446 waddstr(w4, mod[itmp]);
5447 move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
5448 wmove(w5, LINES / 8, 1);
5449 waddstr(w5, mod[itmp]);
5450 pflush();
5451 wait_a_while(nap_msec);
5452 }
5453
5454 saywhat("m4");
5455 move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5456 pflush();
5457 wait_a_while(nap_msec);
5458
5459 canned_panel(px, "t5");
5460 canned_panel(px, "t2");
5461 canned_panel(px, "t1");
5462 canned_panel(px, "d2");
5463 canned_panel(px, "h3");
5464 canned_panel(px, "d1");
5465 canned_panel(px, "d4");
5466 canned_panel(px, "d5");
5467 canned_panel(px, "d3");
5468
5469 wait_a_while(nap_msec);
5470 if (nap_msec == 1)
5471 break;
5472 nap_msec = 100L;
5473 }
5474
5475 erase();
5476 endwin();
micky3879b9f5e72025-07-08 18:04:53 -04005477 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01005478}
micky3879b9f5e72025-07-08 18:04:53 -04005479
5480#if USE_LIBPANEL
5481static int
5482panel_test(bool recur GCC_UNUSED)
5483{
5484 return demo_panels(init_panel, fill_panel);
5485}
5486#endif
5487
5488#if USE_WIDEC_SUPPORT && USE_LIBPANEL
5489static int
5490x_panel_test(bool recur GCC_UNUSED)
5491{
5492 return demo_panels(init_wide_panel, fill_wide_panel);
5493}
5494#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01005495#endif /* USE_LIBPANEL */
5496
5497/****************************************************************************
5498 *
5499 * Pad tester
5500 *
5501 ****************************************************************************/
5502
micky3879b9f5e72025-07-08 18:04:53 -04005503#if HAVE_NEWPAD
5504
5505/* The behavior of mvhline, mvvline for negative/zero length is unspecified,
5506 * though we can rely on negative x/y values to stop the macro.
5507 */
5508static void
5509do_h_line(int y, int x, chtype c, int to)
5510{
5511 if ((to) > (x))
5512 MvHLine(y, x, c, (to) - (x));
5513}
5514
5515static void
5516do_v_line(int y, int x, chtype c, int to)
5517{
5518 if ((to) > (y))
5519 MvVLine(y, x, c, (to) - (y));
5520}
5521
Steve Kondikae271bc2015-11-15 02:50:53 +01005522#define GRIDSIZE 3
5523
5524static bool pending_pan = FALSE;
5525static bool show_panner_legend = TRUE;
5526
5527static int
5528panner_legend(int line)
5529{
5530 static const char *const legend[] =
5531 {
5532 "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
5533 "Use +,- (or j,k) to grow/shrink the panner vertically.",
5534 "Use <,> (or h,l) to grow/shrink the panner horizontally.",
5535 "Number repeats. Toggle legend:? filler:a timer:t scrollmark:s."
5536 };
5537 int n = ((int) SIZEOF(legend) - (LINES - line));
micky3879b9f5e72025-07-08 18:04:53 -04005538 if (n >= 0 && n < (int) SIZEOF(legend)) {
Steve Kondikae271bc2015-11-15 02:50:53 +01005539 if (move(line, 0) != ERR) {
5540 if (show_panner_legend)
5541 printw("%s", legend[n]);
5542 clrtoeol();
5543 return show_panner_legend;
5544 }
5545 }
5546 return FALSE;
5547}
5548
5549static void
5550panner_h_cleanup(int from_y, int from_x, int to_x)
5551{
5552 if (!panner_legend(from_y))
5553 do_h_line(from_y, from_x, ' ', to_x);
5554}
5555
5556static void
5557panner_v_cleanup(int from_y, int from_x, int to_y)
5558{
5559 if (!panner_legend(from_y))
5560 do_v_line(from_y, from_x, ' ', to_y);
5561}
5562
5563static void
5564fill_pad(WINDOW *panpad, bool pan_lines, bool colored)
5565{
5566 int y, x;
5567 unsigned gridcount = 0;
5568 chtype fill = 0;
5569#ifdef A_COLOR
5570 if (colored)
5571 fill = (chtype) COLOR_PAIR(1);
5572#endif
5573
5574 wmove(panpad, 0, 0);
5575 for (y = 0; y < getmaxy(panpad); y++) {
5576 for (x = 0; x < getmaxx(panpad); x++) {
5577 if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
5578 if (y == 0 && x == 0)
5579 waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
5580 else if (y == 0)
5581 waddch(panpad, pan_lines ? ACS_TTEE : '+');
5582 else if (y == 0 || x == 0)
5583 waddch(panpad, pan_lines ? ACS_LTEE : '+');
5584 else
5585 waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
5586 (int) (gridcount++ % 26)) | fill);
5587 } else if (y % GRIDSIZE == 0)
5588 waddch(panpad, pan_lines ? ACS_HLINE : '-');
5589 else if (x % GRIDSIZE == 0)
5590 waddch(panpad, pan_lines ? ACS_VLINE : '|');
5591 else
5592 waddch(panpad, ' ');
5593 }
5594 }
5595}
5596
5597static void
5598panner(WINDOW *pad,
5599 int top_x, int top_y, int porty, int portx,
5600 int (*pgetc) (WINDOW *),
5601 bool colored)
5602{
micky3879b9f5e72025-07-08 18:04:53 -04005603 TimeType before, after;
Steve Kondikae271bc2015-11-15 02:50:53 +01005604 bool timing = TRUE;
Steve Kondikae271bc2015-11-15 02:50:53 +01005605 bool pan_lines = FALSE;
5606 bool scrollers = TRUE;
5607 int basex = 0;
5608 int basey = 0;
5609 int pxmax, pymax, lowend, highend, c;
5610
5611 getmaxyx(pad, pymax, pxmax);
5612 scrollok(stdscr, FALSE); /* we don't want stdscr to scroll! */
5613
5614 c = KEY_REFRESH;
5615 do {
5616#ifdef NCURSES_VERSION
5617 /*
5618 * During shell-out, the user may have resized the window. Adjust
5619 * the port size of the pad to accommodate this. Ncurses automatically
5620 * resizes all of the normal windows to fit on the new screen.
5621 */
5622 if (top_x > COLS)
5623 top_x = COLS;
5624 if (portx > COLS)
5625 portx = COLS;
5626 if (top_y > LINES)
5627 top_y = LINES;
5628 if (porty > LINES)
5629 porty = LINES;
5630#endif
5631 switch (c) {
5632 case KEY_REFRESH:
5633 erase();
5634
5635 /* FALLTHRU */
micky3879b9f5e72025-07-08 18:04:53 -04005636 case HELP_KEY_1:
5637 if (c == HELP_KEY_1)
Steve Kondikae271bc2015-11-15 02:50:53 +01005638 show_panner_legend = !show_panner_legend;
5639 panner_legend(LINES - 4);
5640 panner_legend(LINES - 3);
5641 panner_legend(LINES - 2);
5642 panner_legend(LINES - 1);
5643 break;
5644 case 'a':
5645 pan_lines = !pan_lines;
5646 fill_pad(pad, pan_lines, colored);
5647 pending_pan = FALSE;
5648 break;
5649
Steve Kondikae271bc2015-11-15 02:50:53 +01005650 case 't':
5651 timing = !timing;
5652 if (!timing)
5653 panner_legend(LINES - 1);
5654 break;
Steve Kondikae271bc2015-11-15 02:50:53 +01005655 case 's':
5656 scrollers = !scrollers;
5657 break;
5658
5659 /* Move the top-left corner of the pad, keeping the bottom-right
5660 * corner fixed.
5661 */
5662 case 'h': /* increase-columns: move left edge to left */
5663 if (top_x <= 0)
5664 beep();
5665 else {
5666 panner_v_cleanup(top_y, top_x, porty);
5667 top_x--;
5668 }
5669 break;
5670
5671 case 'j': /* decrease-lines: move top-edge down */
5672 if (top_y >= porty)
5673 beep();
5674 else {
5675 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5676 top_y++;
5677 }
5678 break;
5679
5680 case 'k': /* increase-lines: move top-edge up */
5681 if (top_y <= 0)
5682 beep();
5683 else {
5684 top_y--;
5685 panner_h_cleanup(top_y, top_x, portx);
5686 }
5687 break;
5688
5689 case 'l': /* decrease-columns: move left-edge to right */
5690 if (top_x >= portx)
5691 beep();
5692 else {
5693 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5694 top_x++;
5695 }
5696 break;
5697
5698 /* Move the bottom-right corner of the pad, keeping the top-left
5699 * corner fixed.
5700 */
5701 case KEY_IC: /* increase-columns: move right-edge to right */
5702 if (portx >= pxmax || portx >= COLS)
5703 beep();
5704 else {
5705 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5706 ++portx;
5707 }
5708 break;
5709
5710 case KEY_IL: /* increase-lines: move bottom-edge down */
5711 if (porty >= pymax || porty >= LINES)
5712 beep();
5713 else {
5714 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5715 ++porty;
5716 }
5717 break;
5718
5719 case KEY_DC: /* decrease-columns: move bottom edge up */
5720 if (portx <= top_x)
5721 beep();
5722 else {
5723 portx--;
5724 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5725 }
5726 break;
5727
5728 case KEY_DL: /* decrease-lines */
5729 if (porty <= top_y)
5730 beep();
5731 else {
5732 porty--;
5733 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5734 }
5735 break;
5736
5737 case KEY_LEFT: /* pan leftwards */
5738 if (basex > 0)
5739 basex--;
5740 else
5741 beep();
5742 break;
5743
5744 case KEY_RIGHT: /* pan rightwards */
5745 if (basex + portx - (pymax > porty) < pxmax)
5746 basex++;
5747 else
5748 beep();
5749 break;
5750
5751 case KEY_UP: /* pan upwards */
5752 if (basey > 0)
5753 basey--;
5754 else
5755 beep();
5756 break;
5757
5758 case KEY_DOWN: /* pan downwards */
5759 if (basey + porty - (pxmax > portx) < pymax)
5760 basey++;
5761 else
5762 beep();
5763 break;
5764
5765 case 'H':
5766 case KEY_HOME:
5767 case KEY_FIND:
5768 basey = 0;
5769 break;
5770
5771 case 'E':
5772 case KEY_END:
5773 case KEY_SELECT:
5774 basey = pymax - porty;
5775 if (basey < 0)
5776 basey = 0;
5777 break;
5778
5779 default:
5780 beep();
5781 break;
5782 }
5783
5784 MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5785 do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5786 do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5787
5788 if (scrollers && (pxmax > portx - 1)) {
5789 int length = (portx - top_x - 1);
5790 float ratio = ((float) length) / ((float) pxmax);
5791
5792 lowend = (int) ((float) top_x + ((float) basex * ratio));
5793 highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5794
5795 do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5796 if (highend < portx) {
5797 attron(A_REVERSE);
5798 do_h_line(porty - 1, lowend, ' ', highend + 1);
5799 attroff(A_REVERSE);
5800 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5801 }
5802 } else
5803 do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5804
5805 if (scrollers && (pymax > porty - 1)) {
5806 int length = (porty - top_y - 1);
5807 float ratio = ((float) length) / ((float) pymax);
5808
5809 lowend = (int) ((float) top_y + ((float) basey * ratio));
5810 highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5811
5812 do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5813 if (highend < porty) {
5814 attron(A_REVERSE);
5815 do_v_line(lowend, portx - 1, ' ', highend + 1);
5816 attroff(A_REVERSE);
5817 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5818 }
5819 } else
5820 do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5821
5822 MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5823 MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5824 MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5825
5826 if (!pending_pan) {
micky3879b9f5e72025-07-08 18:04:53 -04005827 GetClockTime(&before);
Steve Kondikae271bc2015-11-15 02:50:53 +01005828 wnoutrefresh(stdscr);
5829
5830 pnoutrefresh(pad,
5831 basey, basex,
5832 top_y, top_x,
5833 porty - (pxmax > portx) - 1,
5834 portx - (pymax > porty) - 1);
5835
5836 doupdate();
Steve Kondikae271bc2015-11-15 02:50:53 +01005837 if (timing) {
micky3879b9f5e72025-07-08 18:04:53 -04005838 GetClockTime(&after);
Steve Kondikae271bc2015-11-15 02:50:53 +01005839 move(LINES - 1, COLS - 12);
micky3879b9f5e72025-07-08 18:04:53 -04005840 printw("Secs: %6.03f", ElapsedSeconds(&before, &after));
Steve Kondikae271bc2015-11-15 02:50:53 +01005841 refresh();
5842 }
Steve Kondikae271bc2015-11-15 02:50:53 +01005843 }
5844
5845 } while
5846 ((c = pgetc(pad)) != KEY_EXIT);
5847
5848 scrollok(stdscr, TRUE); /* reset to driver's default */
5849}
5850
5851static int
5852padgetch(WINDOW *win)
5853{
5854 static int count;
5855 static int last;
Steve Kondikae271bc2015-11-15 02:50:53 +01005856
5857 if ((pending_pan = (count > 0)) != FALSE) {
5858 count--;
5859 pending_pan = (count != 0);
5860 } else {
5861 for (;;) {
micky3879b9f5e72025-07-08 18:04:53 -04005862 int c;
Steve Kondikae271bc2015-11-15 02:50:53 +01005863 switch (c = wGetchar(win)) {
5864 case '!':
5865 ShellOut(FALSE);
5866 /* FALLTHRU */
5867 case CTRL('r'):
5868 endwin();
5869 refresh();
5870 c = KEY_REFRESH;
5871 break;
5872 case CTRL('l'):
5873 c = KEY_REFRESH;
5874 break;
5875 case 'U':
5876 c = KEY_UP;
5877 break;
5878 case 'D':
5879 c = KEY_DOWN;
5880 break;
5881 case 'R':
5882 c = KEY_RIGHT;
5883 break;
5884 case 'L':
5885 c = KEY_LEFT;
5886 break;
5887 case '+':
5888 c = KEY_IL;
5889 break;
5890 case '-':
5891 c = KEY_DL;
5892 break;
5893 case '>':
5894 c = KEY_IC;
5895 break;
5896 case '<':
5897 c = KEY_DC;
5898 break;
5899 case ERR: /* FALLTHRU */
5900 case case_QUIT:
5901 count = 0;
5902 c = KEY_EXIT;
5903 break;
5904 default:
5905 if (c >= '0' && c <= '9') {
5906 count = count * 10 + (c - '0');
5907 continue;
5908 }
5909 break;
5910 }
5911 last = c;
5912 break;
5913 }
5914 if (count > 0)
5915 count--;
5916 }
5917 return (last);
5918}
5919
5920#define PAD_HIGH 200
5921#define PAD_WIDE 200
5922
micky3879b9f5e72025-07-08 18:04:53 -04005923static int
5924pad_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01005925/* Demonstrate pads. */
5926{
5927 WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5928
5929 if (panpad == 0) {
5930 Cannot("cannot create requested pad");
micky3879b9f5e72025-07-08 18:04:53 -04005931 return ERR;
Steve Kondikae271bc2015-11-15 02:50:53 +01005932 }
Steve Kondikae271bc2015-11-15 02:50:53 +01005933#ifdef A_COLOR
micky3879b9f5e72025-07-08 18:04:53 -04005934 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01005935 init_pair(1, COLOR_BLACK, COLOR_GREEN);
5936 init_pair(2, COLOR_CYAN, COLOR_BLUE);
5937 wbkgd(panpad, (chtype) (COLOR_PAIR(2) | ' '));
5938 }
5939#endif
micky3879b9f5e72025-07-08 18:04:53 -04005940 fill_pad(panpad, FALSE, TRUE);
Steve Kondikae271bc2015-11-15 02:50:53 +01005941
5942 panner_legend(LINES - 4);
5943 panner_legend(LINES - 3);
5944 panner_legend(LINES - 2);
5945 panner_legend(LINES - 1);
5946
5947 keypad(panpad, TRUE);
5948
5949 /* Make the pad (initially) narrow enough that a trace file won't wrap.
5950 * We'll still be able to widen it during a test, since that's required
5951 * for testing boundaries.
5952 */
micky3879b9f5e72025-07-08 18:04:53 -04005953 panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch, TRUE);
Steve Kondikae271bc2015-11-15 02:50:53 +01005954
5955 delwin(panpad);
5956 endwin();
5957 erase();
micky3879b9f5e72025-07-08 18:04:53 -04005958 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01005959}
micky3879b9f5e72025-07-08 18:04:53 -04005960#endif /* HAVE_NEWPAD */
Steve Kondikae271bc2015-11-15 02:50:53 +01005961
5962/****************************************************************************
5963 *
5964 * Tests from John Burnell's PDCurses tester
5965 *
5966 ****************************************************************************/
5967
5968static void
5969Continue(WINDOW *win)
5970{
5971 noecho();
5972 wmove(win, 10, 1);
5973 MvWAddStr(win, 10, 1, " Press any key to continue");
5974 wrefresh(win);
5975 wGetchar(win);
5976}
5977
micky3879b9f5e72025-07-08 18:04:53 -04005978static int
5979flushinp_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01005980/* Input test, adapted from John Burnell's PDCurses tester */
5981{
micky3879b9f5e72025-07-08 18:04:53 -04005982 WINDOW *win = stdscr;
Steve Kondikae271bc2015-11-15 02:50:53 +01005983 int w, h, bx, by, sw, sh, i;
5984
5985 WINDOW *subWin;
5986 wclear(win);
5987
5988 getmaxyx(win, h, w);
5989 getbegyx(win, by, bx);
5990 sw = w / 3;
5991 sh = h / 3;
5992 if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
micky3879b9f5e72025-07-08 18:04:53 -04005993 return ERR;
Steve Kondikae271bc2015-11-15 02:50:53 +01005994
5995#ifdef A_COLOR
micky3879b9f5e72025-07-08 18:04:53 -04005996 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01005997 init_pair(2, COLOR_CYAN, COLOR_BLUE);
5998 wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
5999 }
6000#endif
6001 (void) wattrset(subWin, A_BOLD);
6002 box(subWin, ACS_VLINE, ACS_HLINE);
6003 MvWAddStr(subWin, 2, 1, "This is a subwindow");
6004 wrefresh(win);
6005
6006 /*
6007 * This used to set 'nocbreak()'. However, Alexander Lukyanov says that
6008 * it only happened to "work" on SVr4 because that implementation does not
6009 * emulate nocbreak+noecho mode, whereas ncurses does. To get the desired
6010 * test behavior, we're using 'cbreak()', which will allow a single
6011 * character to return without needing a newline. - T.Dickey 1997/10/11.
6012 */
6013 cbreak();
6014 MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
6015
6016 MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
6017 MvWAddStr(win, 3, 1,
6018 "These should be discarded (not echoed) after the subwindow goes away.");
6019 wrefresh(win);
6020
6021 for (i = 0; i < 5; i++) {
6022 MvWPrintw(subWin, 1, 1, "Time = %d", i);
6023 wrefresh(subWin);
6024 napms(1000);
6025 flushinp();
6026 }
6027
6028 delwin(subWin);
6029 werase(win);
6030 flash();
6031 wrefresh(win);
6032 napms(1000);
6033
6034 MvWAddStr(win, 2, 1,
6035 "If you were still typing when the window timer expired,");
6036 MvWAddStr(win, 3, 1,
6037 "or else you typed nothing at all while it was running,");
6038 MvWAddStr(win, 4, 1,
6039 "test was invalid. You'll see garbage or nothing at all. ");
6040 MvWAddStr(win, 6, 1, "Press a key");
6041 wmove(win, 9, 10);
6042 wrefresh(win);
6043 echo();
6044 wGetchar(win);
6045 flushinp();
6046 MvWAddStr(win, 12, 0,
6047 "If you see any key other than what you typed, flushinp() is broken.");
6048 Continue(win);
6049
6050 wmove(win, 9, 10);
6051 wdelch(win);
6052 wrefresh(win);
6053 wmove(win, 12, 0);
6054 clrtoeol();
6055 waddstr(win,
6056 "What you typed should now have been deleted; if not, wdelch() failed.");
6057 Continue(win);
6058
6059 cbreak();
micky3879b9f5e72025-07-08 18:04:53 -04006060 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01006061}
6062
6063/****************************************************************************
6064 *
6065 * Menu test
6066 *
6067 ****************************************************************************/
6068
6069#if USE_LIBMENU
6070
6071#define MENU_Y 8
6072#define MENU_X 8
6073
6074static int
6075menu_virtualize(int c)
6076{
6077 if (c == '\n' || c == KEY_EXIT)
6078 return (MAX_COMMAND + 1);
6079 else if (c == 'u')
6080 return (REQ_SCR_ULINE);
6081 else if (c == 'd')
6082 return (REQ_SCR_DLINE);
6083 else if (c == 'b' || c == KEY_NPAGE)
6084 return (REQ_SCR_UPAGE);
6085 else if (c == 'f' || c == KEY_PPAGE)
6086 return (REQ_SCR_DPAGE);
6087 else if (c == 'n' || c == KEY_DOWN)
6088 return (REQ_NEXT_ITEM);
6089 else if (c == 'p' || c == KEY_UP)
6090 return (REQ_PREV_ITEM);
6091 else if (c == ' ')
6092 return (REQ_TOGGLE_ITEM);
6093 else {
6094 if (c != KEY_MOUSE)
6095 beep();
6096 return (c);
6097 }
6098}
6099
6100static CONST_MENUS char *animals[] =
6101{
6102 "Lions",
6103 "Tigers",
6104 "Bears",
6105 "(Oh my!)",
6106 "Newts",
6107 "Platypi",
6108 "Lemurs",
6109 "(Oh really?!)",
6110 "Leopards",
6111 "Panthers",
6112 "Pumas",
6113 "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6114 "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6115 (char *) 0
6116};
6117
micky3879b9f5e72025-07-08 18:04:53 -04006118static int
6119menu_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01006120{
6121 MENU *m;
6122 ITEM *items[SIZEOF(animals)];
6123 ITEM **ip = items;
6124 CONST_MENUS char **ap;
6125 int mrows, mcols, c;
6126 WINDOW *menuwin;
6127
6128#ifdef NCURSES_MOUSE_VERSION
6129 mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6130#endif
6131 MvAddStr(0, 0, "This is the menu test:");
6132 MvAddStr(2, 0, " Use up and down arrow to move the select bar.");
6133 MvAddStr(3, 0, " 'n' and 'p' act like arrows.");
6134 MvAddStr(4, 0,
6135 " 'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
6136 MvAddStr(5, 0, " Press return to exit.");
6137 refresh();
6138
6139 for (ap = animals; *ap; ap++) {
6140 if ((*ip = new_item(*ap, "")) != 0)
6141 ++ip;
6142 }
6143 *ip = (ITEM *) 0;
6144
6145 m = new_menu(items);
6146
6147 set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
6148 scale_menu(m, &mrows, &mcols);
6149
6150 menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6151 set_menu_win(m, menuwin);
6152 keypad(menuwin, TRUE);
6153 box(menuwin, 0, 0);
6154
6155 set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6156
6157 post_menu(m);
6158
6159 while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
6160 if (c == E_NOT_POSTED)
6161 break;
6162 if (c == E_REQUEST_DENIED)
6163 beep();
Steve Kondikae271bc2015-11-15 02:50:53 +01006164 }
6165
6166 MvPrintw(LINES - 2, 0,
6167 "You chose: %s\n", item_name(current_item(m)));
6168 (void) addstr("Press any key to continue...");
6169 wGetchar(stdscr);
6170
6171 unpost_menu(m);
6172 delwin(menuwin);
6173
6174 free_menu(m);
6175 for (ip = items; *ip; ip++)
6176 free_item(*ip);
6177#ifdef NCURSES_MOUSE_VERSION
6178 mousemask(0, (mmask_t *) 0);
6179#endif
micky3879b9f5e72025-07-08 18:04:53 -04006180 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01006181}
6182
6183#ifdef TRACE
6184#define T_TBL(name) { #name, name }
6185static struct {
6186 const char *name;
6187 unsigned mask;
6188} t_tbl[] = {
6189
6190 T_TBL(TRACE_DISABLE),
6191 T_TBL(TRACE_TIMES),
6192 T_TBL(TRACE_TPUTS),
6193 T_TBL(TRACE_UPDATE),
6194 T_TBL(TRACE_MOVE),
6195 T_TBL(TRACE_CHARPUT),
6196 T_TBL(TRACE_ORDINARY),
6197 T_TBL(TRACE_CALLS),
6198 T_TBL(TRACE_VIRTPUT),
6199 T_TBL(TRACE_IEVENT),
6200 T_TBL(TRACE_BITS),
6201 T_TBL(TRACE_ICALLS),
6202 T_TBL(TRACE_CCALLS),
6203 T_TBL(TRACE_DATABASE),
6204 T_TBL(TRACE_ATTRS),
6205 T_TBL(TRACE_MAXIMUM),
6206 {
6207 (char *) 0, 0
6208 }
6209};
6210
6211static char *
6212tracetrace(unsigned tlevel)
6213{
6214 static char *buf;
micky3879b9f5e72025-07-08 18:04:53 -04006215 static size_t need = 12;
Steve Kondikae271bc2015-11-15 02:50:53 +01006216 int n;
6217
6218 if (buf == 0) {
Steve Kondikae271bc2015-11-15 02:50:53 +01006219 for (n = 0; t_tbl[n].name != 0; n++)
6220 need += strlen(t_tbl[n].name) + 2;
6221 buf = typeMalloc(char, need);
6222 if (!buf)
6223 failed("tracetrace");
6224 }
micky3879b9f5e72025-07-08 18:04:53 -04006225 _nc_SPRINTF(buf, _nc_SLIMIT(need) "0x%02x = {", tlevel);
Steve Kondikae271bc2015-11-15 02:50:53 +01006226 if (tlevel == 0) {
micky3879b9f5e72025-07-08 18:04:53 -04006227 _nc_STRCAT(buf, t_tbl[0].name ? t_tbl[0].name : "", need);
6228 _nc_STRCAT(buf, ", ", need);
Steve Kondikae271bc2015-11-15 02:50:53 +01006229 } else {
6230 for (n = 1; t_tbl[n].name != 0; n++)
6231 if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
micky3879b9f5e72025-07-08 18:04:53 -04006232 _nc_STRCAT(buf, t_tbl[n].name, need);
6233 _nc_STRCAT(buf, ", ", need);
Steve Kondikae271bc2015-11-15 02:50:53 +01006234 }
6235 }
6236 if (buf[strlen(buf) - 2] == ',')
6237 buf[strlen(buf) - 2] = '\0';
micky3879b9f5e72025-07-08 18:04:53 -04006238 _nc_STRCAT(buf, "}", need);
6239 return buf;
Steve Kondikae271bc2015-11-15 02:50:53 +01006240}
6241
6242/* fake a dynamically reconfigurable menu using the 0th entry to deselect
6243 * the others
6244 */
6245static int
6246run_trace_menu(MENU * m)
6247{
6248 ITEM **items;
6249 ITEM *i, **p;
6250
6251 for (;;) {
6252 bool changed = FALSE;
6253 switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
6254 case E_UNKNOWN_COMMAND:
6255 return FALSE;
6256 default:
6257 items = menu_items(m);
6258 i = current_item(m);
6259 if (i == items[0]) {
6260 if (item_value(i)) {
6261 for (p = items + 1; *p != 0; p++)
6262 if (item_value(*p)) {
6263 set_item_value(*p, FALSE);
6264 changed = TRUE;
6265 }
6266 }
6267 } else {
6268 for (p = items + 1; *p != 0; p++)
6269 if (item_value(*p)) {
6270 set_item_value(items[0], FALSE);
6271 changed = TRUE;
6272 break;
6273 }
6274 }
6275 if (!changed)
6276 return TRUE;
6277 }
6278 }
6279}
6280
micky3879b9f5e72025-07-08 18:04:53 -04006281static int
6282trace_set(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01006283/* interactively set the trace level */
6284{
6285 MENU *m;
6286 ITEM *items[SIZEOF(t_tbl)];
6287 ITEM **ip = items;
6288 int mrows, mcols;
6289 unsigned newtrace;
6290 int n;
6291 WINDOW *menuwin;
6292
6293 MvAddStr(0, 0, "Interactively set trace level:");
6294 MvAddStr(2, 0, " Press space bar to toggle a selection.");
6295 MvAddStr(3, 0, " Use up and down arrow to move the select bar.");
6296 MvAddStr(4, 0, " Press return to set the trace level.");
6297 MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
6298
6299 refresh();
6300
6301 for (n = 0; t_tbl[n].name != 0; n++) {
6302 if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
6303 ++ip;
6304 }
6305 }
6306 *ip = (ITEM *) 0;
6307
6308 m = new_menu(items);
6309
6310 set_menu_format(m, 0, 2);
6311 scale_menu(m, &mrows, &mcols);
6312
6313 menu_opts_off(m, O_ONEVALUE);
6314 menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6315 set_menu_win(m, menuwin);
6316 keypad(menuwin, TRUE);
6317 box(menuwin, 0, 0);
6318
6319 set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6320
6321 post_menu(m);
6322
6323 for (ip = menu_items(m); *ip; ip++) {
6324 unsigned mask = t_tbl[item_index(*ip)].mask;
6325 if (mask == 0)
6326 set_item_value(*ip, _nc_tracing == 0);
6327 else if ((mask & _nc_tracing) == mask)
6328 set_item_value(*ip, TRUE);
6329 }
6330
micky3879b9f5e72025-07-08 18:04:53 -04006331 while (run_trace_menu(m)) {
6332 /* EMPTY */ ;
6333 }
Steve Kondikae271bc2015-11-15 02:50:53 +01006334
6335 newtrace = 0;
6336 for (ip = menu_items(m); *ip; ip++)
6337 if (item_value(*ip))
6338 newtrace |= t_tbl[item_index(*ip)].mask;
micky3879b9f5e72025-07-08 18:04:53 -04006339 curses_trace(newtrace);
Steve Kondikae271bc2015-11-15 02:50:53 +01006340 Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
6341
6342 MvPrintw(LINES - 2, 0,
6343 "Trace level is %s\n", tracetrace(_nc_tracing));
6344 (void) addstr("Press any key to continue...");
6345 wGetchar(stdscr);
6346
6347 unpost_menu(m);
6348 delwin(menuwin);
6349
6350 free_menu(m);
6351 for (ip = items; *ip; ip++)
6352 free_item(*ip);
micky3879b9f5e72025-07-08 18:04:53 -04006353
6354 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01006355}
6356#endif /* TRACE */
6357#endif /* USE_LIBMENU */
6358
6359/****************************************************************************
6360 *
6361 * Forms test
6362 *
6363 ****************************************************************************/
6364#if USE_LIBFORM
6365static FIELD *
6366make_label(int frow, int fcol, NCURSES_CONST char *label)
6367{
6368 FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
6369
6370 if (f) {
6371 set_field_buffer(f, 0, label);
micky3879b9f5e72025-07-08 18:04:53 -04006372 set_field_opts(f, (int) ((unsigned) field_opts(f) & (unsigned) ~O_ACTIVE));
Steve Kondikae271bc2015-11-15 02:50:53 +01006373 }
6374 return (f);
6375}
6376
6377static FIELD *
6378make_field(int frow, int fcol, int rows, int cols, bool secure)
6379{
6380 FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
6381
6382 if (f) {
6383 set_field_back(f, A_UNDERLINE);
6384 set_field_userptr(f, (void *) 0);
6385 }
6386 return (f);
6387}
6388
6389static void
micky3879b9f5e72025-07-08 18:04:53 -04006390display_form(FORM *f)
Steve Kondikae271bc2015-11-15 02:50:53 +01006391{
6392 WINDOW *w;
6393 int rows, cols;
6394
6395 scale_form(f, &rows, &cols);
6396
6397 if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
6398 set_form_win(f, w);
6399 set_form_sub(f, derwin(w, rows, cols, 1, 2));
6400 box(w, 0, 0);
6401 keypad(w, TRUE);
6402 if (post_form(f) != E_OK)
6403 wrefresh(w);
6404 }
6405}
6406
6407static void
micky3879b9f5e72025-07-08 18:04:53 -04006408erase_form(FORM *f)
Steve Kondikae271bc2015-11-15 02:50:53 +01006409{
6410 WINDOW *w = form_win(f);
6411 WINDOW *s = form_sub(f);
6412
6413 unpost_form(f);
6414 werase(w);
6415 wrefresh(w);
6416 delwin(s);
6417 delwin(w);
6418}
6419
6420static int
micky3879b9f5e72025-07-08 18:04:53 -04006421edit_secure(FIELD *me, int c)
Steve Kondikae271bc2015-11-15 02:50:53 +01006422{
6423 int rows, cols, frow, fcol, nrow, nbuf;
6424
6425 if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
6426 && nbuf > 0) {
6427 char *source = field_buffer(me, 1);
6428 size_t have = (source ? strlen(source) : 0) + 1;
6429 size_t need = 80 + have;
6430 char *temp = malloc(need);
Steve Kondikae271bc2015-11-15 02:50:53 +01006431
6432 if (temp != 0) {
micky3879b9f5e72025-07-08 18:04:53 -04006433 size_t len;
6434 _nc_STRNCPY(temp, source ? source : "", have + 1);
Steve Kondikae271bc2015-11-15 02:50:53 +01006435 len = (size_t) (char *) field_userptr(me);
6436 if (c <= KEY_MAX) {
6437 if (isgraph(c) && (len + 1) < sizeof(temp)) {
6438 temp[len++] = (char) c;
6439 temp[len] = 0;
6440 set_field_buffer(me, 1, temp);
6441 c = '*';
6442 } else {
6443 c = 0;
6444 }
6445 } else {
6446 switch (c) {
6447 case REQ_BEG_FIELD:
6448 case REQ_CLR_EOF:
6449 case REQ_CLR_EOL:
6450 case REQ_DEL_LINE:
6451 case REQ_DEL_WORD:
6452 case REQ_DOWN_CHAR:
6453 case REQ_END_FIELD:
6454 case REQ_INS_CHAR:
6455 case REQ_INS_LINE:
6456 case REQ_LEFT_CHAR:
6457 case REQ_NEW_LINE:
6458 case REQ_NEXT_WORD:
6459 case REQ_PREV_WORD:
6460 case REQ_RIGHT_CHAR:
6461 case REQ_UP_CHAR:
6462 c = 0; /* we don't want to do inline editing */
6463 break;
6464 case REQ_CLR_FIELD:
6465 if (len) {
6466 temp[0] = 0;
6467 set_field_buffer(me, 1, temp);
6468 }
6469 break;
6470 case REQ_DEL_CHAR:
6471 case REQ_DEL_PREV:
6472 if (len) {
6473 temp[--len] = 0;
6474 set_field_buffer(me, 1, temp);
6475 }
6476 break;
6477 }
6478 }
6479 set_field_userptr(me, (void *) len);
6480 free(temp);
6481 }
6482 }
6483 return c;
6484}
6485
6486static int
micky3879b9f5e72025-07-08 18:04:53 -04006487form_virtualize(FORM *f, WINDOW *w)
Steve Kondikae271bc2015-11-15 02:50:53 +01006488{
6489 /* *INDENT-OFF* */
6490 static const struct {
6491 int code;
6492 int result;
6493 } lookup[] = {
6494 { CTRL('A'), REQ_NEXT_CHOICE },
6495 { CTRL('B'), REQ_PREV_WORD },
6496 { CTRL('C'), REQ_CLR_EOL },
6497 { CTRL('D'), REQ_DOWN_FIELD },
6498 { CTRL('E'), REQ_END_FIELD },
6499 { CTRL('F'), REQ_NEXT_PAGE },
6500 { CTRL('G'), REQ_DEL_WORD },
6501 { CTRL('H'), REQ_DEL_PREV },
6502 { CTRL('I'), REQ_INS_CHAR },
6503 { CTRL('K'), REQ_CLR_EOF },
6504 { CTRL('L'), REQ_LEFT_FIELD },
6505 { CTRL('M'), REQ_NEW_LINE },
6506 { CTRL('N'), REQ_NEXT_FIELD },
6507 { CTRL('O'), REQ_INS_LINE },
6508 { CTRL('P'), REQ_PREV_FIELD },
6509 { CTRL('R'), REQ_RIGHT_FIELD },
6510 { CTRL('S'), REQ_BEG_FIELD },
6511 { CTRL('U'), REQ_UP_FIELD },
6512 { CTRL('V'), REQ_DEL_CHAR },
6513 { CTRL('W'), REQ_NEXT_WORD },
6514 { CTRL('X'), REQ_CLR_FIELD },
6515 { CTRL('Y'), REQ_DEL_LINE },
6516 { CTRL('Z'), REQ_PREV_CHOICE },
6517 { ESCAPE, MAX_FORM_COMMAND + 1 },
6518 { KEY_BACKSPACE, REQ_DEL_PREV },
6519 { KEY_DOWN, REQ_DOWN_CHAR },
6520 { KEY_END, REQ_LAST_FIELD },
6521 { KEY_HOME, REQ_FIRST_FIELD },
6522 { KEY_LEFT, REQ_LEFT_CHAR },
6523 { KEY_LL, REQ_LAST_FIELD },
6524 { KEY_NEXT, REQ_NEXT_FIELD },
6525 { KEY_NPAGE, REQ_NEXT_PAGE },
6526 { KEY_PPAGE, REQ_PREV_PAGE },
6527 { KEY_PREVIOUS, REQ_PREV_FIELD },
6528 { KEY_RIGHT, REQ_RIGHT_CHAR },
6529 { KEY_UP, REQ_UP_CHAR },
6530 { QUIT, MAX_FORM_COMMAND + 1 }
6531 };
6532 /* *INDENT-ON* */
6533
6534 static int mode = REQ_INS_MODE;
6535 int c = wGetchar(w);
Steve Kondikae271bc2015-11-15 02:50:53 +01006536 FIELD *me = current_field(f);
6537 bool current = TRUE;
6538
6539 if (c == CTRL(']')) {
6540 if (mode == REQ_INS_MODE) {
6541 mode = REQ_OVL_MODE;
6542 } else {
6543 mode = REQ_INS_MODE;
6544 }
6545 c = mode;
6546 } else {
micky3879b9f5e72025-07-08 18:04:53 -04006547 unsigned n;
Steve Kondikae271bc2015-11-15 02:50:53 +01006548 for (n = 0; n < SIZEOF(lookup); n++) {
6549 if (lookup[n].code == c) {
6550 c = lookup[n].result;
6551 break;
6552 }
6553 }
6554 }
6555 MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
6556
6557 /*
6558 * Force the field that the user is typing into to be in reverse video,
6559 * while the other fields are shown underlined.
6560 */
6561 switch (c) {
6562 case REQ_BEG_FIELD:
6563 case REQ_CLR_EOF:
6564 case REQ_CLR_EOL:
6565 case REQ_CLR_FIELD:
6566 case REQ_DEL_CHAR:
6567 case REQ_DEL_LINE:
6568 case REQ_DEL_PREV:
6569 case REQ_DEL_WORD:
6570 case REQ_END_FIELD:
6571 case REQ_INS_CHAR:
6572 case REQ_INS_LINE:
6573 case REQ_LEFT_CHAR:
6574 case REQ_LEFT_FIELD:
6575 case REQ_NEXT_WORD:
6576 case REQ_RIGHT_CHAR:
6577 current = TRUE;
6578 break;
6579 default:
6580 current = (c < KEY_MAX);
6581 break;
6582 }
6583 if (current) {
6584 c = edit_secure(me, c);
6585 set_field_back(me, A_REVERSE);
6586 } else {
6587 c = edit_secure(me, c);
6588 set_field_back(me, A_UNDERLINE);
6589 }
6590 return c;
6591}
6592
6593static int
micky3879b9f5e72025-07-08 18:04:53 -04006594my_form_driver(FORM *form, int c)
Steve Kondikae271bc2015-11-15 02:50:53 +01006595{
6596 if (c == (MAX_FORM_COMMAND + 1)
6597 && form_driver(form, REQ_VALIDATION) == E_OK)
6598 return (TRUE);
6599 else {
6600 beep();
6601 return (FALSE);
6602 }
6603}
6604
6605#ifdef NCURSES_VERSION
6606#define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
6607#define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
6608#else
6609#define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
6610#define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
6611#endif
6612
6613/*
6614 * Allow a middle initial, optionally with a '.' to end it.
6615 */
6616static
6617FIELDCHECK_CB(mi_field_check)
6618{
6619 char *s = field_buffer(fld, 0);
6620 int state = 0;
6621 int n;
6622
6623 for (n = 0; s[n] != '\0'; ++n) {
6624 switch (state) {
6625 case 0:
6626 if (s[n] == '.') {
6627 if (n != 1)
6628 return FALSE;
6629 state = 2;
6630 } else if (isspace(UChar(s[n]))) {
6631 state = 2;
6632 }
6633 break;
6634 case 2:
6635 if (!isspace(UChar(s[n])))
6636 return FALSE;
6637 break;
6638 }
6639 }
6640
6641 /* force the form to display a leading capital */
6642 if (islower(UChar(s[0]))) {
6643 s[0] = (char) toupper(UChar(s[0]));
6644 set_field_buffer(fld, 0, s);
6645 }
6646 return TRUE;
6647}
6648
6649static
6650CHAR_CHECK_CB(mi_char_check)
6651{
6652 return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6653}
6654
6655/*
6656 * Passwords should be at least 6 characters.
6657 */
6658static
6659FIELDCHECK_CB(pw_field_check)
6660{
6661 char *s = field_buffer(fld, 0);
6662 int n;
6663
6664 for (n = 0; s[n] != '\0'; ++n) {
6665 if (isspace(UChar(s[n]))) {
6666 if (n < 6)
6667 return FALSE;
6668 }
6669 }
6670 return TRUE;
6671}
6672
6673static
6674CHAR_CHECK_CB(pw_char_check)
6675{
6676 return (isgraph(ch) ? TRUE : FALSE);
6677}
6678
micky3879b9f5e72025-07-08 18:04:53 -04006679static int
6680form_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01006681{
Steve Kondikae271bc2015-11-15 02:50:53 +01006682 FORM *form;
6683 FIELD *f[12], *secure;
6684 FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6685 FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
micky3879b9f5e72025-07-08 18:04:53 -04006686 int c;
Steve Kondikae271bc2015-11-15 02:50:53 +01006687 unsigned n = 0;
6688
6689#ifdef NCURSES_MOUSE_VERSION
6690 mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6691#endif
6692
6693 move(18, 0);
6694 addstr("Defined edit/traversal keys: ^Q/ESC- exit form\n");
6695 addstr("^N -- go to next field ^P -- go to previous field\n");
6696 addstr("Home -- go to first field End -- go to last field\n");
6697 addstr("^L -- go to field to left ^R -- go to field to right\n");
6698 addstr("^U -- move upward to field ^D -- move downward to field\n");
6699 addstr("^W -- go to next word ^B -- go to previous word\n");
6700 addstr("^S -- go to start of field ^E -- go to end of field\n");
6701 addstr("^H -- delete previous char ^Y -- delete line\n");
6702 addstr("^G -- delete current word ^C -- clear to end of line\n");
6703 addstr("^K -- clear to end of field ^X -- clear field\n");
6704 addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6705
6706 MvAddStr(4, 57, "Forms Entry Test");
6707
6708 refresh();
6709
6710 /* describe the form */
6711 memset(f, 0, sizeof(f));
6712 f[n++] = make_label(0, 15, "Sample Form");
6713
6714 f[n++] = make_label(2, 0, "Last Name");
6715 f[n++] = make_field(3, 0, 1, 18, FALSE);
6716 set_field_type(f[n - 1], TYPE_ALPHA, 1);
6717
6718 f[n++] = make_label(2, 20, "First Name");
6719 f[n++] = make_field(3, 20, 1, 12, FALSE);
6720 set_field_type(f[n - 1], TYPE_ALPHA, 1);
6721
6722 f[n++] = make_label(2, 34, "Middle Name");
6723 f[n++] = make_field(3, 34, 1, 12, FALSE);
6724 set_field_type(f[n - 1], fty_middle);
6725
6726 f[n++] = make_label(5, 0, "Comments");
6727 f[n++] = make_field(6, 0, 4, 46, FALSE);
6728
6729 f[n++] = make_label(5, 20, "Password:");
6730 secure =
6731 f[n++] = make_field(5, 30, 1, 9, TRUE);
6732 set_field_type(f[n - 1], fty_passwd);
6733 f[n] = (FIELD *) 0;
6734
6735 if ((form = new_form(f)) != 0) {
micky3879b9f5e72025-07-08 18:04:53 -04006736 WINDOW *w;
6737 int finished = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01006738
6739 display_form(form);
6740
6741 w = form_win(form);
6742 raw();
6743 nonl(); /* lets us read ^M's */
6744 while (!finished) {
6745 switch (form_driver(form, c = form_virtualize(form, w))) {
6746 case E_OK:
6747 MvAddStr(5, 57, field_buffer(secure, 1));
6748 clrtoeol();
6749 refresh();
6750 break;
6751 case E_UNKNOWN_COMMAND:
6752 finished = my_form_driver(form, c);
6753 break;
6754 default:
6755 beep();
6756 break;
6757 }
6758 }
6759
6760 erase_form(form);
6761
6762 free_form(form);
6763 }
6764 for (c = 0; f[c] != 0; c++)
6765 free_field(f[c]);
6766 free_fieldtype(fty_middle);
6767 free_fieldtype(fty_passwd);
6768 noraw();
6769 nl();
6770
6771#ifdef NCURSES_MOUSE_VERSION
6772 mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6773#endif
micky3879b9f5e72025-07-08 18:04:53 -04006774 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01006775}
6776#endif /* USE_LIBFORM */
6777
6778/****************************************************************************
6779 *
6780 * Overlap test
6781 *
6782 ****************************************************************************/
6783
micky3879b9f5e72025-07-08 18:04:53 -04006784#if HAVE_COPYWIN /* ...and overlay, overwrite */
6785
6786static const int overlap_HEAD = 1;
6787static const int overlap_FOOT = 6;
6788
6789static WINDOW *
6790make_overlap(int n)
6791{
6792 WINDOW *result;
6793 int y, x;
6794
6795 getmaxyx(stdscr, y, x);
6796 if (y < 23 || x < 80) {
6797 Cannot("The screen is too small for this test");
6798 result = 0;
6799 } else {
6800 int ymax = y - (overlap_HEAD + overlap_FOOT);
6801 int high = ymax / 5; /* equal-sized parts for cross */
6802 int xmax = x - 2; /* margin */
6803 int wide = (xmax / 5) & ~1;
6804 int lmar, tmar;
6805
6806 if (high > 8)
6807 high = 8;
6808
6809 if (wide > 8)
6810 wide = 8;
6811
6812 tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6813 lmar = (xmax - (5 * wide)) / 2;
6814
6815 if (n == 0) {
6816 result = newwin(3 * high, 3 * wide, tmar, lmar);
6817 } else {
6818 result = newwin(3 * high, 3 * wide, tmar + 2 * high, lmar + 2 * wide);
6819 }
6820 }
6821 return result;
6822}
6823
6824static void
6825clear_overlap(void)
6826{
6827 int row;
6828
6829 for (row = overlap_HEAD; row < LINES - overlap_FOOT; ++row) {
6830 move(row, 0);
6831 clrtoeol();
6832 }
6833}
6834
6835static int
6836move_overlap(int shift, WINDOW *win1)
6837{
6838 int ymax = getmaxy(stdscr) - (overlap_HEAD + overlap_FOOT);
6839 int high = ymax / 5; /* equal-sized parts for cross */
6840 int tmar;
6841 int xmax1 = getmaxx(win1) + 1;
6842 int lmar1 = (COLS - (5 * (xmax1) / 3)) / 2;
6843 int rc = ERR;
6844
6845 if (high > 8)
6846 high = 8;
6847 tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6848
6849 rc = mvwin(win1, tmar, lmar1 + shift);
6850 return rc;
6851}
6852
Steve Kondikae271bc2015-11-15 02:50:53 +01006853static void
6854fillwin(WINDOW *win, char ch)
6855{
6856 int y, x;
6857 int y1, x1;
6858
6859 getmaxyx(win, y1, x1);
6860 for (y = 0; y < y1; y++) {
6861 wmove(win, y, 0);
6862 for (x = 0; x < x1; x++)
6863 waddch(win, UChar(ch));
6864 }
6865}
6866
micky3879b9f5e72025-07-08 18:04:53 -04006867#define InCross(x,y, x1,y1) \
6868 (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3)) \
6869 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3))))
6870
Steve Kondikae271bc2015-11-15 02:50:53 +01006871static void
6872crosswin(WINDOW *win, char ch)
6873{
6874 int y, x;
6875 int y1, x1;
micky3879b9f5e72025-07-08 18:04:53 -04006876 int xw = 1;
Steve Kondikae271bc2015-11-15 02:50:53 +01006877
6878 getmaxyx(win, y1, x1);
6879 for (y = 0; y < y1; y++) {
micky3879b9f5e72025-07-08 18:04:53 -04006880 for (x = 0; x < x1; x += xw) {
6881 if (InCross(x, y, x1, y1)) {
Steve Kondikae271bc2015-11-15 02:50:53 +01006882 wmove(win, y, x);
6883 waddch(win, UChar(ch));
6884 }
micky3879b9f5e72025-07-08 18:04:53 -04006885 }
Steve Kondikae271bc2015-11-15 02:50:53 +01006886 }
6887}
6888
micky3879b9f5e72025-07-08 18:04:53 -04006889/*
6890 * Match "crosswin()", but using line-drawing characters. This could be done
6891 * a little simpler using box(), but the reason for this example is to test
6892 * hline/vline and addch with line-drawing vs the copy/overlay functions.
6893 */
6894static void
6895crossbox(WINDOW *win)
6896{
6897 int y1, x1;
6898 int ymax, xmax;
6899
6900 getmaxyx(win, y1, x1);
6901
6902 ymax = (y1 + 1);
6903 xmax = (x1 + 1);
6904
6905 mvwhline(win, 0, (xmax / 3), ACS_HLINE, (xmax / 3));
6906 mvwhline(win, ymax / 3, 0, ACS_HLINE, xmax);
6907 mvwhline(win, ((2 * ymax) / 3) - 1, 0, ACS_HLINE, xmax);
6908 mvwhline(win, y1 - 1, (xmax / 3), ACS_HLINE, (xmax / 3));
6909
6910 mvwvline(win, (ymax / 3), 0, ACS_VLINE, (ymax / 3));
6911 mvwvline(win, 0, xmax / 3, ACS_VLINE, ymax);
6912 mvwvline(win, 0, ((2 * xmax) / 3) - 1, ACS_VLINE, ymax);
6913 mvwvline(win, (ymax / 3), x1 - 1, ACS_VLINE, (ymax / 3));
6914
6915 mvwaddch(win, 0, (xmax / 3), ACS_ULCORNER);
6916 mvwaddch(win, 0, ((2 * xmax) / 3) - 1, ACS_URCORNER);
6917 mvwaddch(win, y1 - 1, (xmax / 3), ACS_LLCORNER);
6918 mvwaddch(win, y1 - 1, ((2 * xmax) / 3) - 1, ACS_LRCORNER);
6919
6920 mvwaddch(win, (ymax / 3), 0, ACS_ULCORNER);
6921 mvwaddch(win, ((2 * ymax) / 3) - 1, 0, ACS_LLCORNER);
6922 mvwaddch(win, (ymax / 3), x1 - 1, ACS_URCORNER);
6923 mvwaddch(win, ((2 * ymax) / 3) - 1, x1 - 1, ACS_LRCORNER);
6924
6925 mvwaddch(win, (ymax / 3), (xmax / 3), ACS_PLUS);
6926 mvwaddch(win, (ymax / 3), ((2 * xmax) / 3) - 1, ACS_PLUS);
6927 mvwaddch(win, ((2 * ymax) / 3) - 1, ((2 * xmax) / 3) - 1, ACS_PLUS);
6928 mvwaddch(win, ((2 * ymax) / 3) - 1, (xmax / 3), ACS_PLUS);
6929}
6930
6931typedef enum {
6932 otBASE_refresh = 0
6933 ,otBASE_fill
6934 ,otBASE_draw
6935 ,otBASE_clear
6936 ,otBASE_copy
6937} otBASE;
6938
6939#define OVERLAP_FLAVORS 6
6940
6941typedef enum {
6942 otFILL_normal = 0
6943 ,otFILL_bold
6944 ,otFILL_color
6945 ,otFILL_bright
6946} otFILL;
6947
6948#define LimitFILL() UseColors ? 4 : 2
6949
6950typedef enum {
6951 otDRAW_text_cross = 0
6952 ,otDRAW_line_box
6953 ,otDRAW_line_cross
6954 ,otDRAW_set_bg
6955 ,otDRAW_reset_bg
6956} otDRAW;
6957
6958#define LimitDRAW() UseColors ? 5 : 3
6959
6960typedef enum {
6961 otCOPY_overwrite = 0
6962 ,otCOPY_merge
6963 ,otCOPY_force
6964 ,otCOPY_overlay
6965} otCOPY;
6966
6967#define LimitCOPY() 4
Steve Kondikae271bc2015-11-15 02:50:53 +01006968
6969static void
6970overlap_helpitem(int state, int item, char *message)
6971{
6972 int row = (item / 2);
6973 int col = ((item % 2) ? COLS / 2 : 0);
6974
6975 move(LINES - 6 + row, col);
6976 printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6977 clrtoeol();
6978}
6979
6980static void
6981overlap_test_1_attr(WINDOW *win, int flavor, int col)
6982{
6983 NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (1 + (flavor * 2) + col);
6984
micky3879b9f5e72025-07-08 18:04:53 -04006985 switch ((otFILL) flavor) {
6986 case otFILL_normal:
Steve Kondikae271bc2015-11-15 02:50:53 +01006987 (void) wattrset(win, A_NORMAL);
6988 break;
micky3879b9f5e72025-07-08 18:04:53 -04006989 case otFILL_bold:
Steve Kondikae271bc2015-11-15 02:50:53 +01006990 (void) wattrset(win, A_BOLD);
6991 break;
micky3879b9f5e72025-07-08 18:04:53 -04006992 case otFILL_color:
Steve Kondikae271bc2015-11-15 02:50:53 +01006993 init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
6994 (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_NORMAL));
6995 break;
micky3879b9f5e72025-07-08 18:04:53 -04006996 case otFILL_bright:
Steve Kondikae271bc2015-11-15 02:50:53 +01006997 init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6998 (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_BOLD));
6999 break;
7000 }
7001}
7002
7003static void
7004overlap_test_2_attr(WINDOW *win, int flavor, int col)
7005{
7006 NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (9 + (flavor * 2) + col);
7007
micky3879b9f5e72025-07-08 18:04:53 -04007008 switch ((otDRAW) flavor) {
7009 case otDRAW_text_cross:
Steve Kondikae271bc2015-11-15 02:50:53 +01007010 /* no effect */
7011 break;
micky3879b9f5e72025-07-08 18:04:53 -04007012 case otDRAW_line_box:
Steve Kondikae271bc2015-11-15 02:50:53 +01007013 /* no effect */
7014 break;
micky3879b9f5e72025-07-08 18:04:53 -04007015 case otDRAW_line_cross:
7016 /* no effect */
7017 break;
7018 case otDRAW_set_bg:
Steve Kondikae271bc2015-11-15 02:50:53 +01007019 init_pair(cpair, COLOR_RED, COLOR_GREEN);
7020 wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
7021 break;
micky3879b9f5e72025-07-08 18:04:53 -04007022 case otDRAW_reset_bg:
Steve Kondikae271bc2015-11-15 02:50:53 +01007023 wbkgdset(win, ' ' | A_NORMAL);
7024 break;
7025 }
7026}
7027
7028static int
7029overlap_help(int state, int flavors[OVERLAP_FLAVORS])
7030{
Steve Kondikae271bc2015-11-15 02:50:53 +01007031 int item;
micky3879b9f5e72025-07-08 18:04:53 -04007032 int limit[OVERLAP_FLAVORS];
Steve Kondikae271bc2015-11-15 02:50:53 +01007033 char msg[80];
7034
7035 if (state < 0)
7036 state += OVERLAP_FLAVORS;
7037 state = state % OVERLAP_FLAVORS;
7038 assert(state >= 0 && state < OVERLAP_FLAVORS);
7039
7040 for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
micky3879b9f5e72025-07-08 18:04:53 -04007041 int row = item / 2;
7042 int col = item % 2;
7043 const char *ths = col ? "B" : "A";
7044 const char *tht = col ? "A" : "B";
Steve Kondikae271bc2015-11-15 02:50:53 +01007045
micky3879b9f5e72025-07-08 18:04:53 -04007046 switch ((otBASE) row) {
7047 case otBASE_refresh:
7048 limit[row] = 1;
Steve Kondikae271bc2015-11-15 02:50:53 +01007049 flavors[row] = 0;
micky3879b9f5e72025-07-08 18:04:53 -04007050 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7051 "refresh %s, then %s, then doupdate.", ths, tht);
Steve Kondikae271bc2015-11-15 02:50:53 +01007052 break;
micky3879b9f5e72025-07-08 18:04:53 -04007053 case otBASE_fill:
7054 limit[row] = LimitFILL();
7055 flavors[row] %= limit[row];
Steve Kondikae271bc2015-11-15 02:50:53 +01007056 overlap_test_1_attr(stdscr, flavors[row], col);
micky3879b9f5e72025-07-08 18:04:53 -04007057 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7058 "fill window %s with letter %s.", ths, ths);
Steve Kondikae271bc2015-11-15 02:50:53 +01007059 break;
micky3879b9f5e72025-07-08 18:04:53 -04007060 case otBASE_draw:
7061 limit[row] = LimitDRAW();
7062 flavors[row] %= limit[row];
7063 switch ((otDRAW) flavors[row]) {
7064 case otDRAW_text_cross:
7065 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7066 "cross text-pattern in window %s.", ths);
Steve Kondikae271bc2015-11-15 02:50:53 +01007067 break;
micky3879b9f5e72025-07-08 18:04:53 -04007068 case otDRAW_line_box:
7069 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7070 "draw line-box in window %s.", ths);
Steve Kondikae271bc2015-11-15 02:50:53 +01007071 break;
micky3879b9f5e72025-07-08 18:04:53 -04007072 case otDRAW_line_cross:
7073 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7074 "draw line-cross in window %s.", ths);
Steve Kondikae271bc2015-11-15 02:50:53 +01007075 break;
micky3879b9f5e72025-07-08 18:04:53 -04007076 case otDRAW_set_bg:
7077 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7078 "set background of window %s.", ths);
7079 break;
7080 case otDRAW_reset_bg:
7081 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7082 "reset background of window %s.", ths);
Steve Kondikae271bc2015-11-15 02:50:53 +01007083 break;
7084 }
7085 break;
micky3879b9f5e72025-07-08 18:04:53 -04007086 case otBASE_clear:
7087 limit[row] = 1;
Steve Kondikae271bc2015-11-15 02:50:53 +01007088 flavors[row] = 0;
micky3879b9f5e72025-07-08 18:04:53 -04007089 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7090 "clear window %s.", ths);
Steve Kondikae271bc2015-11-15 02:50:53 +01007091 break;
micky3879b9f5e72025-07-08 18:04:53 -04007092 case otBASE_copy:
7093 limit[row] = LimitCOPY();
7094 flavors[row] %= limit[row];
7095 switch ((otCOPY) flavors[row]) {
7096 case otCOPY_overwrite:
7097 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7098 "overwrite %s onto %s.", ths, tht);
Steve Kondikae271bc2015-11-15 02:50:53 +01007099 break;
micky3879b9f5e72025-07-08 18:04:53 -04007100 case otCOPY_merge:
7101 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7102 "copywin(FALSE) %s onto %s.", ths, tht);
Steve Kondikae271bc2015-11-15 02:50:53 +01007103 break;
micky3879b9f5e72025-07-08 18:04:53 -04007104 case otCOPY_force:
7105 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7106 "copywin(TRUE) %s onto %s.", ths, tht);
Steve Kondikae271bc2015-11-15 02:50:53 +01007107 break;
micky3879b9f5e72025-07-08 18:04:53 -04007108 case otCOPY_overlay:
7109 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7110 "overlay %s onto %s.", ths, tht);
Steve Kondikae271bc2015-11-15 02:50:53 +01007111 break;
7112 }
7113 break;
7114 }
7115 overlap_helpitem(state, item, msg);
7116 (void) wattrset(stdscr, A_NORMAL);
7117 wbkgdset(stdscr, ' ' | A_NORMAL);
7118 }
7119 move(LINES - 1, 0);
micky3879b9f5e72025-07-08 18:04:53 -04007120 printw("^Q/ESC = terminate test. </> shift. Up/down/space select (row %d",
7121 state + 1);
7122 if (limit[state] > 1)
7123 printw(" test %d:%d", 1 + flavors[state], limit[state]);
7124 printw(").");
7125 clrtoeol();
Steve Kondikae271bc2015-11-15 02:50:53 +01007126
7127 return state;
7128}
7129
7130static void
7131overlap_test_0(WINDOW *a, WINDOW *b)
7132{
7133 touchwin(a);
7134 touchwin(b);
7135 wnoutrefresh(a);
7136 wnoutrefresh(b);
7137 doupdate();
7138}
7139
7140static void
7141overlap_test_1(int flavor, int col, WINDOW *a, char fill)
7142{
7143 overlap_test_1_attr(a, flavor, col);
7144 fillwin(a, fill);
7145 (void) wattrset(a, A_NORMAL);
7146}
7147
7148static void
7149overlap_test_2(int flavor, int col, WINDOW *a, char fill)
7150{
7151 overlap_test_2_attr(a, flavor, col);
micky3879b9f5e72025-07-08 18:04:53 -04007152 switch ((otDRAW) flavor) {
7153 case otDRAW_text_cross:
Steve Kondikae271bc2015-11-15 02:50:53 +01007154 crosswin(a, fill);
7155 break;
micky3879b9f5e72025-07-08 18:04:53 -04007156 case otDRAW_line_box:
Steve Kondikae271bc2015-11-15 02:50:53 +01007157 box(a, 0, 0);
7158 break;
micky3879b9f5e72025-07-08 18:04:53 -04007159 case otDRAW_line_cross:
7160 crossbox(a);
7161 break;
7162 case otDRAW_set_bg:
Steve Kondikae271bc2015-11-15 02:50:53 +01007163 /* done in overlap_test_2_attr */
7164 break;
micky3879b9f5e72025-07-08 18:04:53 -04007165 case otDRAW_reset_bg:
Steve Kondikae271bc2015-11-15 02:50:53 +01007166 /* done in overlap_test_2_attr */
7167 break;
7168 }
7169}
7170
7171static void
7172overlap_test_3(WINDOW *a)
7173{
7174 wclear(a);
7175 wmove(a, 0, 0);
7176}
7177
7178static void
7179overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
7180{
micky3879b9f5e72025-07-08 18:04:53 -04007181 switch ((otCOPY) flavor) {
7182 case otCOPY_overwrite:
Steve Kondikae271bc2015-11-15 02:50:53 +01007183 overwrite(a, b);
7184 break;
micky3879b9f5e72025-07-08 18:04:53 -04007185 case otCOPY_merge:
Steve Kondikae271bc2015-11-15 02:50:53 +01007186 copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
7187 break;
micky3879b9f5e72025-07-08 18:04:53 -04007188 case otCOPY_force:
Steve Kondikae271bc2015-11-15 02:50:53 +01007189 copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
7190 break;
micky3879b9f5e72025-07-08 18:04:53 -04007191 case otCOPY_overlay:
Steve Kondikae271bc2015-11-15 02:50:53 +01007192 overlay(a, b);
7193 break;
7194 }
7195}
7196
7197/* test effects of overlapping windows */
micky3879b9f5e72025-07-08 18:04:53 -04007198static int
7199overlap_test(bool recur GCC_UNUSED)
Steve Kondikae271bc2015-11-15 02:50:53 +01007200{
micky3879b9f5e72025-07-08 18:04:53 -04007201 WINDOW *win1, *win2;
Steve Kondikae271bc2015-11-15 02:50:53 +01007202 int ch;
micky3879b9f5e72025-07-08 18:04:53 -04007203 int shift = 0, last_refresh = -1;
Steve Kondikae271bc2015-11-15 02:50:53 +01007204 int state, flavor[OVERLAP_FLAVORS];
7205
micky3879b9f5e72025-07-08 18:04:53 -04007206 if ((win1 = make_overlap(0)) == 0) {
7207 return ERR;
7208 } else if ((win2 = make_overlap(1)) == 0) {
7209 delwin(win1);
7210 return ERR;
7211 }
Steve Kondikae271bc2015-11-15 02:50:53 +01007212
7213 curs_set(0);
7214 raw();
7215 refresh();
7216 move(0, 0);
micky3879b9f5e72025-07-08 18:04:53 -04007217 printw("Test wnoutrefresh() for two overlapping windows:");
Steve Kondikae271bc2015-11-15 02:50:53 +01007218
7219 memset(flavor, 0, sizeof(flavor));
7220 state = overlap_help(0, flavor);
7221
micky3879b9f5e72025-07-08 18:04:53 -04007222 while (!isQuit(ch = Getchar(), TRUE)) {
Steve Kondikae271bc2015-11-15 02:50:53 +01007223 switch (ch) {
7224 case 'a': /* refresh window A first, then B */
7225 overlap_test_0(win1, win2);
7226 break;
7227
7228 case 'b': /* refresh window B first, then A */
7229 overlap_test_0(win2, win1);
7230 break;
7231
micky3879b9f5e72025-07-08 18:04:53 -04007232 case 'c': /* fill window A so it is visible */
7233 overlap_test_1(flavor[otBASE_fill], 0, win1, 'A');
Steve Kondikae271bc2015-11-15 02:50:53 +01007234 break;
7235
micky3879b9f5e72025-07-08 18:04:53 -04007236 case 'd': /* fill window B so it is visible */
7237 overlap_test_1(flavor[otBASE_fill], 1, win2, 'B');
Steve Kondikae271bc2015-11-15 02:50:53 +01007238 break;
7239
7240 case 'e': /* cross test pattern in window A */
micky3879b9f5e72025-07-08 18:04:53 -04007241 overlap_test_2(flavor[otBASE_draw], 0, win1, 'A');
Steve Kondikae271bc2015-11-15 02:50:53 +01007242 break;
7243
7244 case 'f': /* cross test pattern in window A */
micky3879b9f5e72025-07-08 18:04:53 -04007245 overlap_test_2(flavor[otBASE_draw], 1, win2, 'B');
Steve Kondikae271bc2015-11-15 02:50:53 +01007246 break;
7247
7248 case 'g': /* clear window A */
7249 overlap_test_3(win1);
7250 break;
7251
7252 case 'h': /* clear window B */
7253 overlap_test_3(win2);
7254 break;
7255
7256 case 'i': /* overwrite A onto B */
micky3879b9f5e72025-07-08 18:04:53 -04007257 overlap_test_4(flavor[otBASE_copy], win1, win2);
Steve Kondikae271bc2015-11-15 02:50:53 +01007258 break;
7259
7260 case 'j': /* overwrite B onto A */
micky3879b9f5e72025-07-08 18:04:53 -04007261 overlap_test_4(flavor[otBASE_copy], win2, win1);
Steve Kondikae271bc2015-11-15 02:50:53 +01007262 break;
7263
7264 case CTRL('n'):
7265 case KEY_DOWN:
7266 state = overlap_help(state + 1, flavor);
7267 break;
7268
7269 case CTRL('p'):
7270 case KEY_UP:
7271 state = overlap_help(state - 1, flavor);
7272 break;
7273
7274 case ' ':
7275 flavor[state] += 1;
7276 state = overlap_help(state, flavor);
7277 break;
7278
micky3879b9f5e72025-07-08 18:04:53 -04007279 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +01007280 state = overlap_help(state, flavor);
7281 break;
7282
micky3879b9f5e72025-07-08 18:04:53 -04007283 case '<':
7284 /* FALLTHRU */
7285 case '>':
7286 /* see below */
7287 break;
7288
Steve Kondikae271bc2015-11-15 02:50:53 +01007289 default:
7290 beep();
7291 break;
7292 }
7293
micky3879b9f5e72025-07-08 18:04:53 -04007294 switch (ch) {
7295 case 'a':
7296 /* FALLTHRU */
7297 case 'b':
7298 last_refresh = ch;
7299 break;
7300 case '<':
7301 shift -= 2;
7302 /* FALLTHRU */
7303 case '>':
7304 shift += 1;
7305 if (move_overlap(shift, win1) != OK) {
7306 flash();
7307 shift += (ch == '>') ? -1 : 1;
7308 } else if (last_refresh > 0) {
7309 clear_overlap();
7310 wnoutrefresh(stdscr);
7311 if (last_refresh == 'a')
7312 overlap_test_0(win1, win2);
7313 else
7314 overlap_test_0(win2, win1);
7315 }
7316 break;
7317 default:
7318 last_refresh = -1;
7319 break;
7320 }
7321 }
7322
Steve Kondikae271bc2015-11-15 02:50:53 +01007323 delwin(win2);
7324 delwin(win1);
7325 erase();
micky3879b9f5e72025-07-08 18:04:53 -04007326 stop_curses();
7327 return OK;
7328}
7329
7330#if USE_WIDEC_SUPPORT
7331static void
7332x_fillwin(WINDOW *win, wchar_t ch)
7333{
7334 int y, x;
7335 int y1, x1;
7336
7337 getmaxyx(win, y1, x1);
7338 x1 /= 2;
7339 for (y = 0; y < y1; y++) {
7340 wmove(win, y, 0);
7341 for (x = 0; x < x1; x++)
7342 waddnwstr(win, &ch, 1);
7343 }
7344}
7345
7346static void
7347x_crosswin(WINDOW *win, wchar_t ch)
7348{
7349 int y, x;
7350 int y1, x1;
7351 int xw = 2;
7352
7353 getmaxyx(win, y1, x1);
7354 for (y = 0; y < y1; y++) {
7355 for (x = 0; x < x1; x += xw) {
7356 if (InCross(x, y, x1, y1)) {
7357 wmove(win, y, x);
7358 waddnwstr(win, &ch, 1);
7359 }
7360 }
7361 }
7362}
7363
7364static void
7365x_overlap_test_1(int flavor, int col, WINDOW *a, wchar_t fill)
7366{
7367 overlap_test_1_attr(a, flavor, col);
7368 x_fillwin(a, fill);
7369 (void) wattrset(a, A_NORMAL);
7370}
7371
7372static void
7373x_overlap_test_2(int flavor, int col, WINDOW *a, wchar_t fill)
7374{
7375 overlap_test_2_attr(a, flavor, col);
7376 switch ((otDRAW) flavor) {
7377 case otDRAW_text_cross:
7378 x_crosswin(a, fill);
7379 break;
7380 case otDRAW_line_box:
7381 box(a, 0, 0);
7382 break;
7383 case otDRAW_line_cross:
7384 crossbox(a);
7385 break;
7386 case otDRAW_set_bg:
7387 /* done in overlap_test_2_attr */
7388 break;
7389 case otDRAW_reset_bg:
7390 /* done in overlap_test_2_attr */
7391 break;
7392 }
7393}
7394
7395/* test effects of overlapping windows */
7396static int
7397x_overlap_test(bool recur GCC_UNUSED)
7398{
7399 const wchar_t WIDE_A = 0xff21;
7400 const wchar_t WIDE_B = 0xff22;
7401 WINDOW *win1, *win2;
7402 int ch;
7403 int shift = 0, last_refresh = -1;
7404 int state, flavor[OVERLAP_FLAVORS];
7405
7406 if ((win1 = make_overlap(0)) == 0) {
7407 return ERR;
7408 } else if ((win2 = make_overlap(1)) == 0) {
7409 delwin(win1);
7410 return ERR;
7411 }
7412
7413 curs_set(0);
7414 raw();
7415 refresh();
7416 move(0, 0);
7417 printw("Test wnoutrefresh() for overlapping windows with double-cell characters:");
7418
7419 memset(flavor, 0, sizeof(flavor));
7420 state = overlap_help(0, flavor);
7421
7422 while (!isQuit(ch = Getchar(), TRUE)) {
7423 switch (ch) {
7424 case 'a': /* refresh window A first, then B */
7425 overlap_test_0(win1, win2);
7426 break;
7427
7428 case 'b': /* refresh window B first, then A */
7429 overlap_test_0(win2, win1);
7430 break;
7431
7432 case 'c': /* fill window A so it is visible */
7433 x_overlap_test_1(flavor[otBASE_fill], 0, win1, WIDE_A);
7434 break;
7435
7436 case 'd': /* fill window B so it is visible */
7437 x_overlap_test_1(flavor[otBASE_fill], 1, win2, WIDE_B);
7438 break;
7439
7440 case 'e': /* cross test pattern in window A */
7441 x_overlap_test_2(flavor[otBASE_draw], 0, win1, WIDE_A);
7442 break;
7443
7444 case 'f': /* cross test pattern in window A */
7445 x_overlap_test_2(flavor[otBASE_draw], 1, win2, WIDE_B);
7446 break;
7447
7448 case 'g': /* clear window A */
7449 overlap_test_3(win1);
7450 break;
7451
7452 case 'h': /* clear window B */
7453 overlap_test_3(win2);
7454 break;
7455
7456 case 'i': /* overwrite A onto B */
7457 overlap_test_4(flavor[otBASE_copy], win1, win2);
7458 break;
7459
7460 case 'j': /* overwrite B onto A */
7461 overlap_test_4(flavor[otBASE_copy], win2, win1);
7462 break;
7463
7464 case CTRL('n'):
7465 case KEY_DOWN:
7466 state = overlap_help(state + 1, flavor);
7467 break;
7468
7469 case CTRL('p'):
7470 case KEY_UP:
7471 state = overlap_help(state - 1, flavor);
7472 break;
7473
7474 case ' ':
7475 flavor[state] += 1;
7476 state = overlap_help(state, flavor);
7477 break;
7478
7479 case HELP_KEY_1:
7480 state = overlap_help(state, flavor);
7481 break;
7482
7483 case '<':
7484 /* FALLTHRU */
7485 case '>':
7486 /* see below */
7487 break;
7488
7489 default:
7490 beep();
7491 break;
7492 }
7493
7494 switch (ch) {
7495 case 'a':
7496 /* FALLTHRU */
7497 case 'b':
7498 last_refresh = ch;
7499 break;
7500 case '<':
7501 shift -= 2;
7502 /* FALLTHRU */
7503 case '>':
7504 shift += 1;
7505 if (move_overlap(shift, win1) != OK) {
7506 flash();
7507 shift += (ch == '>') ? -1 : 1;
7508 } else if (last_refresh > 0) {
7509 clear_overlap();
7510 wnoutrefresh(stdscr);
7511 if (last_refresh == 'a')
7512 overlap_test_0(win1, win2);
7513 else
7514 overlap_test_0(win2, win1);
7515 }
7516 break;
7517 default:
7518 last_refresh = -1;
7519 break;
7520 }
7521 }
7522
7523 delwin(win2);
7524 delwin(win1);
7525 erase();
7526 stop_curses();
7527 return OK;
7528}
7529#endif /* USE_WIDEC_SUPPORT */
7530
7531#endif /* HAVE_COPYWIN */
7532
7533static void
7534show_setting_name(const char *name)
7535{
7536 printw("%-25s ", name);
7537}
7538
7539static void
7540show_string_setting(const char *name, const char *value)
7541{
7542 show_setting_name(name);
7543 if (value) {
7544 printw("\"%s\"", value);
7545 } else {
7546 attron(A_REVERSE);
7547 addstr("<NULL>");
7548 attroff(A_REVERSE);
7549 }
7550 AddCh('\n');
7551}
7552
7553static void
7554show_number_setting(const char *name, int value)
7555{
7556 show_setting_name(name);
7557 if (value >= 0) {
7558 printw("%d", value);
7559 } else {
7560 attron(A_REVERSE);
7561 printw("%d", value);
7562 attroff(A_REVERSE);
7563 }
7564 AddCh('\n');
7565}
7566
7567static void
7568show_boolean_setting(const char *name, int value)
7569{
7570 show_setting_name(name);
7571 if (value >= 0) {
7572 printw("%s", value ? "TRUE" : "FALSE");
7573 } else {
7574 attron(A_REVERSE);
7575 printw("%d", value);
7576 attroff(A_REVERSE);
7577 }
7578 AddCh('\n');
7579}
7580
7581static int
7582settings_test(bool recur GCC_UNUSED)
7583{
7584#if USE_WIDEC_SUPPORT
7585 wchar_t ch;
7586#endif
7587
7588 move(0, 0);
7589 show_string_setting("termname", termname());
7590 show_string_setting("longname", longname());
7591 show_number_setting("baudrate", baudrate());
7592 if (erasechar() > 0) {
7593 show_string_setting("unctrl(erasechar)", unctrl((chtype) erasechar()));
7594 show_string_setting("keyname(erasechar)", keyname(erasechar()));
7595 }
7596 if (killchar() > 0) {
7597 show_string_setting("unctrl(killchar)", unctrl((chtype) killchar()));
7598 show_string_setting("keyname(killchar)", keyname(killchar()));
7599 }
7600#if USE_WIDEC_SUPPORT
7601 if (erasewchar(&ch) == OK) {
7602 show_string_setting("key_name(erasewchar)", key_name(ch));
7603 }
7604 if (killwchar(&ch) == OK) {
7605 show_string_setting("key_name(killwchar)", key_name(ch));
7606 }
7607#endif
7608 show_boolean_setting("has_ic", has_ic());
7609 show_boolean_setting("has_il", has_il());
7610 show_boolean_setting("has_colors", has_colors());
7611#if HAVE_COLOR_CONTENT
7612 show_boolean_setting("can_change_color", can_change_color());
7613#endif
7614 show_setting_name("LINES");
7615 printw("%d\n", LINES);
7616 show_setting_name("COLS");
7617 printw("%d\n", COLS);
7618 Pause();
7619 erase();
7620 stop_curses();
7621 return OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01007622}
7623
7624/****************************************************************************
7625 *
7626 * Main sequence
7627 *
7628 ****************************************************************************/
7629
Steve Kondikae271bc2015-11-15 02:50:53 +01007630static void
micky3879b9f5e72025-07-08 18:04:53 -04007631usage(int ok)
Steve Kondikae271bc2015-11-15 02:50:53 +01007632{
7633 static const char *const tbl[] =
7634 {
7635 "Usage: ncurses [options]"
7636 ,""
micky3879b9f5e72025-07-08 18:04:53 -04007637 ,USAGE_COMMON
Steve Kondikae271bc2015-11-15 02:50:53 +01007638 ,"Options:"
7639#ifdef NCURSES_VERSION
micky3879b9f5e72025-07-08 18:04:53 -04007640 ," -a f,b set default-colors (assumed white-on-black)"
7641 ," -d use default-colors if terminal supports them"
Steve Kondikae271bc2015-11-15 02:50:53 +01007642#endif
7643#if HAVE_USE_ENV
micky3879b9f5e72025-07-08 18:04:53 -04007644 ," -E call use_env(FALSE) to ignore $LINES and $COLUMNS"
Steve Kondikae271bc2015-11-15 02:50:53 +01007645#endif
7646#if USE_SOFTKEYS
micky3879b9f5e72025-07-08 18:04:53 -04007647 ," -e fmt specify format for soft-keys test (e)"
Steve Kondikae271bc2015-11-15 02:50:53 +01007648#endif
7649#if HAVE_RIPOFFLINE
micky3879b9f5e72025-07-08 18:04:53 -04007650 ," -F rip-off footer line (can repeat)"
7651 ," -H rip-off header line (can repeat)"
Steve Kondikae271bc2015-11-15 02:50:53 +01007652#endif
micky3879b9f5e72025-07-08 18:04:53 -04007653 ," -m do not use colors"
7654#if HAVE_COLOR_CONTENT
7655 ," -p file rgb values to use in 'd' rather than ncurses's builtin"
7656#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007657#if USE_LIBPANEL
micky3879b9f5e72025-07-08 18:04:53 -04007658 ," -s msec specify nominal time for panel-demo (default: 1, to hold)"
Steve Kondikae271bc2015-11-15 02:50:53 +01007659#endif
micky3879b9f5e72025-07-08 18:04:53 -04007660#if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_NC_WINDOWS)
7661 ," -T call use_tioctl(TRUE) to allow SIGWINCH to override environment"
Steve Kondikae271bc2015-11-15 02:50:53 +01007662#endif
7663#ifdef TRACE
micky3879b9f5e72025-07-08 18:04:53 -04007664 ," -t mask specify default trace-level (may toggle with ^T)"
7665#endif
7666#if HAVE_COLOR_CONTENT
7667 ," -x use xterm-compatible control for reading color palette"
Steve Kondikae271bc2015-11-15 02:50:53 +01007668#endif
7669 };
7670 size_t n;
7671 for (n = 0; n < SIZEOF(tbl); n++)
7672 fprintf(stderr, "%s\n", tbl[n]);
micky3879b9f5e72025-07-08 18:04:53 -04007673 ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
Steve Kondikae271bc2015-11-15 02:50:53 +01007674}
7675
7676static void
7677set_terminal_modes(void)
7678{
7679 noraw();
7680 cbreak();
7681 noecho();
7682 scrollok(stdscr, TRUE);
7683 idlok(stdscr, TRUE);
7684 keypad(stdscr, TRUE);
7685}
7686
7687#ifdef SIGUSR1
7688static void
7689announce_sig(int sig)
7690{
7691 (void) fprintf(stderr, "Handled signal %d\r\n", sig);
7692}
7693#endif
7694
7695#if HAVE_RIPOFFLINE
7696static int
7697rip_footer(WINDOW *win, int cols)
7698{
7699 wbkgd(win, A_REVERSE);
7700 werase(win);
7701 wmove(win, 0, 0);
7702 wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
7703 wnoutrefresh(win);
7704 return OK;
7705}
7706
7707static int
7708rip_header(WINDOW *win, int cols)
7709{
7710 wbkgd(win, A_REVERSE);
7711 werase(win);
7712 wmove(win, 0, 0);
7713 wprintw(win, "header: window %p, %d columns", (void *) win, cols);
7714 wnoutrefresh(win);
7715 return OK;
7716}
7717#endif /* HAVE_RIPOFFLINE */
7718
7719static void
7720main_menu(bool top)
7721{
Steve Kondikae271bc2015-11-15 02:50:53 +01007722#if USE_WIDEC_SUPPORT
micky3879b9f5e72025-07-08 18:04:53 -04007723 typedef struct {
7724 bool recur;
7725 int (*narrow_func) (bool);
7726 int (*wide_func) (bool);
7727 int code;
7728 const char *help;
7729 } MyCmds;
7730#define BOTH(a) a, x_ ## a
7731#define ONLY(a) a, NULL
7732#define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
7733#else
7734 typedef struct {
7735 bool recur;
7736 int (*narrow_func) (bool);
7737 int code;
7738 const char *help;
7739 } MyCmds;
7740#define BOTH(a) a
7741#define ONLY(a) a
7742#define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
Steve Kondikae271bc2015-11-15 02:50:53 +01007743#endif
micky3879b9f5e72025-07-08 18:04:53 -04007744 /* *INDENT-OFF* */
7745 static MyCmds cmds[] =
7746 {
7747 CMDS(TRUE, BOTH(getch_test), 'a', "keyboard and mouse input test"),
7748 CMDS(TRUE, BOTH(attr_test), 'b', "character attribute test"),
7749 CMDS(TRUE, BOTH(color_test), 'c', "color test pattern"),
7750#if HAVE_COLOR_CONTENT
7751 CMDS(FALSE, ONLY(color_edit), 'd', "edit RGB color values"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007752#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007753#if USE_SOFTKEYS
micky3879b9f5e72025-07-08 18:04:53 -04007754 CMDS(TRUE, BOTH(slk_test), 'e', "exercise soft keys"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007755#endif
micky3879b9f5e72025-07-08 18:04:53 -04007756 CMDS(TRUE, BOTH(acs_test), 'f', "display ACS characters"),
7757 CMDS(TRUE, ONLY(scroll_test), 'g', "display windows and scrolling"),
7758 CMDS(TRUE, ONLY(flushinp_test), 'i', "test flushinp()"),
7759 CMDS(TRUE, ONLY(sgr_attr_test), 'k', "display character attributes"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007760#if USE_LIBMENU
micky3879b9f5e72025-07-08 18:04:53 -04007761 CMDS(TRUE, ONLY(menu_test), 'm', "exercise menu library"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007762#endif
7763#if USE_LIBPANEL
micky3879b9f5e72025-07-08 18:04:53 -04007764 CMDS(TRUE, BOTH(panel_test), 'o', "exercise panel library"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007765#endif
micky3879b9f5e72025-07-08 18:04:53 -04007766#if HAVE_NEWPAD
7767 CMDS(TRUE, ONLY(pad_test), 'p', "exercise pad features"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007768#endif
micky3879b9f5e72025-07-08 18:04:53 -04007769 CMDS(TRUE, ONLY(NULL), 'q', "quit"),
7770#if USE_LIBMENU
7771 CMDS(TRUE, ONLY(form_test), 'r', "exercise form library"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007772#endif
micky3879b9f5e72025-07-08 18:04:53 -04007773#if HAVE_COPYWIN
7774 CMDS(TRUE, BOTH(overlap_test), 's', "overlapping-refresh test"),
7775#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007776#if USE_LIBMENU && defined(TRACE)
micky3879b9f5e72025-07-08 18:04:53 -04007777 CMDS(TRUE, ONLY(trace_set), 't', "set trace level"),
Steve Kondikae271bc2015-11-15 02:50:53 +01007778#endif
micky3879b9f5e72025-07-08 18:04:53 -04007779 CMDS(TRUE, ONLY(settings_test), 'v', "show terminal name and settings"),
7780 CMDS(FALSE, ONLY(NULL), '?', "repeat this command summary")
7781 };
7782 /* *INDENT-ON* */
7783
7784 int (*doit) (bool);
7785 char command;
7786 unsigned n;
7787 do {
7788 printf("This is the ncurses main menu (uppercase for wide-characters)\n");
7789 for (n = 0; n < SIZEOF(cmds); ++n) {
7790 if (top || cmds[n].recur) {
7791 putchar(' ');
7792#if USE_WIDEC_SUPPORT
7793 if (cmds[n].wide_func) {
7794 printf("%c,", toupper(cmds[n].code));
7795 }
7796#endif
7797 printf("%c\t= %s\n", cmds[n].code, cmds[n].help);
7798 }
7799 }
Steve Kondikae271bc2015-11-15 02:50:53 +01007800
7801 (void) fputs("> ", stdout);
7802 (void) fflush(stdout); /* necessary under SVr4 curses */
7803
7804 /*
7805 * This used to be an 'fgets()' call (until 1996/10). However with
7806 * some runtime libraries, mixing stream I/O and 'read()' causes the
7807 * input stream to be flushed when switching between the two.
7808 */
7809 command = 0;
7810 for (;;) {
7811 char ch = '\0';
7812 if (read(fileno(stdin), &ch, (size_t) 1) <= 0) {
micky3879b9f5e72025-07-08 18:04:53 -04007813 int save_err = errno;
7814 perror("\nOOPS");
7815 if (save_err == EINTR) {
7816 clearerr(stdin);
7817 continue;
7818 } else if (command == 0) {
Steve Kondikae271bc2015-11-15 02:50:53 +01007819 command = 'q';
micky3879b9f5e72025-07-08 18:04:53 -04007820 }
Steve Kondikae271bc2015-11-15 02:50:53 +01007821 break;
7822 } else if (command == 0 && !isspace(UChar(ch))) {
7823 command = ch;
7824 } else if (ch == '\n' || ch == '\r') {
7825 if ((command == 'd') && !top) {
7826 (void) fputs("Do not nest test-d\n", stdout);
7827 command = 0;
7828 }
7829 if (command != 0)
7830 break;
7831 (void) fputs("> ", stdout);
7832 (void) fflush(stdout);
7833 }
7834 }
7835
micky3879b9f5e72025-07-08 18:04:53 -04007836 doit = NULL;
7837 for (n = 0; n < SIZEOF(cmds); ++n) {
7838 if (cmds[n].code == command) {
7839 doit = cmds[n].narrow_func;
7840 break;
7841 }
7842#if USE_WIDEC_SUPPORT
7843 if (toupper(cmds[n].code) == command) {
7844 doit = cmds[n].wide_func;
7845 break;
7846 }
7847#endif
7848 }
7849
7850 if (doit != NULL && doit(FALSE) == OK) {
Steve Kondikae271bc2015-11-15 02:50:53 +01007851 /*
micky3879b9f5e72025-07-08 18:04:53 -04007852 * This may be overkill; it is intended to reset everything back
Steve Kondikae271bc2015-11-15 02:50:53 +01007853 * to the initial terminal modes so that tests don't get in
7854 * each other's way.
7855 */
7856 flushinp();
7857 set_terminal_modes();
7858 reset_prog_mode();
7859 clear();
7860 refresh();
7861 endwin();
7862 if (command == '?') {
7863 (void) puts("This is the ncurses capability tester.");
7864 (void)
7865 puts("You may select a test from the main menu by typing the");
7866 (void)
7867 puts("key letter of the choice (the letter to left of the =)");
7868 (void)
7869 puts("at the > prompt. Type `q' to exit.");
7870 }
7871 continue;
7872 }
7873 } while
7874 (command != 'q');
7875}
7876
7877/*+-------------------------------------------------------------------------
7878 main(argc,argv)
7879--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04007880/* *INDENT-OFF* */
7881VERSION_COMMON()
7882/* *INDENT-ON* */
Steve Kondikae271bc2015-11-15 02:50:53 +01007883
7884int
7885main(int argc, char *argv[])
7886{
micky3879b9f5e72025-07-08 18:04:53 -04007887 int ch;
Steve Kondikae271bc2015-11-15 02:50:53 +01007888 int my_e_param = 1;
micky3879b9f5e72025-07-08 18:04:53 -04007889#ifdef NCURSES_VERSION_PATCH
7890#if HAVE_USE_DEFAULT_COLORS
Steve Kondikae271bc2015-11-15 02:50:53 +01007891 int default_fg = COLOR_WHITE;
7892 int default_bg = COLOR_BLACK;
Steve Kondikae271bc2015-11-15 02:50:53 +01007893 bool default_colors = FALSE;
micky3879b9f5e72025-07-08 18:04:53 -04007894#if HAVE_ASSUME_DEFAULT_COLORS
7895 bool assumed_colors = FALSE;
Steve Kondikae271bc2015-11-15 02:50:53 +01007896#endif
micky3879b9f5e72025-07-08 18:04:53 -04007897#endif
7898#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007899 bool monochrome = FALSE;
micky3879b9f5e72025-07-08 18:04:53 -04007900#if HAVE_COLOR_CONTENT
7901 bool xterm_colors = FALSE;
7902 char *palette_file = 0;
7903#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007904
7905 setlocale(LC_ALL, "");
7906
micky3879b9f5e72025-07-08 18:04:53 -04007907 while ((ch = getopt(argc, argv, OPTS_COMMON "a:dEe:FHmp:s:Tt:x")) != -1) {
7908 switch (ch) {
7909#ifdef NCURSES_VERSION_PATCH
7910#if HAVE_USE_DEFAULT_COLORS
7911#if HAVE_ASSUME_DEFAULT_COLORS
Steve Kondikae271bc2015-11-15 02:50:53 +01007912 case 'a':
7913 assumed_colors = TRUE;
7914 switch (sscanf(optarg, "%d,%d", &default_fg, &default_bg)) {
7915 case 0:
7916 default_fg = COLOR_WHITE;
7917 /* FALLTHRU */
7918 case 1:
7919 default_bg = COLOR_BLACK;
7920 break;
7921 }
7922 break;
micky3879b9f5e72025-07-08 18:04:53 -04007923#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007924 case 'd':
7925 default_colors = TRUE;
7926 break;
7927#endif
micky3879b9f5e72025-07-08 18:04:53 -04007928#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007929#if HAVE_USE_ENV
7930 case 'E':
7931 use_env(FALSE);
7932 break;
7933#endif
7934 case 'e':
7935 my_e_param = atoi(optarg);
7936#ifdef NCURSES_VERSION
7937 if (my_e_param > 3) /* allow extended layouts */
micky3879b9f5e72025-07-08 18:04:53 -04007938 usage(FALSE);
Steve Kondikae271bc2015-11-15 02:50:53 +01007939#else
7940 if (my_e_param > 1)
micky3879b9f5e72025-07-08 18:04:53 -04007941 usage(FALSE);
Steve Kondikae271bc2015-11-15 02:50:53 +01007942#endif
7943 break;
7944#if HAVE_RIPOFFLINE
micky3879b9f5e72025-07-08 18:04:53 -04007945 case 'F':
Steve Kondikae271bc2015-11-15 02:50:53 +01007946 ripoffline(-1, rip_footer);
7947 break;
micky3879b9f5e72025-07-08 18:04:53 -04007948 case 'H':
Steve Kondikae271bc2015-11-15 02:50:53 +01007949 ripoffline(1, rip_header);
7950 break;
7951#endif /* HAVE_RIPOFFLINE */
7952 case 'm':
7953 monochrome = TRUE;
7954 break;
micky3879b9f5e72025-07-08 18:04:53 -04007955#if HAVE_COLOR_CONTENT
Steve Kondikae271bc2015-11-15 02:50:53 +01007956 case 'p':
7957 palette_file = optarg;
7958 break;
micky3879b9f5e72025-07-08 18:04:53 -04007959#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01007960#if USE_LIBPANEL
7961 case 's':
7962 nap_msec = (int) atol(optarg);
7963 break;
7964#endif
micky3879b9f5e72025-07-08 18:04:53 -04007965#if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_NC_WINDOWS)
Steve Kondikae271bc2015-11-15 02:50:53 +01007966 case 'T':
7967 use_tioctl(TRUE);
7968 break;
7969#endif
7970#ifdef TRACE
7971 case 't':
7972 save_trace = (unsigned) strtol(optarg, 0, 0);
7973 break;
7974#endif
micky3879b9f5e72025-07-08 18:04:53 -04007975#if HAVE_COLOR_CONTENT
7976 case 'x':
7977 xterm_colors = TRUE;
7978 break;
7979#endif
7980 case OPTS_VERSION:
7981 show_version(argv);
7982 ExitProgram(EXIT_SUCCESS);
Steve Kondikae271bc2015-11-15 02:50:53 +01007983 default:
micky3879b9f5e72025-07-08 18:04:53 -04007984 usage(ch == OPTS_USAGE);
7985 /* NOTREACHED */
Steve Kondikae271bc2015-11-15 02:50:53 +01007986 }
7987 }
7988
7989 /*
7990 * If there's no menus (unlikely for ncurses!), then we'll have to set
7991 * tracing on initially, just in case the user wants to test something that
7992 * doesn't involve wGetchar.
7993 */
7994#ifdef TRACE
7995 /* enable debugging */
7996#if !USE_LIBMENU
micky3879b9f5e72025-07-08 18:04:53 -04007997 curses_trace(save_trace);
Steve Kondikae271bc2015-11-15 02:50:53 +01007998#else
7999 if (!isatty(fileno(stdin)))
micky3879b9f5e72025-07-08 18:04:53 -04008000 curses_trace(save_trace);
Steve Kondikae271bc2015-11-15 02:50:53 +01008001#endif /* USE_LIBMENU */
8002#endif /* TRACE */
8003
8004#if USE_SOFTKEYS
8005 /* tell it we're going to play with soft keys */
8006 slk_init(my_e_param);
8007#endif
8008
8009#ifdef SIGUSR1
8010 /* set up null signal catcher so we can see what interrupts to getch do */
8011 signal(SIGUSR1, announce_sig);
8012#endif
8013
8014 /* we must initialize the curses data structure only once */
8015 initscr();
8016 bkgdset(BLANK);
8017
micky3879b9f5e72025-07-08 18:04:53 -04008018 set_terminal_modes();
8019 def_prog_mode();
Steve Kondikae271bc2015-11-15 02:50:53 +01008020
micky3879b9f5e72025-07-08 18:04:53 -04008021 /* tests, in general, will want these modes */
8022 UseColors = (bool) (monochrome ? FALSE : has_colors());
8023
8024 if (UseColors) {
Steve Kondikae271bc2015-11-15 02:50:53 +01008025 start_color();
8026#ifdef NCURSES_VERSION_PATCH
micky3879b9f5e72025-07-08 18:04:53 -04008027 MaxColors = COLORS; /* was > 16 ? 16 : COLORS */
Steve Kondikae271bc2015-11-15 02:50:53 +01008028#if HAVE_USE_DEFAULT_COLORS
8029 if (default_colors) {
8030 use_default_colors();
micky3879b9f5e72025-07-08 18:04:53 -04008031 MinColors = -1;
Steve Kondikae271bc2015-11-15 02:50:53 +01008032 }
8033#if HAVE_ASSUME_DEFAULT_COLORS
8034 if (assumed_colors)
8035 assume_default_colors(default_fg, default_bg);
8036#endif
8037#endif
8038#else /* normal SVr4 curses */
micky3879b9f5e72025-07-08 18:04:53 -04008039 MaxColors = COLORS; /* was > 8 ? 8 : COLORS */
Steve Kondikae271bc2015-11-15 02:50:53 +01008040#endif
8041 max_pairs = COLOR_PAIRS; /* was > 256 ? 256 : COLOR_PAIRS */
8042
micky3879b9f5e72025-07-08 18:04:53 -04008043#if HAVE_COLOR_CONTENT
Steve Kondikae271bc2015-11-15 02:50:53 +01008044 if (can_change_color()) {
micky3879b9f5e72025-07-08 18:04:53 -04008045 init_all_colors(xterm_colors, palette_file);
Steve Kondikae271bc2015-11-15 02:50:53 +01008046 }
micky3879b9f5e72025-07-08 18:04:53 -04008047#endif
Steve Kondikae271bc2015-11-15 02:50:53 +01008048 }
Steve Kondikae271bc2015-11-15 02:50:53 +01008049
8050 /*
8051 * Return to terminal mode, so we're guaranteed of being able to
8052 * select terminal commands even if the capabilities are wrong.
8053 */
8054 endwin();
8055
8056#if HAVE_CURSES_VERSION
8057 (void) printf("Welcome to %s. Press ? for help.\n", curses_version());
8058#elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
8059 (void) printf("Welcome to ncurses %d.%d.%d. Press ? for help.\n",
8060 NCURSES_VERSION_MAJOR,
8061 NCURSES_VERSION_MINOR,
8062 NCURSES_VERSION_PATCH);
8063#else
8064 (void) puts("Welcome to ncurses. Press ? for help.");
8065#endif
8066
8067 main_menu(TRUE);
8068
8069 ExitProgram(EXIT_SUCCESS);
8070}
8071
8072/* ncurses.c ends here */