diff --git a/src/xcb.h b/src/xcb.h index 09e123e..231986b 100644 --- a/src/xcb.h +++ b/src/xcb.h @@ -337,6 +337,13 @@ xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c, */ xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c, xcb_special_event_t *se); + +/** + * @brief Returns the next event from a special queue, blocking until one arrives + */ +xcb_generic_event_t *xcb_wait_for_special_event_with_timeout(xcb_connection_t *c, + xcb_special_event_t *se, + unsigned int millisecs_timeout); /** * @typedef typedef struct xcb_extension_t xcb_extension_t */ diff --git a/src/xcb_conn.c b/src/xcb_conn.c index 8dab658..62002cb 100644 --- a/src/xcb_conn.c +++ b/src/xcb_conn.c @@ -37,6 +37,7 @@ #include #include #include +#include /* I bet this does not work on Windows... */ #include "xcb.h" #include "xcbint.h" @@ -434,6 +435,11 @@ xcb_connection_t *_xcb_conn_ret_error(int err) } int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count) +{ + return _xcb_conn_timedwait(c, cond, vector, count, NULL, NULL); +} + +int _xcb_conn_timedwait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count, struct timeval *now, struct timeval *deadline) { int ret; #if USE_POLL @@ -445,7 +451,13 @@ int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vec /* If the thing I should be doing is already being done, wait for it. */ if(count ? c->out.writing : c->in.reading) { - pthread_cond_wait(cond, &c->iolock); + if(deadline) { + struct timespec spec; + spec.tv_sec = deadline->tv_sec; + spec.tv_nsec = deadline->tv_usec * 100; + pthread_cond_timedwait(cond, &c->iolock, &spec); + } else + pthread_cond_wait(cond, &c->iolock); return 1; } @@ -477,7 +489,13 @@ int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vec pthread_mutex_unlock(&c->iolock); do { #if USE_POLL - ret = poll(&fd, 1, -1); + int timeout = -1; + if(deadline && now) { + struct timeval diff; + timersub(deadline, now, &diff); + timeout = now->tv_sec * 1000 + now->tv_usec / 1000; + } + ret = poll(&fd, 1, timeout); /* If poll() returns an event we didn't expect, such as POLLNVAL, treat * it as if it failed. */ if(ret >= 0 && (fd.revents & ~fd.events)) @@ -486,7 +504,13 @@ int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vec break; } #else - ret = select(c->fd + 1, &rfds, &wfds, 0, 0); + struct timeval diff; + struct timeval *timeout = NULL; + if(deadline && now) { + timersub(deadline, now, &diff); + timeout = &diff; + } + ret = select(c->fd + 1, &rfds, &wfds, 0, timeout); #endif } while (ret == -1 && errno == EINTR); if(ret < 0) diff --git a/src/xcb_in.c b/src/xcb_in.c index 796b4e9..4018e0c 100644 --- a/src/xcb_in.c +++ b/src/xcb_in.c @@ -35,6 +35,7 @@ #include #include #include +#include /* I bet this does not work on Windows... */ #if USE_POLL #include @@ -778,6 +779,42 @@ xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c, return event; } +xcb_generic_event_t *xcb_wait_for_special_event_with_timeout(xcb_connection_t *c, + xcb_special_event_t *se, + unsigned int millisecs_timeout) +{ + struct timeval now, deadline, timeout; + special_list special; + xcb_generic_event_t *event; + + if(c->has_error) + return 0; + + timeout.tv_sec = millisecs_timeout / 1000; + timeout.tv_usec = (millisecs_timeout % 1000) * 1000; + gettimeofday(&now, NULL); // TODO: Check for errors? + timeradd(&now, &timeout, &deadline); + + pthread_mutex_lock(&c->iolock); + + insert_special(&c->in.special_waiters, &special, se); + + /* get_special_event returns 0 on empty list. */ + while(!(event = get_special_event(c, se))) { + if(timercmp(&deadline, &now, <)) + break; + if(!_xcb_conn_timedwait(c, &se->special_event_cond, 0, 0, &now, &deadline)) + break; + gettimeofday(&now, NULL); // TODO: Check for errors? + } + + remove_special(&c->in.special_waiters, &special); + + _xcb_in_wake_up_next_reader(c); + pthread_mutex_unlock(&c->iolock); + return event; +} + xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c, xcb_special_event_t *se) { diff --git a/src/xcbint.h b/src/xcbint.h index cef9821..0eda037 100644 --- a/src/xcbint.h +++ b/src/xcbint.h @@ -217,6 +217,7 @@ void _xcb_conn_shutdown(xcb_connection_t *c, int err); xcb_connection_t *_xcb_conn_ret_error(int err); int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count); +int _xcb_conn_timedwait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count, struct timeval *now, struct timeval *deadline); /* xcb_auth.c */