2 * $Id: ui_getc.c,v 1.67 2013/03/24 23:53:19 tom Exp $
4 * ui_getc.c - user interface glue for getc()
6 * Copyright 2001-2012,2013 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 * A select() might find more than one input ready for service. Handle them
126 handle_inputs(WINDOW
*win
)
134 getyx(win
, cur_y
, cur_x
);
135 for (p
= dialog_state
.getc_callbacks
, q
= 0; p
!= 0; p
= q
) {
137 if ((p
->handle_input
!= 0) && p
->input_ready
) {
138 p
->input_ready
= FALSE
;
142 if (p
->handle_input(p
)) {
148 (void) wmove(win
, cur_y
, cur_x
); /* Restore cursor position */
156 may_handle_inputs(void)
162 for (p
= dialog_state
.getc_callbacks
; p
!= 0; p
= p
->next
) {
173 * Check any any inputs registered via callbacks, to see if there is any input
174 * available. If there is, return a file-descriptor which should be read.
175 * Otherwise, return -1.
188 if ((p
= dialog_state
.getc_callbacks
) != 0) {
192 p
->input_ready
= FALSE
;
193 if (p
->input
!= 0 && (fd
= fileno(p
->input
)) >= 0) {
194 FD_SET(fd
, &read_fds
);
202 test
.tv_usec
= WTIMEOUT_VAL
* 1000;
203 found
= select(last_fd
+ 1, &read_fds
,
209 for (p
= dialog_state
.getc_callbacks
; p
!= 0; p
= p
->next
) {
211 && (fd
= fileno(p
->input
)) >= 0
212 && FD_ISSET(fd
, &read_fds
)) {
213 p
->input_ready
= TRUE
;
224 dlg_getc_callbacks(int ch
, int fkey
, int *result
)
227 DIALOG_CALLBACK
*p
, *q
;
229 if ((p
= dialog_state
.getc_callbacks
) != 0) {
230 if (check_inputs() >= 0) {
233 if (p
->input_ready
) {
234 if (!(p
->handle_getc(p
, ch
, fkey
, result
))) {
235 dlg_remove_callback(p
);
238 } while ((p
= q
) != 0);
240 code
= (dialog_state
.getc_callbacks
!= 0);
246 dlg_raise_window(WINDOW
*win
)
249 wmove(win
, getcury(win
), getcurx(win
));
255 * This is a work-around for the case where we actually need the wide-character
256 * code versus a byte stream.
258 static int last_getc
= ERR
;
260 #ifdef USE_WIDE_CURSES
261 static char last_getc_bytes
[80];
262 static int have_last_getc
;
263 static int used_last_getc
;
269 #ifdef USE_WIDE_CURSES
270 if (used_last_getc
!= 1)
271 return ERR
; /* not really an error... */
280 #ifdef USE_WIDE_CURSES
287 * Report the last key entered by the user. The 'mode' parameter controls
288 * the way it is separated from other results:
290 * -1 (separator after the key name)
291 * 0 (separator is optionally before the key name)
295 dlg_add_last_key(int mode
)
297 if (dialog_vars
.last_key
) {
300 dlg_add_last_key(-1);
302 if (dlg_need_separator())
304 dlg_add_last_key(-2);
308 sprintf(temp
, "%d", last_getc
);
309 dlg_add_string(temp
);
317 * Check if the stream has been unexpectedly closed, returning false in that
327 if (fcntl(fd
, F_GETFL
, 0) >= 0) {
335 really_getch(WINDOW
*win
, int *fkey
)
338 #ifdef USE_WIDE_CURSES
345 * We get a wide character, translate it to multibyte form to avoid
346 * having to change the rest of the code to use wide-characters.
348 if (used_last_getc
>= have_last_getc
) {
353 code
= wget_wch(win
, &my_wint
);
354 my_wchar
= (wchar_t) my_wint
;
357 ch
= *fkey
= my_wchar
;
358 last_getc
= my_wchar
;
361 memset(&state
, 0, sizeof(state
));
362 have_last_getc
= (int) wcrtomb(last_getc_bytes
, my_wchar
, &state
);
363 if (have_last_getc
< 0) {
364 have_last_getc
= used_last_getc
= 0;
365 last_getc_bytes
[0] = (char) my_wchar
;
367 ch
= (int) CharOf(last_getc_bytes
[used_last_getc
++]);
368 last_getc
= my_wchar
;
378 ch
= (int) CharOf(last_getc_bytes
[used_last_getc
++]);
383 *fkey
= (ch
> KEY_MIN
&& ch
< KEY_MAX
);
388 static DIALOG_CALLBACK
*
389 next_callback(DIALOG_CALLBACK
* p
)
391 if ((p
= dialog_state
.getc_redirect
) != 0) {
394 p
= dialog_state
.getc_callbacks
;
399 static DIALOG_CALLBACK
*
400 prev_callback(DIALOG_CALLBACK
* p
)
404 if ((p
= dialog_state
.getc_redirect
) != 0) {
405 if (p
== dialog_state
.getc_callbacks
) {
406 for (p
= dialog_state
.getc_callbacks
; p
->next
!= 0; p
= p
->next
) ;
408 for (q
= dialog_state
.getc_callbacks
; q
->next
!= p
; q
= q
->next
) ;
412 p
= dialog_state
.getc_callbacks
;
417 #define isBeforeChr(chr) ((chr) == before_chr && !before_fkey)
418 #define isBeforeFkey(chr) ((chr) == before_chr && before_fkey)
421 * Read a character from the given window. Handle repainting here (to simplify
422 * things in the calling application). Also, if input-callback(s) are set up,
423 * poll the corresponding files and handle the updates, e.g., for displaying a
427 dlg_getc(WINDOW
*win
, int *fkey
)
429 WINDOW
*save_win
= win
;
435 bool literal
= FALSE
;
436 DIALOG_CALLBACK
*p
= 0;
437 int interval
= (dialog_vars
.timeout_secs
* 1000);
438 time_t expired
= time((time_t *) 0) + dialog_vars
.timeout_secs
;
441 if (may_handle_inputs())
442 wtimeout(win
, WTIMEOUT_VAL
);
443 else if (interval
> 0)
444 wtimeout(win
, interval
);
447 bool handle_others
= FALSE
;
450 * If there was no pending file-input, check the keyboard.
452 ch
= really_getch(win
, fkey
);
461 ch
= dlg_lookup_key(win
, ch
, fkey
);
462 dlg_trace_chr(ch
, *fkey
);
464 current
= time((time_t *) 0);
467 * If we acquired a fkey value, then it is one of dialog's builtin
468 * codes such as DLGK_HELPFILE.
470 if (!*fkey
|| *fkey
!= before_fkey
) {
477 (void) touchwin(win
);
478 (void) wrefresh(curscr
);
480 case ERR
: /* wtimeout() in effect; check for file I/O */
482 && current
>= expired
) {
483 dlg_exiterr("timeout");
485 if (!valid_file(stdin
)
486 || !valid_file(dialog_state
.screen_output
)) {
489 } else if (check_inputs()) {
490 if (handle_inputs(win
))
491 dlg_raise_window(win
);
495 done
= (interval
<= 0);
499 if (dialog_vars
.help_file
) {
501 getyx(win
, yold
, xold
);
502 dialog_helpfile("HELP", dialog_vars
.help_file
, 0, 0);
503 dlg_raise_window(win
);
504 wmove(win
, yold
, xold
);
507 case DLGK_FIELD_PREV
:
511 case DLGK_FIELD_NEXT
:
514 /* Handle tab/backtab as a special case for traversing between
515 * the nominal "current" window, and other windows having
516 * callbacks. If the nominal (control) window closes, we'll
517 * close the windows with callbacks.
519 if (dialog_state
.getc_callbacks
!= 0 &&
521 isBeforeFkey(KEY_BTAB
))) {
522 p
= (isBeforeChr(TAB
)
525 if ((dialog_state
.getc_redirect
= p
) != 0) {
530 dlg_raise_window(win
);
536 if (isBeforeChr(DLG_CTRL('P'))) {
537 /* for testing, ^P closes the connection */
544 handle_others
= TRUE
;
546 #ifdef HAVE_DLG_TRACE
553 handle_others
= TRUE
;
557 if ((p
= dialog_state
.getc_redirect
) != 0) {
558 if (!(p
->handle_getc(p
, ch
, *fkey
, &result
))) {
559 done
= (p
->win
== save_win
) && (!p
->keep_win
);
560 dlg_remove_callback(p
);
561 dialog_state
.getc_redirect
= 0;
575 finish_bg(int sig GCC_UNUSED
)
578 dlg_exit(DLG_EXIT_ERROR
);
582 * If we have callbacks active, purge the list of all that are not marked
583 * to keep in the background. If any remain, run those in a background
587 dlg_killall_bg(int *retval
)
591 #ifdef HAVE_TYPE_UNIONWAIT
597 if ((cb
= dialog_state
.getc_callbacks
) != 0) {
602 dlg_remove_callback(cb
);
603 cb
= dialog_state
.getc_callbacks
;
606 if (dialog_state
.getc_callbacks
!= 0) {
612 if ((pid
= fork()) != 0) {
613 _exit(pid
> 0 ? DLG_EXIT_OK
: DLG_EXIT_ERROR
);
614 } else if (pid
== 0) { /* child */
615 if ((pid
= fork()) != 0) {
617 * Echo the process-id of the grandchild so a shell script
618 * can read that, and kill that process. We'll wait around
619 * until then. Our parent has already left, leaving us
620 * temporarily orphaned.
622 if (pid
> 0) { /* parent */
623 fprintf(stderr
, "%d\n", pid
);
628 while (-1 == waitpid(pid
, &wstatus
, 0)) {
634 if (errno
== ERESTARTSYS
)
636 #endif /* ERESTARTSYS */
640 while (wait(&wstatus
) != pid
) /* do nothing */
643 _exit(WEXITSTATUS(wstatus
));
644 } else if (pid
== 0) {
645 if (!dialog_vars
.cant_kill
)
646 (void) signal(SIGHUP
, finish_bg
);
647 (void) signal(SIGINT
, finish_bg
);
648 (void) signal(SIGQUIT
, finish_bg
);
649 (void) signal(SIGSEGV
, finish_bg
);
650 while (dialog_state
.getc_callbacks
!= 0) {
652 dlg_getc_callbacks(ERR
, fkey
, retval
);