• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2018-2022,2023 Thomas E. Dickey                                *
3  * Copyright 2006-2016,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 /*
31  * Author: Thomas E. Dickey, 2006
32  *
33  * $Id: foldkeys.c,v 1.12 2023/02/25 16:51:01 tom Exp $
34  *
35  * Demonstrate a method for altering key definitions at runtime.
36  *
37  * This program reads the key definitions, merging those which have xterm-style
38  * modifiers into their equivalents which have no modifiers.  It does this
39  * merging only for the keys which are defined in the terminal description.
40  */
41 
42 #define NEED_TIME_H
43 #include <test.priv.h>
44 
45 #if defined(NCURSES_VERSION) && NCURSES_EXT_FUNCS
46 
47 #define MY_LOGFILE "demo_foldkeys.log"
48 #define MY_KEYS (KEY_MAX + 1)
49 
50 /*
51  * Log the most recently-written line to our logfile
52  */
53 static void
log_last_line(WINDOW * win)54 log_last_line(WINDOW *win)
55 {
56     FILE *fp;
57 
58     if ((fp = fopen(MY_LOGFILE, "a")) != 0) {
59 	char temp[256];
60 	int y, x, n;
61 	int need = sizeof(temp) - 1;
62 	if (need > COLS)
63 	    need = COLS;
64 	getyx(win, y, x);
65 	wmove(win, y - 1, 0);
66 	n = winnstr(win, temp, need);
67 	while (n-- > 0) {
68 	    if (isspace(UChar(temp[n])))
69 		temp[n] = '\0';
70 	    else
71 		break;
72 	}
73 	wmove(win, y, x);
74 	fprintf(fp, "%s\n", temp);
75 	fclose(fp);
76     }
77 }
78 
79 /*
80  * ncurses has no API for telling what the actual last key-code is.  That is
81  * a secret because the codes past KEY_MAX are computed at run-time and may
82  * differ depending on the previous calls to newterm(), etc.  It is unlikely
83  * that one could have more than a thousand key definitions...
84  */
85 #define MAX_KEYS 2000
86 
87 typedef struct {
88     const char *name;
89     const char *value;
90     int code;
91     int state;
92 } KeyInfo;
93 
94 static void
demo_foldkeys(void)95 demo_foldkeys(void)
96 {
97     KeyInfo info[MAX_KEYS];
98     int info_len = 0;
99     int merged = 0;
100     int code;
101     int j, k;
102 
103     /*
104      * Tell ncurses that we want to use function keys.  That will make it add
105      * any user-defined keys that appear in the terminfo.
106      */
107     keypad(stdscr, TRUE);
108 
109     /*
110      * List the predefined keys using the strnames[] array.
111      */
112     for (code = 0; code < STRCOUNT; ++code) {
113 	NCURSES_CONST char *name = strnames[code];
114 	NCURSES_CONST char *value = tigetstr(name);
115 	if (value != 0 && value != (NCURSES_CONST char *) -1) {
116 	    info[info_len].name = strnames[code];
117 	    info[info_len].code = key_defined(value);
118 	    info[info_len].value = value;
119 	    info[info_len].state = 0;
120 	    if (info[info_len].code > 0)
121 		++info_len;
122 	}
123     }
124 
125     /*
126      * We can get the names for user-defined keys from keyname().  It returns
127      * a name like KEY_foo for the predefined keys, which tigetstr() does not
128      * understand.
129      */
130     for (code = KEY_MAX; code < MAX_KEYS; ++code) {
131 	NCURSES_CONST char *name = keyname(code);
132 	if (name != 0) {
133 	    info[info_len].name = name;
134 	    info[info_len].code = code;
135 	    info[info_len].value = tigetstr(name);
136 	    info[info_len].state = 0;
137 	    ++info_len;
138 	}
139     }
140     printw("Initially %d key definitions\n", info_len);
141 
142     /*
143      * Look for keys that have xterm-style modifiers.
144      */
145     for (j = 0; j < info_len; ++j) {
146 	int first, second;
147 	char final[2];
148 	char *value;
149 	size_t need;
150 
151 	if (info[j].state == 0
152 	    && sscanf(info[j].value,
153 		      "\033[%d;%d%c",
154 		      &first,
155 		      &second,
156 		      final) == 3
157 	    && *final != ';'
158 	    && (need = strlen(info[j].value)) != 0
159 	    && (value = strdup(info[j].value)) != 0) {
160 	    (void) need;	/* _nc_SLIMIT is normally nothing  */
161 	    _nc_SPRINTF(value, _nc_SLIMIT(need) "\033[%d%c", first, *final);
162 	    for (k = 0; k < info_len; ++k) {
163 		if (info[k].state == 0
164 		    && !strcmp(info[k].value, value)) {
165 		    info[j].state = 1;
166 		    break;
167 		}
168 	    }
169 	    if (info[j].state == 0) {
170 		_nc_SPRINTF(value, _nc_SLIMIT(need) "\033O%c", *final);
171 		for (k = 0; k < info_len; ++k) {
172 		    if (info[k].state == 0
173 			&& !strcmp(info[k].value, value)) {
174 			info[j].state = 1;
175 			break;
176 		    }
177 		}
178 	    }
179 	    if (info[j].state == 1) {
180 		if ((define_key(info[j].value, info[k].code)) != ERR) {
181 		    printw("map %s to %s\n", info[j].value, info[k].value);
182 		    keyok(info[j].code, FALSE);
183 		    ++merged;
184 		} else {
185 		    printw("? cannot define_key %d:%s\n", j, info[j].value);
186 		}
187 	    } else {
188 		printw("? cannot merge %d:%s\n", j, info[j].value);
189 	    }
190 	    free(value);
191 	}
192     }
193     printw("Merged to %d key definitions\n", info_len - merged);
194 }
195 
196 static void
usage(int ok)197 usage(int ok)
198 {
199     static const char *msg[] =
200     {
201 	"Usage: foldkeys [options]"
202 	,""
203 	,USAGE_COMMON
204     };
205     size_t n;
206 
207     for (n = 0; n < SIZEOF(msg); n++)
208 	fprintf(stderr, "%s\n", msg[n]);
209 
210     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
211 }
212 /* *INDENT-OFF* */
VERSION_COMMON()213 VERSION_COMMON()
214 /* *INDENT-ON* */
215 
216 int
217 main(int argc, char *argv[])
218 {
219     int ch;
220     TimeType previous;
221 
222     while ((ch = getopt(argc, argv, OPTS_COMMON)) != -1) {
223 	switch (ch) {
224 	case OPTS_VERSION:
225 	    show_version(argv);
226 	    ExitProgram(EXIT_SUCCESS);
227 	default:
228 	    usage(ch == OPTS_USAGE);
229 	    /* NOTREACHED */
230 	}
231     }
232     if (optind < argc)
233 	usage(FALSE);
234 
235     if (newterm(0, stdout, stdin) == 0) {
236 	fprintf(stderr, "Cannot initialize terminal\n");
237 	ExitProgram(EXIT_FAILURE);
238     }
239 
240     unlink(MY_LOGFILE);
241 
242     (void) cbreak();		/* take input chars one at a time, no wait for \n */
243     (void) noecho();		/* don't echo input */
244 
245     scrollok(stdscr, TRUE);
246     keypad(stdscr, TRUE);
247     move(0, 0);
248 
249     demo_foldkeys();
250 
251     GetClockTime(&previous);
252 
253     while ((ch = getch()) != ERR) {
254 	bool escaped = (ch >= MY_KEYS);
255 	const char *name = keyname(escaped ? (ch - MY_KEYS) : ch);
256 	TimeType current;
257 
258 	GetClockTime(&current);
259 	printw("%6.03f ", ElapsedSeconds(&previous, &current));
260 	previous = current;
261 
262 	printw("Keycode %d, name %s%s\n",
263 	       ch,
264 	       escaped ? "ESC-" : "",
265 	       name != 0 ? name : "<null>");
266 	log_last_line(stdscr);
267 	clrtoeol();
268 	if (ch == 'q')
269 	    break;
270     }
271     endwin();
272     ExitProgram(EXIT_SUCCESS);
273 }
274 #else
275 int
main(void)276 main(void)
277 {
278     printf("This program requires the ncurses library\n");
279     ExitProgram(EXIT_FAILURE);
280 }
281 #endif
282