diff options
author | Peter Stephenson <pws@users.sourceforge.net> | 2006-09-10 15:24:26 +0000 |
---|---|---|
committer | Peter Stephenson <pws@users.sourceforge.net> | 2006-09-10 15:24:26 +0000 |
commit | b726ead94e911e1ce3e8b582c315b3d6f83a6eb7 (patch) | |
tree | 6298d1215ba8a55aa8cac4fd536de3af91bbb4b2 /Src/Zle | |
parent | 638b0da9704add12fff91868efdfbb2dd35f0b54 (diff) | |
download | zsh-b726ead94e911e1ce3e8b582c315b3d6f83a6eb7.tar.gz zsh-b726ead94e911e1ce3e8b582c315b3d6f83a6eb7.tar.xz zsh-b726ead94e911e1ce3e8b582c315b3d6f83a6eb7.zip |
22676, 22678: extend sched and make it able to run events when waiting for
input
Diffstat (limited to 'Src/Zle')
-rw-r--r-- | Src/Zle/zle_main.c | 459 | ||||
-rw-r--r-- | Src/Zle/zle_thingy.c | 3 |
2 files changed, 307 insertions, 155 deletions
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 1d4636937..0e34a3fc3 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -122,7 +122,11 @@ int insmode; mod_export int eofchar; static int eofsent; -static long keytimeout; +/* + * Key timeout in hundredths of a second: we use time_t so + * that we only have the limits on one integer type to worry about. + */ +static time_t keytimeout; #if defined(HAVE_SELECT) || defined(HAVE_POLL) /* Terminal baud rate */ @@ -387,11 +391,110 @@ breakread(int fd, char *buf, int n) # define read breakread #endif +/* + * Possible forms of timeout. + */ +enum ztmouttp { + /* No timeout in use. */ + ZTM_NONE, + /* + * Key timeout in use (do_keytmout flag set). If this goes off + * we return without anything being read. + */ + ZTM_KEY, + /* + * Function timeout in use (from timedfns list). + * If this goes off we call any functions which have reached + * the time and then continue processing. + */ + ZTM_FUNC, + /* + * Timeout hit the maximum allowed; if it fires we + * need to recalculate. As we may use poll() for the timeout, + * which takes an int value in milliseconds, we might need this + * for times long in the future. (We make no attempt to extend + * the range of time beyond that of time_t, however; that seems + * like a losing battle.) + * + * For key timeouts we just limit the value to + * ZMAXTIMEOUT; that's already absurdly large. + * + * The following is the maximum signed range over 1024 (2^10), which + * is a little more convenient than 1000, but done differently + * to avoid problems with unsigned integers. We assume 8-bit bytes; + * there's no general way to fix up if that's wrong. + */ + ZTM_MAX +#define ZMAXTIMEOUT ((time_t)(1 << (sizeof(time_t)*8-11))) +}; + +struct ztmout { + /* Type of timeout setting, see enum above */ + enum ztmouttp tp; + /* + * Value for timeout in 100ths of a second if type is not ZTM_NONE. + */ + time_t exp100ths; +}; + +/* + * See if we need a timeout either for a key press or for a + * timed function. + */ + +static void +calc_timeout(struct ztmout *tmoutp, int do_keytmout) +{ + if (do_keytmout && keytimeout > 0) { + if (keytimeout > ZMAXTIMEOUT * 100 /* 24 days for a keypress???? */) + tmoutp->exp100ths = ZMAXTIMEOUT * 100; + else + tmoutp->exp100ths = keytimeout; + tmoutp->tp = ZTM_KEY; + } else + tmoutp->tp = ZTM_NONE; + + if (timedfns) { + for (;;) { + LinkNode tfnode = firstnode(timedfns); + Timedfn tfdat; + time_t diff, exp100ths; + + if (!tfnode) + break; + + tfdat = (Timedfn)getdata(tfnode); + diff = tfdat->when - time(NULL); + if (diff < 0) { + /* Already due; call it and rescan. */ + tfdat->func(); + continue; + } + + if (diff > ZMAXTIMEOUT) { + tmoutp->exp100ths = ZMAXTIMEOUT * 100; + tmoutp->tp = ZTM_MAX; + } else if (diff > 0) { + exp100ths = diff * 100; + if (tmoutp->tp != ZTM_KEY || + exp100ths < tmoutp->exp100ths) { + tmoutp->exp100ths = exp100ths; + tmoutp->tp = ZTM_FUNC; + } + } + break; + } + /* In case we called a function which messed up the display... */ + if (resetneeded) + zrefresh(); + } +} + static int -raw_getbyte(int keytmout, char *cptr) +raw_getbyte(int do_keytmout, char *cptr) { - long exp100ths; int ret; + struct ztmout tmout; #if defined(HAS_TIO) && \ (defined(sun) || (!defined(HAVE_POLL) && !defined(HAVE_SELECT))) struct ttyinfo ti; @@ -402,204 +505,254 @@ raw_getbyte(int keytmout, char *cptr) # endif #endif + calc_timeout(&tmout, do_keytmout); + /* - * Handle timeouts and watched fd's. We only do one at once; - * key timeouts take precedence. This saves tricky timing - * problems with the key timeout. + * Handle timeouts and watched fd's. If a watched fd or a function + * timeout triggers we restart any key timeout. This is likely to + * be harmless: the combination is extremely rare and a function + * is likely to occupy the user for a little while anyway. We used + * to make timeouts take precedence, but we can't now that the + * timeouts may be external, so we may have both a permanent watched + * fd and a long-term timeout. */ - if ((nwatch || keytmout) + if ((nwatch || tmout.tp != ZTM_NONE) #ifdef FIONREAD && ! delayzsetterm #endif ) { - if (!keytmout || keytimeout <= 0) - exp100ths = 0; - else if (keytimeout > 500) - exp100ths = 500; - else - exp100ths = keytimeout; #if defined(HAVE_SELECT) || defined(HAVE_POLL) - if (!keytmout || exp100ths) { - int i, errtry = 0, selret; + int i, errtry = 0, selret; # ifdef HAVE_POLL - int poll_timeout; - int nfds; - struct pollfd *fds; -# else - int fdmax; - struct timeval *tvptr; - struct timeval expire_tv; + int nfds; + struct pollfd *fds; # endif # if defined(HAS_TIO) && defined(sun) - /* - * Yes, I know this is complicated. Yes, I know we - * already have three bits of code to poll the terminal - * down below. No, I don't want to do this either. - * However, it turns out on certain OSes, specifically - * Solaris, that you can't poll typeahead for love nor - * money without actually trying to read it. But - * if we are trying to select (and we need to if we - * are watching other fd's) we won't pick that up. - * So we just try and read it without blocking in - * the time-honoured (i.e. absurdly baroque) termios - * fashion. - */ - gettyinfo(&ti); - ti.tio.c_cc[VMIN] = 0; - settyinfo(&ti); - ret = read(SHTTY, cptr, 1); - ti.tio.c_cc[VMIN] = 1; - settyinfo(&ti); - if (ret > 0) - return 1; + /* + * Yes, I know this is complicated. Yes, I know we + * already have three bits of code to poll the terminal + * down below. No, I don't want to do this either. + * However, it turns out on certain OSes, specifically + * Solaris, that you can't poll typeahead for love nor + * money without actually trying to read it. But + * if we are trying to select (and we need to if we + * are watching other fd's) we won't pick that up. + * So we just try and read it without blocking in + * the time-honoured (i.e. absurdly baroque) termios + * fashion. + */ + gettyinfo(&ti); + ti.tio.c_cc[VMIN] = 0; + settyinfo(&ti); + ret = read(SHTTY, cptr, 1); + ti.tio.c_cc[VMIN] = 1; + settyinfo(&ti); + if (ret > 0) + return 1; # endif # ifdef HAVE_POLL - nfds = keytmout ? 1 : 1 + nwatch; - /* First pollfd is SHTTY, following are the nwatch fds */ - fds = zalloc(sizeof(struct pollfd) * nfds); - if (exp100ths) - poll_timeout = exp100ths * 10; + nfds = 1 + nwatch; + /* First pollfd is SHTTY, following are the nwatch fds */ + fds = zalloc(sizeof(struct pollfd) * nfds); + fds[0].fd = SHTTY; + /* + * POLLIN, POLLIN, POLLIN, + * Keep those fd's POLLIN... + */ + fds[0].events = POLLIN; + for (i = 0; i < nwatch; i++) { + fds[i+1].fd = watch_fds[i]; + fds[i+1].events = POLLIN; + } +# endif + do { +# ifdef HAVE_POLL + int poll_timeout; + + if (tmout.tp != ZTM_NONE) + poll_timeout = tmout.exp100ths * 10; else poll_timeout = -1; - fds[0].fd = SHTTY; - /* - * POLLIN, POLLIN, POLLIN, - * Keep those fd's POLLIN... - */ - fds[0].events = POLLIN; - if (!keytmout) { + selret = poll(fds, errtry ? 1 : nfds, poll_timeout); +# else + int fdmax = SHTTY; + struct timeval *tvptr; + struct timeval expire_tv; + + FD_ZERO(&foofd); + FD_SET(SHTTY, &foofd); + if (!errtry) { for (i = 0; i < nwatch; i++) { - fds[i+1].fd = watch_fds[i]; - fds[i+1].events = POLLIN; + int fd = watch_fds[i]; + FD_SET(fd, &foofd); + if (fd > fdmax) + fdmax = fd; } } -# else - fdmax = SHTTY; - tvptr = NULL; - if (exp100ths) { - expire_tv.tv_sec = exp100ths / 100; - expire_tv.tv_usec = (exp100ths % 100) * 10000L; + + if (tmout.tp != ZTM_NONE) { + expire_tv.tv_sec = tmout.exp100ths / 100; + expire_tv.tv_usec = (tmout.exp100ths % 100) * 10000L; tvptr = &expire_tv; } + else + tvptr = NULL; + + selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd, + NULL, NULL, tvptr); # endif - do { -# ifdef HAVE_POLL - selret = poll(fds, errtry ? 1 : nfds, poll_timeout); -# else - FD_ZERO(&foofd); - FD_SET(SHTTY, &foofd); - if (!keytmout && !errtry) { - for (i = 0; i < nwatch; i++) { - int fd = watch_fds[i]; - FD_SET(fd, &foofd); - if (fd > fdmax) - fdmax = fd; - } - } - selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd, - NULL, NULL, tvptr); -# endif - /* - * Make sure a user interrupt gets passed on straight away. - */ - if (selret < 0 && errflag) - break; + /* + * Make sure a user interrupt gets passed on straight away. + */ + if (selret < 0 && errflag) + break; + /* + * Try to avoid errors on our special fd's from + * messing up reads from the terminal. Try first + * with all fds, then try unsetting the special ones. + */ + if (selret < 0 && !errtry) { + errtry = 1; + continue; + } + if (selret == 0) { /* - * Try to avoid errors on our special fd's from - * messing up reads from the terminal. Try first - * with all fds, then try unsetting the special ones. + * Nothing ready and no error, so we timed out. */ - if (selret < 0 && !keytmout && !errtry) { - errtry = 1; - continue; - } - if (selret == 0) { + switch (tmout.tp) { + case ZTM_NONE: + /* keeps compiler happy if not debugging */ +#ifdef DEBUG + dputs("BUG: timeout fired with no timeout set."); +#endif + /* treat as if a key timeout triggered */ + /*FALLTHROUGH*/ + case ZTM_KEY: /* Special value -2 signals nothing ready */ selret = -2; - } - if (selret < 0) break; - if (!keytmout && nwatch) { + + case ZTM_FUNC: + while (firstnode(timedfns)) { + Timedfn tfdat = (Timedfn)getdata(firstnode(timedfns)); + /* + * It's possible a previous function took + * a long time to run (though it can't + * call zle recursively), so recalculate + * the time on each iteration. + */ + time_t now = time(NULL); + if (tfdat->when > now) + break; + tfdat->func(); + } + /* Function may have messed up the display */ + if (resetneeded) + zrefresh(); + /* We need to recalculate the timeout */ + /*FALLTHROUGH*/ + case ZTM_MAX: /* - * Copy the details of the watch fds in case the - * user decides to delete one from inside the - * handler function. + * Reached the limit of our range, but not the + * actual timeout; recalculate the timeout. + * We're cheating with the key timeout here: + * if one clashed with a function timeout we + * reconsider the key timeout from scratch. + * The effect of this is microscopic. */ - int lnwatch = nwatch; - int *lwatch_fds = zalloc(lnwatch*sizeof(int)); - char **lwatch_funcs = zarrdup(watch_funcs); - memcpy(lwatch_fds, watch_fds, lnwatch*sizeof(int)); - for (i = 0; i < lnwatch; i++) { - if ( + calc_timeout(&tmout, do_keytmout); + break; + } + /* + * If we handled the timeout successfully, + * carry on. + */ + if (selret == 0) + continue; + } + /* If error or unhandled timeout, give up. */ + if (selret < 0) + break; + if (nwatch && !errtry) { + /* + * Copy the details of the watch fds in case the + * user decides to delete one from inside the + * handler function. + */ + int lnwatch = nwatch; + int *lwatch_fds = zalloc(lnwatch*sizeof(int)); + char **lwatch_funcs = zarrdup(watch_funcs); + memcpy(lwatch_fds, watch_fds, lnwatch*sizeof(int)); + for (i = 0; i < lnwatch; i++) { + if ( # ifdef HAVE_POLL - (fds[i+1].revents & POLLIN) + (fds[i+1].revents & POLLIN) # else - FD_ISSET(lwatch_fds[i], &foofd) + FD_ISSET(lwatch_fds[i], &foofd) # endif - ) { - /* Handle the fd. */ - LinkList funcargs = znewlinklist(); - zaddlinknode(funcargs, ztrdup(lwatch_funcs[i])); - { - char buf[BDIGBUFSIZE]; - convbase(buf, lwatch_fds[i], 10); - zaddlinknode(funcargs, ztrdup(buf)); - } + ) { + /* Handle the fd. */ + LinkList funcargs = znewlinklist(); + zaddlinknode(funcargs, ztrdup(lwatch_funcs[i])); + { + char buf[BDIGBUFSIZE]; + convbase(buf, lwatch_fds[i], 10); + zaddlinknode(funcargs, ztrdup(buf)); + } # ifdef HAVE_POLL # ifdef POLLERR - if (fds[i+1].revents & POLLERR) - zaddlinknode(funcargs, ztrdup("err")); + if (fds[i+1].revents & POLLERR) + zaddlinknode(funcargs, ztrdup("err")); # endif # ifdef POLLHUP - if (fds[i+1].revents & POLLHUP) - zaddlinknode(funcargs, ztrdup("hup")); + if (fds[i+1].revents & POLLHUP) + zaddlinknode(funcargs, ztrdup("hup")); # endif # ifdef POLLNVAL - if (fds[i+1].revents & POLLNVAL) - zaddlinknode(funcargs, ztrdup("nval")); + if (fds[i+1].revents & POLLNVAL) + zaddlinknode(funcargs, ztrdup("nval")); # endif # endif - callhookfunc(lwatch_funcs[i], funcargs); - if (errflag) { - /* No sensible way of handling errors here */ - errflag = 0; - /* - * Paranoia: don't run the hooks again this - * time. - */ - errtry = 1; - } - freelinklist(funcargs, freestr); + callhookfunc(lwatch_funcs[i], funcargs); + if (errflag) { + /* No sensible way of handling errors here */ + errflag = 0; + /* + * Paranoia: don't run the hooks again this + * time. + */ + errtry = 1; } + freelinklist(funcargs, freestr); } - /* Function may have invalidated the display. */ - if (resetneeded) - zrefresh(); - zfree(lwatch_fds, lnwatch*sizeof(int)); - freearray(lwatch_funcs); } - } while (! + /* Function may have invalidated the display. */ + if (resetneeded) + zrefresh(); + zfree(lwatch_fds, lnwatch*sizeof(int)); + freearray(lwatch_funcs); + } + } while (! # ifdef HAVE_POLL - (fds[0].revents & POLLIN) + (fds[0].revents & POLLIN) # else - FD_ISSET(SHTTY, &foofd) + FD_ISSET(SHTTY, &foofd) # endif - ); + ); # ifdef HAVE_POLL - zfree(fds, sizeof(struct pollfd) * nfds); + zfree(fds, sizeof(struct pollfd) * nfds); # endif - if (selret < 0) - return selret; - } + if (selret < 0) + return selret; #else # ifdef HAS_TIO ti = shttyinfo; ti.tio.c_lflag &= ~ICANON; ti.tio.c_cc[VMIN] = 0; - ti.tio.c_cc[VTIME] = exp100ths / 10; + ti.tio.c_cc[VTIME] = tmout.exp100ths / 10; # ifdef HAVE_TERMIOS_H tcsetattr(SHTTY, TCSANOW, &ti.tio); # else @@ -623,7 +776,7 @@ raw_getbyte(int keytmout, char *cptr) /**/ mod_export int -getbyte(int keytmout, int *timeout) +getbyte(int do_keytmout, int *timeout) { char cc; unsigned int ret; @@ -656,7 +809,7 @@ getbyte(int keytmout, int *timeout) for (;;) { int q = queue_signal_level(); dont_queue_signals(); - r = raw_getbyte(keytmout, &cc); + r = raw_getbyte(do_keytmout, &cc); restore_queue_signals(q); if (r == -2) { /* timeout */ @@ -733,9 +886,9 @@ getbyte(int keytmout, int *timeout) /**/ mod_export ZLE_INT_T -getfullchar(int keytmout) +getfullchar(int do_keytmout) { - int inchar = getbyte(keytmout, NULL); + int inchar = getbyte(do_keytmout, NULL); #ifdef MULTIBYTE_SUPPORT return getrestchar(inchar); @@ -938,7 +1091,7 @@ zleread(char **lp, char **rp, int flags, int context) return shingetline(); } - keytimeout = getiparam("KEYTIMEOUT"); + keytimeout = (time_t)getiparam("KEYTIMEOUT"); if (!shout) { if (SHTTY != -1) init_shout(); @@ -1551,7 +1704,7 @@ zle_resetprompt(void) mod_export void trashzle(void) { - if (zleactive) { + if (zleactive && !trashedzle) { /* This zrefresh() is just to get the main editor display right and * * get the cursor in the right place. For that reason, we disable * * list display (which would otherwise result in infinite * diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 72d3314ff..debd31a28 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -721,8 +721,7 @@ bin_zle_invalidate(UNUSED(char *name), UNUSED(char **args), UNUSED(Options ops), * true if a completion widget is active. */ if (zleactive) { - if (!trashedzle) - trashzle(); + trashzle(); return 0; } else return 1; |