2 * $Id: ui_getc.c,v 1.48 2010/01/18 10:24:06 tom Exp $
4 * ui_getc.c - user interface glue for getc()
6 * Copyright 2001-2009,2010 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
31 #if TIME_WITH_SYS_TIME
32 # include <sys/time.h>
36 # include <sys/time.h>
42 #ifdef HAVE_SYS_WAIT_H
47 #include <sys/select.h>
51 # ifdef HAVE_TYPE_UNIONWAIT
52 # define WEXITSTATUS(status) (status.w_retcode)
54 # define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
59 # ifdef HAVE_TYPE_UNIONWAIT
60 # define WTERMSIG(status) (status.w_termsig)
62 # define WTERMSIG(status) ((status) & 0x7f)
67 dlg_add_callback(DIALOG_CALLBACK * p)
69 p->next = dialog_state.getc_callbacks;
70 dialog_state.getc_callbacks = p;
71 wtimeout(p->win, WTIMEOUT_VAL);
75 * Like dlg_add_callback(), but providing for cleanup of caller's associated
79 dlg_add_callback_ref(DIALOG_CALLBACK ** p, DIALOG_FREEBACK freeback)
82 (*p)->freeback = freeback;
87 dlg_remove_callback(DIALOG_CALLBACK * p)
93 if (p->input == dialog_state.pipe_input)
94 dialog_state.pipe_input = 0;
99 dlg_del_window(p->win);
100 if ((q = dialog_state.getc_callbacks) == p) {
101 dialog_state.getc_callbacks = p->next;
112 /* handle dlg_add_callback_ref cleanup */
113 if (p->freeback != 0)
122 * FIXME: this could be replaced by a select/poll on several file descriptors
125 dlg_getc_ready(DIALOG_CALLBACK * p)
128 int fd = fileno(p->input);
132 FD_SET(fd, &read_fds);
134 test.tv_sec = 0; /* Seconds. */
135 test.tv_usec = (isatty(fd) /* Microseconds. */
136 ? (WTIMEOUT_VAL * 1000)
138 return (select(fd + 1, &read_fds, (fd_set *) 0, (fd_set *) 0, &test) == 1)
139 && (FD_ISSET(fd, &read_fds));
143 dlg_getc_callbacks(int ch, int fkey, int *result)
146 DIALOG_CALLBACK *p, *q;
148 if ((p = dialog_state.getc_callbacks) != 0) {
151 if (dlg_getc_ready(p)) {
152 if (!(p->handle_getc(p, ch, fkey, result))) {
153 dlg_remove_callback(p);
156 } while ((p = q) != 0);
157 code = (dialog_state.getc_callbacks != 0);
163 dlg_raise_window(WINDOW *win)
166 wmove(win, getcury(win), getcurx(win));
172 * This is a work-around for the case where we actually need the wide-character
173 * code versus a byte stream.
175 static int last_getc = ERR;
177 #ifdef USE_WIDE_CURSES
178 static char last_getc_bytes[80];
179 static int have_last_getc;
180 static int used_last_getc;
186 #ifdef USE_WIDE_CURSES
187 if (used_last_getc != 1)
188 return ERR; /* not really an error... */
197 #ifdef USE_WIDE_CURSES
204 * Check if the stream has been unexpectedly closed, returning false in that
215 if ((result = fcntl(fd, F_GETFL, 0)) >= 0) {
223 * Read a character from the given window. Handle repainting here (to simplify
224 * things in the calling application). Also, if input-callback(s) are set up,
225 * poll the corresponding files and handle the updates, e.g., for displaying a
229 dlg_getc(WINDOW *win, int *fkey)
231 WINDOW *save_win = win;
236 bool literal = FALSE;
238 int interval = dialog_vars.timeout_secs;
239 time_t expired = time((time_t *) 0) + dialog_vars.timeout_secs;
242 if (dialog_state.getc_callbacks != 0)
243 wtimeout(win, WTIMEOUT_VAL);
244 else if (interval > 0)
245 wtimeout(win, interval);
248 #ifdef USE_WIDE_CURSES
255 * We get a wide character, translate it to multibyte form to avoid
256 * having to change the rest of the code to use wide-characters.
258 if (used_last_getc >= have_last_getc) {
263 code = wget_wch(win, &my_wint);
264 my_wchar = (wchar_t) my_wint;
267 ch = *fkey = my_wchar;
268 last_getc = my_wchar;
271 memset(&state, 0, sizeof(state));
272 have_last_getc = (int) wcrtomb(last_getc_bytes, my_wchar, &state);
273 if (have_last_getc < 0) {
274 have_last_getc = used_last_getc = 0;
275 last_getc_bytes[0] = (char) my_wchar;
277 ch = (int) CharOf(last_getc_bytes[used_last_getc++]);
278 last_getc = my_wchar;
288 ch = (int) CharOf(last_getc_bytes[used_last_getc++]);
293 *fkey = (ch > KEY_MIN && ch < KEY_MAX);
301 ch = dlg_lookup_key(win, ch, fkey);
302 dlg_trace_chr(ch, *fkey);
304 current = time((time_t *) 0);
315 (void) touchwin(win);
316 (void) wrefresh(curscr);
318 case ERR: /* wtimeout() in effect; check for file I/O */
320 && current >= expired) {
321 dlg_exiterr("timeout");
323 if (dlg_getc_callbacks(ch, *fkey, &result)) {
324 dlg_raise_window(win);
326 done = (interval <= 0);
328 if (!valid_file(stdin)
329 || !valid_file(dialog_state.screen_output)) {
334 case DLGK_FIELD_NEXT:
337 /* Handle tab as a special case for traversing between the nominal
338 * "current" window, and other windows having callbacks. If the
339 * nominal (control) window closes, we'll close the windows with
342 if (dialog_state.getc_callbacks != 0 &&
343 before_lookup == TAB) {
344 if ((p = dialog_state.getc_redirect) != 0) {
347 p = dialog_state.getc_callbacks;
349 if ((dialog_state.getc_redirect = p) != 0) {
354 dlg_raise_window(win);
360 if (before_lookup == DLG_CTRL('P')) {
361 /* for testing, ^P closes the connection */
368 if ((p = dialog_state.getc_redirect) != 0) {
369 if (!(p->handle_getc(p, ch, *fkey, &result))) {
370 dlg_remove_callback(p);
371 dialog_state.getc_redirect = 0;
378 #ifdef HAVE_DLG_TRACE
391 finish_bg(int sig GCC_UNUSED)
394 dlg_exit(DLG_EXIT_ERROR);
398 * If we have callbacks active, purge the list of all that are not marked
399 * to keep in the background. If any remain, run those in a background
403 dlg_killall_bg(int *retval)
407 #ifdef HAVE_TYPE_UNIONWAIT
413 if ((cb = dialog_state.getc_callbacks) != 0) {
418 dlg_remove_callback(cb);
419 cb = dialog_state.getc_callbacks;
422 if (dialog_state.getc_callbacks != 0) {
428 if ((pid = fork()) != 0) {
429 _exit(pid > 0 ? DLG_EXIT_OK : DLG_EXIT_ERROR);
430 } else if (pid == 0) { /* child */
431 if ((pid = fork()) != 0) {
433 * Echo the process-id of the grandchild so a shell script
434 * can read that, and kill that process. We'll wait around
435 * until then. Our parent has already left, leaving us
436 * temporarily orphaned.
438 if (pid > 0) { /* parent */
439 fprintf(stderr, "%d\n", pid);
444 while (-1 == waitpid(pid, &wstatus, 0)) {
450 if (errno == ERESTARTSYS)
452 #endif /* ERESTARTSYS */
456 while (wait(&wstatus) != pid) /* do nothing */
459 _exit(WEXITSTATUS(wstatus));
460 } else if (pid == 0) {
461 if (!dialog_vars.cant_kill)
462 (void) signal(SIGHUP, finish_bg);
463 (void) signal(SIGINT, finish_bg);
464 (void) signal(SIGQUIT, finish_bg);
465 (void) signal(SIGSEGV, finish_bg);
466 while (dialog_state.getc_callbacks != 0) {
468 dlg_getc_callbacks(ERR, fkey, retval);