ncurses 6.2 - patch 20211018
[ncurses.git] / test / demo_new_pair.c
1 /****************************************************************************
2  * Copyright 2018-2020,2021 Thomas E. Dickey                                *
3  * Copyright 2017 Free Software Foundation, Inc.                            *
4  *                                                                          *
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  * $Id: demo_new_pair.c,v 1.24 2021/02/21 01:24:06 tom Exp $
31  *
32  * Demonstrate the alloc_pair() function.
33  */
34
35 #include <test.priv.h>
36 #include <time.h>
37 #include <popup_msg.h>
38
39 #if HAVE_ALLOC_PAIR && USE_WIDEC_SUPPORT
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44
45 #define MAX_BITS 8              /* all but A_ALTCHARSET */
46 #define MAX_ATTR ((1<<MAX_BITS)-1)
47
48 static bool
49 valid_cap(NCURSES_CONST char *name)
50 {
51     char *value = tigetstr(name);
52     return (value != 0 && value != (char *) -1) ? TRUE : FALSE;
53 }
54
55 static attr_t
56 next_attr(int now)
57 {
58     static bool init = FALSE;
59     static attr_t table[MAX_BITS * MAX_BITS];
60     static int limit = 0;
61
62     if (!init) {
63         int j, k;
64         attr_t bits[MAX_BITS];
65
66         init = TRUE;
67         bits[limit++] = WA_NORMAL;
68         if (valid_cap("smso"))
69             bits[limit++] = WA_STANDOUT;
70         if (valid_cap("smul"))
71             bits[limit++] = WA_UNDERLINE;
72         if (valid_cap("rev"))
73             bits[limit++] = WA_REVERSE;
74         if (valid_cap("blink"))
75             bits[limit++] = WA_BLINK;
76         if (valid_cap("dim"))
77             bits[limit++] = WA_DIM;
78         if (valid_cap("bold"))
79             bits[limit++] = WA_BOLD;
80         for (j = 0; j < limit; ++j) {
81             for (k = 0; k < limit; ++k) {
82                 table[j * limit + k] = bits[j] | bits[k];
83             }
84         }
85     }
86     return table[now % limit];
87 }
88
89 static void
90 our_content(int pair, int *fg, int *bg)
91 {
92     pair %= COLOR_PAIRS;
93     *fg = (pair / COLORS) % COLORS;
94     *bg = (pair % COLORS);
95 }
96
97 static int
98 make_color(int now)
99 {
100     int fg, bg;
101     our_content(now, &fg, &bg);
102     return alloc_pair(fg, bg);
103 }
104
105 static int
106 next_color(int now)
107 {
108     int result = 0;
109     if ((short) now > 0) {
110         if (now < COLOR_PAIRS) {
111             int fg, bg;
112             our_content(now, &fg, &bg);
113             if (init_pair((short) now, (short) fg, (short) bg) != OK)
114                 now = ERR;
115         } else {
116             now %= COLOR_PAIRS;
117         }
118         result = now;
119     }
120     return result;
121 }
122
123 static time_t
124 now(void)
125 {
126     return time((time_t *) 0);
127 }
128
129 static void
130 usage(void)
131 {
132     static const char *msg[] =
133     {
134         "Usage: demo_new_pair [options]",
135         "",
136         "Repeatedly print using all possible color combinations.",
137         "",
138         "Options:",
139         " -g       use getcchar to check setcchar",
140         " -i       use init_pair rather than alloc_pair",
141         " -p       start in paged-mode",
142         " -s       start in single-step mode",
143         " -w       print a wide-character cell",
144     };
145     unsigned n;
146     for (n = 0; n < SIZEOF(msg); ++n) {
147         fprintf(stderr, "%s\n", msg[n]);
148     }
149     ExitProgram(EXIT_FAILURE);
150 }
151
152 #define use_pages() \
153         paged_mode = TRUE, single_mode = TRUE
154
155 #define use_single() \
156         paged_mode = FALSE, single_mode = TRUE
157
158 #define update_modes() \
159             scrollok(stdscr, !paged_mode); \
160             nodelay(stdscr, !single_mode || paged_mode)
161
162 int
163 main(int argc, char *argv[])
164 {
165     static const char *help[] =
166     {
167         "This program iterates over the possible color combinations,",
168         "allocating or initializing color pairs.  For best results,",
169         "choose screen-width dividing evenly into the number of colors,",
170         "e.g.,",
171         "",
172         "  32x64,32x128  256 colors",
173         "  24x44,24x88   88 colors",
174         "  32x64,24x128  16 colors",
175         "",
176         "Keys:",
177         "  c      toggle between coloring and de-coloring cells",
178         "  p      show one page at a time",
179         "  s      show one character at a time",
180         " <space> display char/page without pausing",
181         "  v/V    cycle through video attributes",
182         "  w      toggle between \"#\" and a double-width equivalent",
183         "  ?      print this screen (exit on any character).",
184         "",
185         "To exit this program, press ^Q, ^[ or \"q\".",
186         0
187     };
188
189     bool done = FALSE;
190     bool check_set = FALSE;
191     bool clobber = FALSE;
192     bool hascolor = FALSE;
193     bool use_init = FALSE;
194     bool use_wide = FALSE;
195     bool paged_mode = FALSE;
196     bool single_mode = FALSE;
197     int video_mode = 0;
198     int current;
199     int ch;
200     wchar_t wch[2];
201     time_t start = now();
202     long total_cells = 0;
203     FILE *output = 0;
204
205     setlocale(LC_ALL, "");
206
207     while ((ch = getopt(argc, argv, "gipsw")) != -1) {
208         switch (ch) {
209         case 'g':
210             check_set = TRUE;
211             break;
212         case 'i':
213             use_init = TRUE;
214             break;
215         case 'p':
216             use_pages();
217             break;
218         case 's':
219             use_single();
220             break;
221         case 'w':
222             use_wide = TRUE;
223             break;
224         default:
225             usage();
226             break;
227         }
228     }
229
230     if (isatty(fileno(stderr))) {
231         output = stderr;
232     } else if ((ch = open("/dev/tty", O_WRONLY)) >= 0) {
233         output = fdopen(ch, "w");
234     } else {
235         fprintf(stderr, "cannot open terminal for output\n");
236         ExitProgram(EXIT_FAILURE);
237     }
238     if (newterm(NULL, output, stdin) == 0) {
239         fprintf(stderr, "Cannot initialize terminal\n");
240         fclose(output);
241         ExitProgram(EXIT_FAILURE);
242     }
243     (void) cbreak();            /* read chars without wait for \n */
244     (void) noecho();            /* don't echo input */
245     update_modes();
246     curs_set(0);
247
248     keypad(stdscr, TRUE);
249
250     if ((hascolor = has_colors())) {
251         start_color();
252         current = 1;
253     } else {
254         current = 0;
255     }
256
257     /*
258      * Repeatedly cycle through all colors, initializing pairs as needed.
259      * Provide for single-stepping, or page-at-a-time, as well as quitting.
260      */
261     while (!done) {
262         cchar_t temp;
263         attr_t my_attrs;
264         int my_pair;
265
266         switch (getch()) {
267         case HELP_KEY_1:
268             popup_msg(stdscr, help);
269             break;
270         case 'p':
271             /* step-by-page */
272             use_pages();
273             update_modes();
274             break;
275         case 's':
276             /* step-by-char */
277             use_single();
278             update_modes();
279             break;
280         case ' ':
281             single_mode = FALSE;
282             update_modes();
283             break;
284         case QUIT:
285         case ESCAPE:
286         case 'q':
287             done = TRUE;
288             continue;
289         case 'c':
290             clobber = !clobber;
291             continue;
292         case 'v':
293             if (--video_mode < 0)
294                 video_mode = MAX_ATTR;
295             continue;
296         case 'V':
297             if (video_mode > MAX_ATTR)
298                 video_mode = 0;
299             continue;
300         case 'w':
301             use_wide = !use_wide;
302             continue;
303         case ERR:
304             break;
305         default:
306             beep();
307             break;
308         }
309         if (hascolor) {
310             my_attrs = next_attr(video_mode);
311             if (clobber) {
312                 int fg, bg;
313                 our_content(current, &fg, &bg);
314                 my_pair = find_pair(fg, bg);
315                 if (my_pair > 0) {
316                     free_pair(my_pair);
317                 }
318                 my_pair = 0;
319             } else {
320                 my_pair = (use_init
321                            ? next_color(current)
322                            : make_color(current));
323             }
324         } else {
325             my_attrs = next_attr(current);
326             my_pair = 0;
327         }
328         if (my_pair < 0)
329             break;
330         wch[0] = use_wide ? 0xff03 : '#';
331         wch[1] = 0;
332         setcchar(&temp, wch, my_attrs,
333                  (short) my_pair,
334                  (use_init ? NULL : (void *) &my_pair));
335
336         if (check_set) {
337             int problem = 0;
338             wchar_t chk_wch[2];
339             attr_t chk_attrs = 0;
340             short chk_pair = 0;
341             int chk_pair2 = 0;
342
343 #define AllButColor(a) ((a) & (A_ATTRIBUTES & ~A_COLOR))
344
345             if (getcchar(&temp, NULL, &chk_attrs, &chk_pair,
346                          (use_init ? NULL : (void *) &chk_pair2)) != 2) {
347                 problem = 1;
348             } else if (getcchar(&temp, chk_wch, &chk_attrs, &chk_pair,
349                                 (use_init ? NULL : (void *) &chk_pair2)) != OK) {
350                 problem = 2;
351             } else if (chk_wch[0] != wch[0]) {
352                 problem = 3;
353             } else if (AllButColor(my_attrs) != AllButColor(chk_attrs)) {
354                 problem = 4;
355             } else if (my_pair != chk_pair) {
356                 problem = 4;
357             } else if (!use_init && (my_pair != chk_pair2)) {
358                 problem = 5;
359             }
360             if (problem) {
361                 wch[0] = (wchar_t) (problem + '0');
362                 setcchar(&temp, wch, my_attrs,
363                          (short) my_pair,
364                          (use_init ? NULL : (void *) &my_pair));
365             }
366         }
367
368         /*
369          * At the end of a page, move the cursor to the home position.
370          */
371         if ((add_wch(&temp) == ERR) && paged_mode) {
372             nodelay(stdscr, !single_mode);
373             move(0, 0);
374         }
375         total_cells += 1 + (use_wide ? 1 : 0);
376         ++current;
377     }
378     stop_curses();
379     fclose(output);
380
381     printf("%.1f cells/second\n",
382            (double) (total_cells) / (double) (now() - start));
383
384     ExitProgram(EXIT_SUCCESS);
385 }
386
387 #else
388 int
389 main(void)
390 {
391     printf("This program requires the ncurses alloc_pair function\n");
392     ExitProgram(EXIT_FAILURE);
393 }
394 #endif