]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/demo_new_pair.c
ncurses 6.4 - patch 20240420
[ncurses.git] / test / demo_new_pair.c
1 /****************************************************************************
2  * Copyright 2018-2021,2022 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.27 2022/12/04 00:40:11 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(int ok)
131 {
132     static const char *msg[] =
133     {
134         "Usage: demo_new_pair [options]"
135         ,""
136         ,"Repeatedly print using all possible color combinations."
137         ,""
138         ,USAGE_COMMON
139         ,"Options:"
140         ," -g       use getcchar to check setcchar"
141         ," -i       use init_pair rather than alloc_pair"
142         ," -p       start in paged-mode"
143         ," -s       start in single-step mode"
144         ," -w       print a wide-character cell"
145     };
146     unsigned n;
147     for (n = 0; n < SIZEOF(msg); ++n) {
148         fprintf(stderr, "%s\n", msg[n]);
149     }
150     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
151 }
152
153 #define use_pages() \
154         paged_mode = TRUE, single_mode = TRUE
155
156 #define use_single() \
157         paged_mode = FALSE, single_mode = TRUE
158
159 #define update_modes() \
160             scrollok(stdscr, !paged_mode); \
161             nodelay(stdscr, !single_mode || paged_mode)
162 /* *INDENT-OFF* */
163 VERSION_COMMON()
164 /* *INDENT-ON* */
165
166 int
167 main(int argc, char *argv[])
168 {
169     static const char *help[] =
170     {
171         "This program iterates over the possible color combinations,",
172         "allocating or initializing color pairs.  For best results,",
173         "choose screen-width dividing evenly into the number of colors,",
174         "e.g.,",
175         "",
176         "  32x64,32x128  256 colors",
177         "  24x44,24x88   88 colors",
178         "  32x64,24x128  16 colors",
179         "",
180         "Keys:",
181         "  c      toggle between coloring and de-coloring cells",
182         "  p      show one page at a time",
183         "  s      show one character at a time",
184         " <space> display char/page without pausing",
185         "  v/V    cycle through video attributes",
186         "  w      toggle between \"#\" and a double-width equivalent",
187         "  ?      print this screen (exit on any character).",
188         "",
189         "To exit this program, press ^Q, ^[ or \"q\".",
190         0
191     };
192
193     bool done = FALSE;
194     bool check_set = FALSE;
195     bool clobber = FALSE;
196     bool hascolor = FALSE;
197     bool use_init = FALSE;
198     bool use_wide = FALSE;
199     bool paged_mode = FALSE;
200     bool single_mode = FALSE;
201     int video_mode = 0;
202     int current;
203     int ch;
204     wchar_t wch[2];
205     time_t start = now();
206     long total_cells = 0;
207     FILE *output = 0;
208
209     setlocale(LC_ALL, "");
210
211     while ((ch = getopt(argc, argv, OPTS_COMMON "gipsw")) != -1) {
212         switch (ch) {
213         case 'g':
214             check_set = TRUE;
215             break;
216         case 'i':
217             use_init = TRUE;
218             break;
219         case 'p':
220             use_pages();
221             break;
222         case 's':
223             use_single();
224             break;
225         case 'w':
226             use_wide = TRUE;
227             break;
228         case OPTS_VERSION:
229             show_version(argv);
230             ExitProgram(EXIT_SUCCESS);
231         default:
232             usage(ch == OPTS_USAGE);
233             /* NOTREACHED */
234         }
235     }
236
237     if (isatty(fileno(stderr))) {
238         output = stderr;
239     } else if ((ch = open("/dev/tty", O_WRONLY)) >= 0) {
240         output = fdopen(ch, "w");
241     } else {
242         fprintf(stderr, "cannot open terminal for output\n");
243         ExitProgram(EXIT_FAILURE);
244     }
245     if (newterm(NULL, output, stdin) == 0) {
246         fprintf(stderr, "Cannot initialize terminal\n");
247         fclose(output);
248         ExitProgram(EXIT_FAILURE);
249     }
250     (void) cbreak();            /* read chars without wait for \n */
251     (void) noecho();            /* don't echo input */
252     update_modes();
253     curs_set(0);
254
255     keypad(stdscr, TRUE);
256
257     if ((hascolor = has_colors())) {
258         start_color();
259         current = 1;
260     } else {
261         current = 0;
262     }
263
264     /*
265      * Repeatedly cycle through all colors, initializing pairs as needed.
266      * Provide for single-stepping, or page-at-a-time, as well as quitting.
267      */
268     while (!done) {
269         cchar_t temp;
270         attr_t my_attrs;
271         int my_pair;
272
273         switch (getch()) {
274         case HELP_KEY_1:
275             popup_msg(stdscr, help);
276             break;
277         case 'p':
278             /* step-by-page */
279             use_pages();
280             update_modes();
281             break;
282         case 's':
283             /* step-by-char */
284             use_single();
285             update_modes();
286             break;
287         case ' ':
288             single_mode = FALSE;
289             update_modes();
290             break;
291         case QUIT:
292         case ESCAPE:
293         case 'q':
294             done = TRUE;
295             continue;
296         case 'c':
297             clobber = !clobber;
298             continue;
299         case 'v':
300             if (--video_mode < 0)
301                 video_mode = MAX_ATTR;
302             continue;
303         case 'V':
304             if (video_mode > MAX_ATTR)
305                 video_mode = 0;
306             continue;
307         case 'w':
308             use_wide = !use_wide;
309             continue;
310         case ERR:
311             break;
312         default:
313             beep();
314             break;
315         }
316         if (hascolor) {
317             my_attrs = next_attr(video_mode);
318             if (clobber) {
319                 int fg, bg;
320                 our_content(current, &fg, &bg);
321                 my_pair = find_pair(fg, bg);
322                 if (my_pair > 0) {
323                     free_pair(my_pair);
324                 }
325                 my_pair = 0;
326             } else {
327                 my_pair = (use_init
328                            ? next_color(current)
329                            : make_color(current));
330             }
331         } else {
332             my_attrs = next_attr(current);
333             my_pair = 0;
334         }
335         if (my_pair < 0)
336             break;
337         wch[0] = use_wide ? 0xff03 : '#';
338         wch[1] = 0;
339         setcchar(&temp, wch, my_attrs,
340                  (short) my_pair,
341                  (use_init ? NULL : (void *) &my_pair));
342
343         if (check_set) {
344             int problem = 0;
345             wchar_t chk_wch[2];
346             attr_t chk_attrs = 0;
347             short chk_pair = 0;
348             int chk_pair2 = 0;
349
350 #define AllButColor(a) ((a) & (A_ATTRIBUTES & ~A_COLOR))
351
352             if (getcchar(&temp, NULL, &chk_attrs, &chk_pair,
353                          (use_init ? NULL : (void *) &chk_pair2)) != 2) {
354                 problem = 1;
355             } else if (getcchar(&temp, chk_wch, &chk_attrs, &chk_pair,
356                                 (use_init ? NULL : (void *) &chk_pair2)) != OK) {
357                 problem = 2;
358             } else if (chk_wch[0] != wch[0]) {
359                 problem = 3;
360             } else if (AllButColor(my_attrs) != AllButColor(chk_attrs)) {
361                 problem = 4;
362             } else if (my_pair != chk_pair) {
363                 problem = 4;
364             } else if (!use_init && (my_pair != chk_pair2)) {
365                 problem = 5;
366             }
367             if (problem) {
368                 wch[0] = (wchar_t) (problem + '0');
369                 setcchar(&temp, wch, my_attrs,
370                          (short) my_pair,
371                          (use_init ? NULL : (void *) &my_pair));
372             }
373         }
374
375         /*
376          * At the end of a page, move the cursor to the home position.
377          */
378         if ((add_wch(&temp) == ERR) && paged_mode) {
379             nodelay(stdscr, !single_mode);
380             move(0, 0);
381         }
382         total_cells += 1 + (use_wide ? 1 : 0);
383         ++current;
384     }
385     stop_curses();
386     fclose(output);
387
388     printf("%.1f cells/second\n",
389            (double) (total_cells) / (double) (now() - start));
390
391     ExitProgram(EXIT_SUCCESS);
392 }
393
394 #else
395 int
396 main(void)
397 {
398     printf("This program requires the ncurses alloc_pair function\n");
399     ExitProgram(EXIT_FAILURE);
400 }
401 #endif