diff --git a/doc/man/man3/notcurses_input.3.md b/doc/man/man3/notcurses_input.3.md index e2feb00dc..99b5ad7ea 100644 --- a/doc/man/man3/notcurses_input.3.md +++ b/doc/man/man3/notcurses_input.3.md @@ -67,9 +67,8 @@ to fill whenever it reads. **notcurses_get** allows a **struct timespec** to be specified as a timeout. If **ts** is **NULL**, **notcurses_get** will block until it reads input, or is interrupted by a signal. If its values are zeroes, there will be no blocking. -Otherwise, **ts** specifies a minimum time to wait for input before giving up. -On timeout, 0 is returned. Signals in **sigmask** will be masked and blocked in -the same manner as a call to **ppoll(2)**. **sigmask** may be **NULL**. Event +Otherwise, **ts** specifies an absolute deadline (using the same source and +timezone as **gettimeofday(2)**). On timeout, 0 is returned. Event details will be reported in **ni**, unless **ni** is NULL. **notcurses_inputready_fd** provides a file descriptor suitable for use with @@ -181,6 +180,7 @@ are resolved. # SEE ALSO +**gettimeofday(2)**, **poll(2)**, **notcurses(3)**, **notcurses_refresh(3)**, diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 1dc4fb591..bf3f0c7f3 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1070,7 +1070,8 @@ ncinput_equal_p(const ncinput* n1, const ncinput* n2){ // timespec to bound blocking. Returns a single Unicode code point, or // (uint32_t)-1 on error. 'sigmask' may be NULL. Returns 0 on a timeout. If an // event is processed, the return value is the 'id' field from that event. -// 'ni' may be NULL. +// 'ni' may be NULL. 'ts' is an *absolute* time relative to gettimeofday() +// (see pthread_cond_timedwait(3)). API uint32_t notcurses_get(struct notcurses* n, const struct timespec* ts, ncinput* ni) __attribute__ ((nonnull (1))); diff --git a/src/lib/in.c b/src/lib/in.c index a0e492296..568013325 100644 --- a/src/lib/in.c +++ b/src/lib/in.c @@ -23,7 +23,6 @@ // integrate main specials trie with automaton, enable input_errors // wake up input thread when space becomes available // (needs pipes/eventfds) -// handle timeouts static sig_atomic_t resize_seen; @@ -844,6 +843,13 @@ inc_input_events(inputctx* ictx){ pthread_mutex_unlock(&ictx->stats->lock); } +static inline void +inc_input_errors(inputctx* ictx){ + pthread_mutex_lock(&ictx->stats->lock); + ++ictx->stats->s.input_errors; + pthread_mutex_unlock(&ictx->stats->lock); +} + // add a decoded, valid Unicode to the bulk output buffer, or drop it if no // space is available. static void @@ -1472,7 +1478,7 @@ pump_control_read(inputctx* ictx, unsigned char c){ } break; default: - fprintf(stderr, "Reached invalid init state %d\n", ictx->state); + logerror("Reached invalid init state %d\n", ictx->state); return -1; } return 0; @@ -1732,12 +1738,10 @@ block_on_input(inputctx* ictx){ sigdelset(&smask, SIGWINCH); while((events = ppoll(pfds, pfdcount, ts, &smask)) < 0){ #endif -fprintf(stderr, "GOT EVENTS: %d!\n", events); if(errno != EINTR && errno != EAGAIN && errno != EBUSY && errno != EWOULDBLOCK){ return -1; } if(resize_seen){ -fprintf(stderr, "SAW A RESIZE!\n"); return 1; } } @@ -1814,7 +1818,13 @@ internal_get(inputctx* ictx, const struct timespec* ts, ncinput* ni){ } pthread_mutex_lock(&ictx->ilock); while(!ictx->ivalid){ - pthread_cond_wait(&ictx->icond, &ictx->ilock); + if(pthread_cond_timedwait(&ictx->icond, &ictx->ilock, ts)){ + if(errno == ETIMEDOUT){ + return 0; + } + inc_input_errors(ictx); + return (uint32_t)-1; + } } memcpy(ni, &ictx->inputs[ictx->iread], sizeof(*ni)); if(++ictx->iread == ictx->isize){ @@ -1826,18 +1836,6 @@ internal_get(inputctx* ictx, const struct timespec* ts, ncinput* ni){ return ni->id; } -struct initial_responses* inputlayer_get_responses(inputctx* ictx){ - struct initial_responses* iresp; - pthread_mutex_lock(&ictx->ilock); - while(!ictx->initdata_complete){ - pthread_cond_wait(&ictx->icond, &ictx->ilock); - } - iresp = ictx->initdata_complete; - ictx->initdata_complete = NULL; - pthread_mutex_unlock(&ictx->ilock); - return iresp; -} - // infp has already been set non-blocking uint32_t notcurses_get(notcurses* nc, const struct timespec* ts, ncinput* ni){ uint32_t r = internal_get(nc->tcache.ictx, ts, ni); @@ -1984,3 +1982,15 @@ linesigs_enable(tinfo* ti){ int notcurses_linesigs_enable(notcurses* n){ return linesigs_enable(&n->tcache); } + +struct initial_responses* inputlayer_get_responses(inputctx* ictx){ + struct initial_responses* iresp; + pthread_mutex_lock(&ictx->ilock); + while(!ictx->initdata_complete){ + pthread_cond_wait(&ictx->icond, &ictx->ilock); + } + iresp = ictx->initdata_complete; + ictx->initdata_complete = NULL; + pthread_mutex_unlock(&ictx->ilock); + return iresp; +}