Commit Diff


commit - b19b8dbca985e2f567bb3f476b116ea18c1ca9a2
commit + 98d3e6c172747dc58042bde09a848d3e03572934
blob - 659ab9e8b4f977072e2cacadbf9af8244697ea4a
blob + 46ec1afce4bacfa28a5124980c8dcf89ff1c154c
--- Makefile.am
+++ Makefile.am
@@ -4,7 +4,9 @@ bin_PROGRAMS =		telescope telescope-identity
 
 EXTRA_telescope_SOURCES = compat/ohash.h compat/queue.h compat/imsg.h contrib
 
-telescope_SOURCES =	certs.c			\
+telescope_SOURCES =	bufio.c			\
+			bufio.h			\
+			certs.c			\
 			certs.h			\
 			cmd.c			\
 			cmd.gen.c		\
@@ -18,6 +20,8 @@ telescope_SOURCES =	certs.c			\
 			defaults.h		\
 			downloads.c		\
 			emoji-matcher.c		\
+			ev.c			\
+			ev.h			\
 			fs.c			\
 			fs.h			\
 			gencmd.awk		\
@@ -61,6 +65,8 @@ telescope_SOURCES =	certs.c			\
 telescope_identity_SOURCES =			\
 			certs.c			\
 			certs.h			\
+			ev.c			\
+			ev.h			\
 			fs.c			\
 			fs.h			\
 			hist.c			\
@@ -68,7 +74,7 @@ telescope_identity_SOURCES =			\
 			identity.c		\
 			parser.c		\
 			parser.h		\
-			utils.c		\
+			utils.c			\
 			utils.h
 
 noinst_PROGRAMS =	pagebundler
blob - 8928556709972b28db62863d0b7aeb0dd403b236
blob + 99b46defa92628536ea7bd98665fe2b7cbd877e0
--- cmd.c
+++ cmd.c
@@ -24,6 +24,7 @@
 #include "cmd.h"
 #include "compl.h"
 #include "defaults.h"
+#include "ev.h"
 #include "hist.h"
 #include "keymap.h"
 #include "mcache.h"
@@ -278,7 +279,7 @@ kill_telescope_cb(int r, struct tab *tab)
 {
 	if (r) {
 		save_session();
-		event_loopbreak();
+		ev_break();
 	}
 }
 
blob - de06e6b654820fabe2d2634479ec69a0d184441c
blob + e83e196c254ef03fb7eeec1c996028ee967634be
--- compat.h
+++ compat.h
@@ -52,19 +52,6 @@
 # error no compatible endian.h header found
 #endif
 
-#if HAVE_EVENT2
-# include <event2/event.h>
-# include <event2/event_compat.h>
-# include <event2/event_struct.h>
-# include <event2/buffer.h>
-# include <event2/buffer_compat.h>
-# include <event2/bufferevent.h>
-# include <event2/bufferevent_struct.h>
-# include <event2/bufferevent_compat.h>
-#else
-# include <event.h>
-#endif
-
 #ifdef HAVE_QUEUE_H
 # include <sys/queue.h>
 #else
blob - 73930b982ee6b2c5b3dabf2fe2d223b2f959dd9f
blob + db4a22fe78ee5db9f37557e66e93c8729dd84519
--- control.c
+++ control.c
@@ -30,6 +30,7 @@
 #include <unistd.h>
 
 #include "control.h"
+#include "ev.h"
 #include "minibuffer.h"
 #include "telescope.h"
 #include "utils.h"
@@ -38,8 +39,7 @@
 #define	CONTROL_BACKLOG	5
 
 struct {
-	struct event	ev;
-	struct event	evt;
+	unsigned long	timeout;
 	int		fd;
 } control_state = {.fd = -1};
 
@@ -110,23 +110,19 @@ control_listen(int fd)
 		return (-1);
 	}
 
-	event_set(&control_state.ev, control_state.fd, EV_READ,
-	    control_accept, NULL);
-	event_add(&control_state.ev, NULL);
-	evtimer_set(&control_state.evt, control_accept, NULL);
-
+	ev_add(control_state.fd, EV_READ, control_accept, NULL);
 	return (0);
 }
 
 void
-control_accept(int listenfd, short event, void *bula)
+control_accept(int listenfd, int event, void *bula)
 {
 	int			 connfd;
 	socklen_t		 len;
 	struct sockaddr_un	 sun;
 	struct ctl_conn		*c;
 
-	event_add(&control_state.ev, NULL);
+	ev_add(control_state.fd, EV_READ, control_accept, NULL);
 	if ((event & EV_TIMEOUT))
 		return;
 
@@ -134,13 +130,14 @@ control_accept(int listenfd, short event, void *bula)
 	if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) {
 		/*
 		 * Pause accept if we are out of file descriptors, or
-		 * libevent will haunt us here too.
+		 * ev will haunt us here too.
 		 */
 		if (errno == ENFILE || errno == EMFILE) {
 			struct timeval evtpause = { 1, 0 };
 
-			event_del(&control_state.ev);
-			evtimer_add(&control_state.evt, &evtpause);
+			ev_del(control_state.fd);
+			control_state.timeout = ev_timer(&evtpause,
+			    control_accept, NULL);
 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
 		    errno != ECONNABORTED)
 			message("%s: accept4: %s", __func__, strerror(errno));
@@ -163,9 +160,12 @@ control_accept(int listenfd, short event, void *bula)
 	imsg_init(&c->iev.ibuf, connfd);
 	c->iev.handler = control_dispatch_imsg;
 	c->iev.events = EV_READ;
-	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
-	    c->iev.handler, &c->iev);
-	event_add(&c->iev.ev, NULL);
+	if (ev_add(connfd, c->iev.events, c->iev.handler, &c->iev) == -1) {
+		message("%s: ev_add: %s", __func__, strerror(errno));
+		close(connfd);
+		free(c);
+		return;
+	}
 
 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
 }
@@ -209,20 +209,21 @@ control_close(int fd)
 	msgbuf_clear(&c->iev.ibuf.w);
 	TAILQ_REMOVE(&ctl_conns, c, entry);
 
-	event_del(&c->iev.ev);
+	ev_del(c->iev.ibuf.fd);
 	close(c->iev.ibuf.fd);
 
 	/* Some file descriptors are available again. */
-	if (evtimer_pending(&control_state.evt, NULL)) {
-		evtimer_del(&control_state.evt);
-		event_add(&control_state.ev, NULL);
+	if (ev_timer_pending(control_state.timeout)) {
+		ev_timer_cancel(control_state.timeout);
+		control_state.timeout = 0;
+		ev_add(control_state.fd, EV_READ, control_accept, NULL);
 	}
 
 	free(c);
 }
 
 void
-control_dispatch_imsg(int fd, short event, void *bula)
+control_dispatch_imsg(int fd, int event, void *bula)
 {
 	struct ctl_conn	*c;
 	struct imsg	 imsg;
blob - c779aaeb95cf5546bf933fe861acf75b7474d386
blob + 83562a8d7d6323eb000e8daf7bff0c57faf64700
--- control.h
+++ control.h
@@ -19,6 +19,6 @@
 #ifndef	SMALL
 int	control_init(char *);
 int	control_listen(int);
-void	control_accept(int, short, void *);
-void	control_dispatch_imsg(int, short, void *);
+void	control_accept(int, int, void *);
+void	control_dispatch_imsg(int, int, void *);
 #endif	/* SMALL */
blob - 3e60428279ef2a543079d6aae59d4faa4249cabd
blob + ccb1c1f16740952e7d570287251025d2eadd9a74
--- mcache.c
+++ mcache.c
@@ -16,17 +16,20 @@
 
 #include "compat.h"
 
+#include <sys/time.h>
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 
+#include "ev.h"
 #include "hist.h"
 #include "mcache.h"
 #include "parser.h"
 #include "utils.h"
 
 static struct timeval tv = { 5 * 60, 0 };
-static struct event timerev;
+static unsigned int timeout;
 
 static struct ohash	h;
 static size_t		npages;
@@ -59,7 +62,7 @@ mcache_free_entry(const char *url)
 }
 
 static void
-clean_old_entries(int fd, short ev, void *data)
+clean_old_entries(int fd, int ev, void *data)
 {
 	struct mcache_entry	*e;
 	unsigned int		 i;
@@ -72,7 +75,7 @@ clean_old_entries(int fd, short ev, void *data)
 		if (e->ts < treshold)
 			mcache_free_entry(e->url);
 
-	evtimer_add(&timerev, &tv);
+	timeout = ev_timer(&tv, clean_old_entries, NULL);
 }
 
 void
@@ -86,8 +89,6 @@ mcache_init(void)
 	};
 
 	ohash_init(&h, 5, &info);
-
-	evtimer_set(&timerev, clean_old_entries, NULL);
 }
 
 int
@@ -127,8 +128,8 @@ mcache_tab(struct tab *tab)
 	npages++;
 	tot += e->buflen;
 
-	if (!evtimer_pending(&timerev, NULL))
-		evtimer_add(&timerev, &tv);
+	if (!ev_timer_pending(timeout))
+		timeout = ev_timer(&tv, clean_old_entries, NULL);
 
 	return 0;
 
blob - 8b73b7a720677518f8c1cbf84d5024ba473567fa
blob + b3c3efce84a1314766b58c3874096490c548edf7
--- minibuffer.c
+++ minibuffer.c
@@ -16,6 +16,8 @@
 
 #include "compat.h"
 
+#include <sys/time.h>
+
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
@@ -25,6 +27,7 @@
 #include "certs.h"
 #include "cmd.h"
 #include "defaults.h"
+#include "ev.h"
 #include "fs.h"
 #include "hist.h"
 #include "iri.h"
@@ -45,10 +48,10 @@ static void		 yornp_abort(void);
 static void		 read_self_insert(void);
 static void		 read_abort(void);
 static void		 read_select(void);
-static void		 handle_clear_echoarea(int, short, void *);
+static void		 handle_clear_echoarea(int, int, void *);
 
-static struct event	clechoev;
-static struct timeval	clechoev_timer = { 5, 0 };
+static unsigned long	clechotimer;
+static struct timeval	clechotv = { 5, 0 };
 
 static void (*yornp_cb)(int, struct tab *);
 static struct tab *yornp_data;
@@ -642,7 +645,7 @@ minibuffer_read(const char *prompt, void (*fn)(const c
 }
 
 static void
-handle_clear_echoarea(int fd, short ev, void *d)
+handle_clear_echoarea(int fd, int ev, void *d)
 {
 	free(ministate.curmesg);
 	ministate.curmesg = NULL;
@@ -653,15 +656,14 @@ handle_clear_echoarea(int fd, short ev, void *d)
 void
 vmessage(const char *fmt, va_list ap)
 {
-	if (evtimer_pending(&clechoev, NULL))
-		evtimer_del(&clechoev);
+	ev_timer_cancel(clechotimer);
 
 	free(ministate.curmesg);
 	ministate.curmesg = NULL;
 
 	if (fmt != NULL) {
-		evtimer_set(&clechoev, handle_clear_echoarea, NULL);
-		evtimer_add(&clechoev, &clechoev_timer);
+		clechotimer = ev_timer(&clechotv, handle_clear_echoarea,
+		    NULL);
 
 		/* TODO: what to do if the allocation fails here? */
 		if (vasprintf(&ministate.curmesg, fmt, ap) == -1)
@@ -697,6 +699,4 @@ minibuffer_init(void)
 	ministate.vline.parent = &ministate.line;
 	ministate.buffer.page.name = "*minibuffer*";
 	ministate.buffer.current_line = &ministate.vline;
-
-	evtimer_set(&clechoev, handle_clear_echoarea, NULL);
 }
blob - f9f6b98d8a2c8649026269776fb9304e788086ca
blob + d0bf32c05c2e6a792754779e460ce76a4c17b200
--- net.c
+++ net.c
@@ -16,6 +16,9 @@
 
 #include "compat.h"
 
+/* XXX needs some work to run on top of ev */
+#undef HAVE_ASR_RUN
+
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -38,17 +41,28 @@
 # include <asr.h>
 #endif
 
+#include "bufio.h"
+#include "ev.h"
 #include "telescope.h"
 #include "utils.h"
 
 static struct imsgev		*iev_ui;
 
+enum conn_state {
+	CONN_CONNECTING,
+	CONN_HANDSHAKE,
+	CONN_HEADER,
+	CONN_BODY,
+	CONN_CLOSE,
+	CONN_ERROR,
+};
+
 /* a pending request */
 struct req {
 	uint32_t		 id;
+	enum conn_state		 state;
 	int			 proto;
 	int			 fd;
-	struct tls		*ctx;
 	char			*host;
 	char			*port;
 	char			*req;
@@ -56,9 +70,11 @@ struct req {
 	void			*ccert;
 	size_t			 ccert_len;
 	int			 ccert_fd;
-	int			 done_header;
-	struct bufferevent	*bev;
 
+	int			 eof;
+	unsigned int		 handshake_tout;
+	struct bufio		 bio;
+
 	int			 conn_error;
 	const char		*cause;
 
@@ -75,7 +91,6 @@ static struct req	*req_by_id(uint32_t);
 
 static void	 die(void) __attribute__((__noreturn__));
 
-static void	 try_to_connect(int, short, void*);
 
 #if HAVE_ASR_RUN
 static void	 query_done(struct asr_result*, void*);
@@ -86,40 +101,18 @@ static void	 close_with_err(struct req*, const char*);
 static void	 close_with_errf(struct req*, const char*, ...)
     __attribute__((format(printf, 2, 3)));
 
-static void	 net_tls_handshake(int, short, void *);
-static void	 net_tls_readcb(int, short, void *);
-static void	 net_tls_writecb(int, short, void *);
-
+static int	 try_to_connect(struct req *);
 static int	 gemini_parse_reply(struct req *, const char *, size_t);
+static void	 net_ev(int, int, void *);
+static void	 handle_dispatch_imsg(int, int, void*);
 
-static void	 net_ready(struct req *req);
-static void	 net_read(struct bufferevent *, void *);
-static void	 net_write(struct bufferevent *, void *);
-static void	 net_error(struct bufferevent *, short, void *);
-
-static void	 handle_dispatch_imsg(int, short, void*);
-
 static int	 net_send_ui(int, uint32_t, const void *, uint16_t);
 
 /* TODO: making this customizable */
 struct timeval timeout_for_handshake = { 5, 0 };
 
-typedef void (*statefn)(int, short, void*);
-
 TAILQ_HEAD(, req) reqhead;
 
-static inline void
-yield_r(struct req *req, statefn fn, struct timeval *tv)
-{
-	event_once(req->fd, EV_READ, fn, req, tv);
-}
-
-static inline void
-yield_w(struct req *req, statefn fn, struct timeval *tv)
-{
-	event_once(req->fd, EV_WRITE, fn, req, tv);
-}
-
 static struct req *
 req_by_id(uint32_t id)
 {
@@ -139,117 +132,6 @@ die(void)
 	abort(); 		/* TODO */
 }
 
-static void
-try_to_connect(int fd, short ev, void *d)
-{
-	struct req	*req = d;
-	int		 error = 0;
-	socklen_t	 len = sizeof(error);
-
-again:
-	if (req->p == NULL)
-		goto err;
-
-	if (req->fd != -1) {
-		if (getsockopt(req->fd, SOL_SOCKET, SO_ERROR, &error,
-		    &len) == -1)
-			goto err;
-		if (error != 0) {
-			req->conn_error = error;
-			req->cause = "connect";
-			close(req->fd);
-			req->fd = -1;
-			req->p = req->p->ai_next;
-			goto again;
-		}
-		goto done;
-	}
-
-	req->fd = socket(req->p->ai_family, req->p->ai_socktype,
-	    req->p->ai_protocol);
-	if (req->fd == -1) {
-		req->conn_error = errno;
-		req->cause = "socket";
-		req->p = req->p->ai_next;
-		goto again;
-	}
-
-	if (!mark_nonblock_cloexec(req->fd)) {
-		req->conn_error = errno;
-		req->cause = "setsockopt";
-		goto err;
-	}
-	if (connect(req->fd, req->p->ai_addr, req->p->ai_addrlen) == 0)
-		goto done;
-	yield_w(req, try_to_connect, NULL);
-	return;
-
-err:
-	freeaddrinfo(req->servinfo);
-	close_with_errf(req, "failed to connect to %s (%s: %s.)", req->host,
-	    req->cause, strerror(req->conn_error));
-	return;
-
-done:
-	freeaddrinfo(req->servinfo);
-
-	switch (req->proto) {
-	case PROTO_FINGER:
-	case PROTO_GOPHER:
-		/* finger and gopher don't have a header nor TLS */
-		req->done_header = 1;
-		net_ready(req);
-		break;
-
-	case PROTO_GEMINI: {
-		struct tls_config *conf;
-
-		if ((conf = tls_config_new()) == NULL)
-			die();
-
-		tls_config_insecure_noverifycert(conf);
-		tls_config_insecure_noverifyname(conf);
-
-		if (req->ccert && tls_config_set_keypair_mem(conf,
-		    req->ccert, req->ccert_len, req->ccert, req->ccert_len)
-		    == -1) {
-			close_with_errf(req, "failed to load keypair: %s",
-			    tls_config_error(conf));
-			tls_config_free(conf);
-			return;
-		}
-
-		/* prepare tls */
-		if ((req->ctx = tls_client()) == NULL) {
-			close_with_errf(req, "tls_client: %s",
-			    strerror(errno));
-			tls_config_free(conf);
-			return;
-		}
-
-		if (tls_configure(req->ctx, conf) == -1) {
-			close_with_errf(req, "tls_configure: %s",
-			    tls_error(req->ctx));
-			tls_config_free(conf);
-			return;
-		}
-		tls_config_free(conf);
-
-		if (tls_connect_socket(req->ctx, req->fd, req->host)
-		    == -1) {
-			close_with_errf(req, "tls_connect_socket: %s",
-			    tls_error(req->ctx));
-			return;
-		}
-		yield_w(req, net_tls_handshake, &timeout_for_handshake);
-		break;
-	}
-
-	default:
-		die();
-	}
-}
-
 #if HAVE_ASR_RUN
 static void
 query_done(struct asr_result *res, void *d)
@@ -266,7 +148,8 @@ query_done(struct asr_result *res, void *d)
 	req->fd = -1;
 	req->servinfo = res->ar_addrinfo;
 	req->p = res->ar_addrinfo;
-	try_to_connect(0, 0, req);
+	req->state = CONN_CONNECTING;
+	net_ev(-1, EV_READ, req);
 }
 
 static void
@@ -300,39 +183,41 @@ conn_towards(struct req *req)
 
 	req->fd = -1;
 	req->p = req->servinfo;
-	try_to_connect(0, 0, req);
+	req->state = CONN_CONNECTING;
+	net_ev(-1, EV_READ, req);
 }
 #endif
 
 static void
-close_conn(int fd, short ev, void *d)
+close_conn(int fd, int ev, void *d)
 {
 	struct req	*req = d;
 
+	if (req->state != CONN_ERROR)
+		req->state = CONN_CLOSE;
+
 #if HAVE_ASR_RUN
 	if (req->asrev != NULL)
 		event_asr_abort(req->asrev);
 #endif
 
-	if (req->bev != NULL) {
-		bufferevent_free(req->bev);
-		req->bev = NULL;
+	if (req->handshake_tout != 0) {
+		ev_timer_cancel(req->handshake_tout);
+		req->handshake_tout = 0;
 	}
 
-	if (req->ctx != NULL) {
-		switch (tls_close(req->ctx)) {
-		case TLS_WANT_POLLIN:
-			yield_r(req, close_conn, NULL);
-			return;
-		case TLS_WANT_POLLOUT:
-			yield_w(req, close_conn, NULL);
-			return;
-		}
-
-		tls_free(req->ctx);
-		req->ctx = NULL;
+	if (req->state == CONN_CLOSE &&
+	    bufio_close(&req->bio) == -1 &&
+	    errno == EAGAIN) {
+		ev_add(req->fd, bufio_ev(&req->bio), close_conn, req);
+		return;
 	}
 
+	if (req->servinfo)
+		freeaddrinfo(req->servinfo);
+
+	bufio_free(&req->bio);
+
 	if (req->ccert != NULL) {
 		munmap(req->ccert, req->ccert_len);
 		close(req->ccert_fd);
@@ -343,14 +228,17 @@ close_conn(int fd, short ev, void *d)
 	free(req->req);
 
 	TAILQ_REMOVE(&reqhead, req, reqs);
-	if (req->fd != -1)
+	if (req->fd != -1) {
+		ev_del(req->fd);
 		close(req->fd);
+	}
 	free(req);
 }
 
 static void
 close_with_err(struct req *req, const char *err)
 {
+	req->state = CONN_ERROR;
 	net_send_ui(IMSG_ERR, req->id, err, strlen(err)+1);
 	close_conn(0, 0, req);
 }
@@ -370,138 +258,54 @@ close_with_errf(struct req *req, const char *fmt, ...)
 	free(s);
 }
 
-static void
-net_tls_handshake(int fd, short event, void *d)
+static int
+try_to_connect(struct req *req)
 {
-	struct req	*req = d;
-	const char	*hash;
+	int		 error;
+	socklen_t	 len = sizeof(error);
 
-	if (event == EV_TIMEOUT) {
-		close_with_err(req, "Timeout loading page");
-		return;
-	}
+ again:
+	if (req->p == NULL)
+		return (-1);
 
-	switch (tls_handshake(req->ctx)) {
-	case TLS_WANT_POLLIN:
-		yield_r(req, net_tls_handshake, NULL);
-		return;
-	case TLS_WANT_POLLOUT:
-		yield_w(req, net_tls_handshake, NULL);
-		return;
-	}
+	if (req->fd != -1) {
+		if (getsockopt(req->fd, SOL_SOCKET, SO_ERROR, &error,
+		    &len) == -1) {
+			req->conn_error = errno;
+			req->cause = "getsockopt";
+			return (-1);
+		}
 
-	hash = tls_peer_cert_hash(req->ctx);
-	if (hash == NULL) {
-		close_with_errf(req, "handshake failed: %s",
-		    tls_error(req->ctx));
-		return;
-	}
-	net_send_ui(IMSG_CHECK_CERT, req->id, hash, strlen(hash)+1);
-}
+		if (error == 0) /* connected */
+			return (0);
 
-static void
-net_tls_readcb(int fd, short event, void *d)
-{
-	struct bufferevent	*bufev = d;
-	struct req		*req = bufev->cbarg;
-	char			 buf[IBUF_READ_SIZE];
-	int			 what = EVBUFFER_READ;
-	int			 howmuch = IBUF_READ_SIZE;
-	int			 res;
-	ssize_t			 ret;
-	size_t			 len;
-
-	if (event == EV_TIMEOUT) {
-		what |= EVBUFFER_TIMEOUT;
-		goto err;
+		req->conn_error = error;
+		req->cause = "connect";
+		close(req->fd);
+		req->fd = -1;
+		req->p = req->p->ai_next;
+		goto again;
 	}
 
-	if (bufev->wm_read.high != 0)
-		howmuch = MIN(sizeof(buf), bufev->wm_read.high);
-
-	switch (ret = tls_read(req->ctx, buf, howmuch)) {
-	case TLS_WANT_POLLIN:
-	case TLS_WANT_POLLOUT:
-		goto retry;
-	case -1:
-		what |= EVBUFFER_ERROR;
-		goto err;
-	}
-	len = ret;
-
-	if (len == 0) {
-		what |= EVBUFFER_EOF;
-		goto err;
-	}
-
-	res = evbuffer_add(bufev->input, buf, len);
-	if (res == -1) {
-		what |= EVBUFFER_ERROR;
-		goto err;
-	}
-
-	event_add(&bufev->ev_read, NULL);
-
-	len = EVBUFFER_LENGTH(bufev->input);
-	if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
-		return;
-
-	if (bufev->readcb != NULL)
-		(*bufev->readcb)(bufev, bufev->cbarg);
-	return;
-
-retry:
-	event_add(&bufev->ev_read, NULL);
-	return;
-
-err:
-	(*bufev->errorcb)(bufev, what, bufev->cbarg);
-}
-
-static void
-net_tls_writecb(int fd, short event, void *d)
-{
-	struct bufferevent	*bufev = d;
-	struct req		*req = bufev->cbarg;
-	ssize_t			 ret;
-	size_t			 len;
-	short			 what = EVBUFFER_WRITE;
-
-	if (event & EV_TIMEOUT) {
-		what |= EVBUFFER_TIMEOUT;
-		goto err;
+	req->fd = socket(req->p->ai_family, req->p->ai_socktype,
+	    req->p->ai_protocol);
+	if (req->fd == -1) {
+		req->conn_error = errno;
+		req->cause = "socket";
+		req->p = req->p->ai_next;
+		goto again;
 	}
 
-	if (EVBUFFER_LENGTH(bufev->output) != 0) {
-		ret = tls_write(req->ctx, EVBUFFER_DATA(bufev->output),
-		    EVBUFFER_LENGTH(bufev->output));
-		switch (ret) {
-		case TLS_WANT_POLLIN:
-		case TLS_WANT_POLLOUT:
-			goto retry;
-		case -1:
-			what |= EVBUFFER_ERROR;
-			goto err;
-		}
-		len = ret;
-
-		evbuffer_drain(bufev->output, len);
+	if (!mark_nonblock_cloexec(req->fd)) {
+		req->conn_error = errno;
+		req->cause = "setsockopt";
+		return (-1);
 	}
 
-	if (EVBUFFER_LENGTH(bufev->output) != 0)
-		event_add(&bufev->ev_write, NULL);
-
-	if (bufev->writecb != NULL &&
-	    EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
-		(*bufev->writecb)(bufev, bufev->cbarg);
-	return;
-
-retry:
-	event_add(&bufev->ev_write, NULL);
-	return;
-
-err:
-	(*bufev->errorcb)(bufev, what, bufev->cbarg);
+	if (connect(req->fd, req->p->ai_addr, req->p->ai_addrlen) == 0)
+		return (0);
+	errno = EAGAIN;
+	return (-1);
 }
 
 static int
@@ -533,140 +337,164 @@ gemini_parse_reply(struct req *req, const char *header
 	imsg_close(&iev_ui->ibuf, ibuf);
 	imsg_event_add(iev_ui);
 
-	bufferevent_disable(req->bev, EV_READ|EV_WRITE);
+	/* pause until we've told go go ahead */
+	ev_del(req->fd);
 
 	return code;
 }
 
-/* called when we're ready to read/write */
-static void
-net_ready(struct req *req)
+static inline int
+net_send_req(struct req *req)
 {
-	req->bev = bufferevent_new(req->fd, net_read, net_write, net_error,
-	    req);
-	if (req->bev == NULL)
-		die();
-
-#if HAVE_EVENT2
-	evbuffer_unfreeze(req->bev->input, 0);
-	evbuffer_unfreeze(req->bev->output, 1);
-#endif
-
-	/* setup tls i/o layer */
-	if (req->ctx != NULL) {
-		event_set(&req->bev->ev_read, req->fd, EV_READ,
-		    net_tls_readcb, req->bev);
-		event_set(&req->bev->ev_write, req->fd, EV_WRITE,
-		    net_tls_writecb, req->bev);
-	}
-
-	/* TODO: adjust watermarks */
-	bufferevent_setwatermark(req->bev, EV_WRITE, 1, 0);
-	bufferevent_setwatermark(req->bev, EV_READ,  1, 0);
-
-	bufferevent_enable(req->bev, EV_READ|EV_WRITE);
-
-	bufferevent_write(req->bev, req->req, req->len);
+	return (bufio_compose(&req->bio, req->req, req->len));
 }
 
-/* called after a read has been done */
 static void
-net_read(struct bufferevent *bev, void *d)
+net_ev(int fd, int ev, void *d)
 {
 	static char	 buf[4096];
 	struct req	*req = d;
-	struct evbuffer	*src = EVBUFFER_INPUT(bev);
+	const char	*hash;
+	ssize_t		 read;
 	size_t		 len;
+	char		*header, *endl;
 	int		 r;
-	char		*header;
 
-	if (!req->done_header) {
-		header = evbuffer_readln(src, &len, EVBUFFER_EOL_CRLF_STRICT);
-		if (header == NULL && EVBUFFER_LENGTH(src) >= 1024) {
-			(*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg);
+	if (ev == EV_TIMEOUT) {
+		close_with_err(req, "Timeout loading page");
+		return;
+	}
+
+	if (req->state == CONN_CONNECTING) {
+		ev_del(req->fd);
+		if (try_to_connect(req) == -1) {
+			if (req->fd != -1 && errno == EAGAIN) {
+				ev_add(req->fd, EV_WRITE, net_ev, req);
+				return;
+			}
+			close_with_errf(req, "failed to connect to %s"
+			    " (%s: %s)", req->host, req->cause,
+			    strerror(req->conn_error));
 			return;
 		}
-		if (header == NULL)
+
+		bufio_set_fd(&req->bio, req->fd);
+
+		switch (req->proto) {
+		case PROTO_FINGER:
+		case PROTO_GOPHER:
+			/* finger and gopher don't have a header nor TLS */
+			req->state = CONN_BODY;
+			if (net_send_req(req) == -1) {
+				close_with_err(req, "failed to send request");
+				return;
+			}
+			break;
+		case PROTO_GEMINI:
+			req->state = CONN_HANDSHAKE;
+			if (bufio_starttls(&req->bio, req->host, 1,
+			    req->ccert, req->ccert_len,
+			    req->ccert, req->ccert_len) == -1) {
+				close_with_err(req, "failed to setup TLS");
+				return;
+			}
+			req->handshake_tout = ev_timer(&timeout_for_handshake,
+			    net_ev, req);
+			if (req->handshake_tout == 0) {
+				close_with_err(req, "failed to setup"
+				    " handshake timer");
+				return;
+			}
+			break;
+		}
+	}
+
+	if (req->state == CONN_HANDSHAKE) {
+		if (bufio_handshake(&req->bio) == -1 && errno == EAGAIN) {
+			ev_add(req->fd, bufio_ev(&req->bio),
+			    net_ev, req);
 			return;
-		r = gemini_parse_reply(req, header, len);
-		free(header);
-		req->done_header = 1;
-		if (r == 0) {
-			(*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg);
-			return;
 		}
-		if (r < 20 || r >= 30) {
-			close_conn(0, 0, req);
+
+		ev_timer_cancel(req->handshake_tout);
+		req->handshake_tout = 0;
+
+		req->state = CONN_HEADER;
+
+		/* pause until we've told the certificate is OK */
+		ev_del(req->fd);
+
+		hash = tls_peer_cert_hash(req->bio.ctx);
+		if (hash == NULL) {
+			close_with_errf(req, "handshake failed: %s",
+			    tls_error(req->bio.ctx));
 			return;
 		}
-	}
 
-	/*
-	 * Split data into chunks before sending.  imsg can't handle
-	 * message that are "too big".
-	 */
-	for (;;) {
-		if ((len = bufferevent_read(bev, buf, sizeof(buf))) == 0)
-			break;
-		net_send_ui(IMSG_BUF, req->id, buf, len);
+		net_send_ui(IMSG_CHECK_CERT, req->id, hash, strlen(hash)+1);
+		return;
 	}
-}
 
-/* called after a write has been done */
-static void
-net_write(struct bufferevent *bev, void *d)
-{
-	struct evbuffer	*dst = EVBUFFER_OUTPUT(bev);
-
-	if (EVBUFFER_LENGTH(dst) == 0)
-		(*bev->errorcb)(bev, EVBUFFER_WRITE, bev->cbarg);
-}
-
-static void
-net_error(struct bufferevent *bev, short error, void *d)
-{
-	struct req	*req = d;
-	struct evbuffer	*src;
-
-	if (error & EVBUFFER_TIMEOUT) {
-		close_with_err(req, "Timeout loading page");
-		return;
+	if (ev & EV_READ) {
+		read = bufio_read(&req->bio);
+		if (read == -1 && errno != EAGAIN) {			
+			close_with_errf(req, "Read error");
+			return;
+		}
+		if (read == 0)
+			req->eof = 1;
 	}
 
-	if (error & EVBUFFER_ERROR) {
-		close_with_errf(req, "%s error (0x%x)",
-		    (error & EVBUFFER_READ) ? "read" : "write", error);
+	if ((ev & EV_WRITE) && bufio_write(&req->bio) == -1 &&
+	    errno != EAGAIN) {
+		close_with_errf(req, "bufio_write: %s", strerror(errno));
 		return;
 	}
 
-	if (error & EVBUFFER_EOF) {
-		/* EOF and no header */
-		if (!req->done_header) {
-			close_with_err(req, "protocol error");
+	if (req->state == CONN_HEADER) {
+		header = req->bio.rbuf.buf;
+		endl = memmem(header, req->bio.rbuf.len, "\r\n", 2);
+		if (endl == NULL && req->bio.rbuf.len >= 1024) {
+			close_with_err(req, "Invalid gemini reply (too long)");
 			return;
 		}
-
-		src = EVBUFFER_INPUT(req->bev);
-		if (EVBUFFER_LENGTH(src) != 0)
-			net_send_ui(IMSG_BUF, req->id, EVBUFFER_DATA(src),
-			    EVBUFFER_LENGTH(src));
-		net_send_ui(IMSG_EOF, req->id, NULL, 0);
-		close_conn(0, 0, req);
+		if (endl == NULL && req->eof) {
+			close_with_err(req, "Invalid gemini reply.");
+			return;
+		}
+		if (endl == NULL) {
+			ev_add(req->fd, bufio_ev(&req->bio), net_ev, req);
+			return;
+		}
+		*endl = '\0';
+		req->state = CONN_BODY;
+		r = gemini_parse_reply(req, header, strlen(header));
+		buf_drain(&req->bio.rbuf, endl - header + 2);
+		if (r == 0) {
+			close_with_err(req, "Malformed gemini reply");
+			return;
+		}
+		if (r < 20 || r >= 30)
+			close_conn(0, 0, req);
 		return;
 	}
-
-	if (error & EVBUFFER_WRITE) {
-		/* finished sending request */
-		bufferevent_disable(bev, EV_WRITE);
-		return;
+	
+	/*
+	 * Split data into chunks before sending.  imsg can't handle
+	 * message that are "too big".
+	 */
+	for (;;) {
+		if ((len = bufio_drain(&req->bio, buf, sizeof(buf))) == 0)
+			break;
+		net_send_ui(IMSG_BUF, req->id, buf, len);
 	}
 
-	if (error & EVBUFFER_READ) {
-		close_with_err(req, "protocol error");
-		return;
+	if (req->eof) {
+		net_send_ui(IMSG_EOF, req->id, NULL, 0);
+		close_conn(0, 0, req);
 	}
 
-	close_with_errf(req, "unknown event error %x", error);
+	ev_add(req->fd, bufio_ev(&req->bio), net_ev, req);
 }
 
 static int
@@ -702,7 +530,7 @@ load_cert(struct imsg *imsg, struct req *req)
 }
 
 static void
-handle_dispatch_imsg(int fd, short event, void *d)
+handle_dispatch_imsg(int fd, int event, void *d)
 {
 	struct imsgev	*iev = d;
 	struct imsgbuf	*ibuf = &iev->ibuf;
@@ -757,9 +585,10 @@ handle_dispatch_imsg(int fd, short event, void *d)
 				die();
 			if (load_cert(&imsg, req) == -1)
 				die();
+			if (bufio_init(&req->bio) == -1)
+				die();
 
 			req->len = strlen(req->req);
-
 			req->proto = r.proto;
 			conn_towards(req);
 			break;
@@ -771,16 +600,28 @@ handle_dispatch_imsg(int fd, short event, void *d)
 			if (imsg_get_data(&imsg, &certok, sizeof(certok)) ==
 			    -1)
 				die();
-			if (certok)
-				net_ready(req);
-			else
+			if (!certok) {
 				close_conn(0, 0, req);
+				break;
+			}
+
+			if (net_send_req(req) == -1) {
+				close_with_err(req, "failed to send request");
+				break;
+			}
+
+			if (ev_add(req->fd, EV_WRITE, net_ev, req) == -1) {
+				close_with_err(req,
+				    "failed to register event.");
+				break;
+			}
 			break;
 
 		case IMSG_PROCEED:
 			if ((req = req_by_id(imsg_get_id(&imsg))) == NULL)
 				break;
-			bufferevent_enable(req->bev, EV_READ);
+			ev_add(req->fd, EV_READ, net_ev, req);
+			net_ev(req->fd, 0, req);
 			break;
 
 		case IMSG_STOP:
@@ -790,7 +631,7 @@ handle_dispatch_imsg(int fd, short event, void *d)
 			break;
 
 		case IMSG_QUIT:
-			event_loopbreak();
+			ev_break();
 			imsg_free(&imsg);
 			return;
 
@@ -819,7 +660,8 @@ net_main(void)
 
 	TAILQ_INIT(&reqhead);
 
-	event_init();
+	if (ev_init() == -1)
+		exit(1);
 
 	/* Setup pipe and event handler to the main process */
 	if ((iev_ui = malloc(sizeof(*iev_ui))) == NULL)
@@ -827,13 +669,11 @@ net_main(void)
 	imsg_init(&iev_ui->ibuf, 3);
 	iev_ui->handler = handle_dispatch_imsg;
 	iev_ui->events = EV_READ;
-	event_set(&iev_ui->ev, iev_ui->ibuf.fd, iev_ui->events,
-	    iev_ui->handler, iev_ui);
-	event_add(&iev_ui->ev, NULL);
+	ev_add(iev_ui->ibuf.fd, iev_ui->events, iev_ui->handler, iev_ui);
 
 	sandbox_net_process();
 
-	event_dispatch();
+	ev_loop();
 
 	msgbuf_clear(&iev_ui->ibuf.w);
 	close(iev_ui->ibuf.fd);
blob - 00ec289712d0468169ede9e25727384fb46c6299
blob + ea916a32f1698678ee10b0001f14d2568abd59e2
--- session.c
+++ session.c
@@ -16,6 +16,8 @@
 
 #include "compat.h"
 
+#include <sys/time.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -26,6 +28,7 @@
 #include <unistd.h>
 
 #include "defaults.h"
+#include "ev.h"
 #include "fs.h"
 #include "hist.h"
 #include "minibuffer.h"
@@ -35,7 +38,7 @@
 
 struct history	history;
 
-static struct event	 autosaveev;
+static unsigned int	 autosavetimer;
 
 void
 switch_to_tab(struct tab *tab)
@@ -65,19 +68,18 @@ new_tab(const char *url, const char *base, struct tab 
 	autosave_hook();
 
 	if ((tab = calloc(1, sizeof(*tab))) == NULL) {
-		event_loopbreak();
+		ev_break();
 		return NULL;
 	}
 
 	if ((tab->hist = hist_new(HIST_LINEAR)) == NULL) {
 		free(tab);
-		event_loopbreak();
+		ev_break();
 		return NULL;
 	}
 
 	TAILQ_INIT(&tab->buffer.head);
 	TAILQ_INIT(&tab->buffer.page.head);
-	evtimer_set(&tab->loadingev, NULL, NULL);
 
 	tab->id = tab_new_id();
 
@@ -111,8 +113,7 @@ kill_tab(struct tab *tab, int append)
 	ui_schedule_redraw();
 	autosave_hook();
 
-	if (evtimer_pending(&tab->loadingev, NULL))
-		evtimer_del(&tab->loadingev);
+	ev_timer_cancel(tab->loading_timer);
 
 	if (append)
 		TAILQ_INSERT_TAIL(&ktabshead, tab, tabs);
@@ -406,11 +407,11 @@ history_add(const char *uri)
 void
 autosave_init(void)
 {
-	evtimer_set(&autosaveev, autosave_timer, NULL);
+	return;
 }
 
 void
-autosave_timer(int fd, short event, void *data)
+autosave_timer(int fd, int event, void *data)
 {
 	save_session();
 }
@@ -427,11 +428,11 @@ autosave_hook(void)
 	if (autosave <= 0)
 		return;
 
-	if (!evtimer_pending(&autosaveev, NULL)) {
+	if (!ev_timer_pending(autosavetimer)) {
 		tv.tv_sec = autosave;
 		tv.tv_usec = 0;
 
-		evtimer_add(&autosaveev, &tv);
+		autosavetimer = ev_timer(&tv, autosave_timer, NULL);
 	}
 }
 
blob - bf1ec4d751e38ebc8a2188849d6202fd717c1a79
blob + 52ea5baad8d6c7c51f1126311fa7267a8137b001
--- session.h
+++ session.h
@@ -67,7 +67,7 @@ void		 history_sort(void);
 void		 history_add(const char *);
 
 void		 autosave_init(void);
-void		 autosave_timer(int, short, void *);
+void		 autosave_timer(int, int, void *);
 void		 autosave_hook(void);
 
 int		 load_session(struct ohash *);
blob - ef2f9c40c819f99968ab181beedb609c3e3a7a84
blob + 4e71ecbaff97f7964d344324aed229fd8d1cf793
--- telescope.c
+++ telescope.c
@@ -17,6 +17,7 @@
 #include "compat.h"
 
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 
@@ -34,6 +35,7 @@
 #include "cmd.h"
 #include "control.h"
 #include "defaults.h"
+#include "ev.h"
 #include "fs.h"
 #include "hist.h"
 #include "iri.h"
@@ -119,7 +121,7 @@ static void		 handle_check_cert_user_choice(int, struc
 static void		 handle_maybe_save_new_cert(int, struct tab *);
 static void		 handle_maybe_save_page(int, struct tab *);
 static void		 handle_save_page_path(const char *, struct tab *);
-static void		 handle_dispatch_imsg(int, short, void *);
+static void		 handle_dispatch_imsg(int, int, void *);
 static void		 load_about_url(struct tab *, const char *);
 static void		 load_file_url(struct tab *, const char *);
 static void		 load_finger_url(struct tab *, const char *);
@@ -435,7 +437,7 @@ handle_save_page_path(const char *path, struct tab *ta
 }
 
 static void
-handle_dispatch_imsg(int fd, short event, void *data)
+handle_dispatch_imsg(int fd, int event, void *data)
 {
 	struct imsgev	*iev = data;
 	struct imsgbuf	*imsgbuf = &iev->ibuf;
@@ -844,7 +846,7 @@ load_url(struct tab *tab, const char *url, const char 
 
 	if (dohist) {
 		if (hist_push(tab->hist, url) == -1) {
-			event_loopbreak();
+			ev_break();
 			return;
 		}
 
@@ -1165,7 +1167,8 @@ main(int argc, char * const *argv)
 	/* initialize tofu store */
 	tofu_init(&certs, 5, offsetof(struct tofu_entry, domain));
 
-	event_init();
+	if (ev_init() == -1)
+		err(1, "ev_init");
 
 	if (!safe_mode) {
 		if ((control_fd = control_init(ctlsock_path)) == -1)
@@ -1181,9 +1184,7 @@ main(int argc, char * const *argv)
 
 	/* Setup event handlers for pipes to net */
 	iev_net->events = EV_READ;
-	event_set(&iev_net->ev, iev_net->ibuf.fd, iev_net->events,
-	    iev_net->handler, iev_net);
-	event_add(&iev_net->ev, NULL);
+	ev_add(iev_net->ibuf.fd, iev_net->events, iev_net->handler, iev_net);
 
 	if (ui_init()) {
 		sandbox_ui_process();
blob - cf1297fdfadd033ea352e0e23b27b1d18d0c8292
blob + 80534e7a35a0ef841f9140001c72bf13ca683226
--- telescope.h
+++ telescope.h
@@ -33,8 +33,7 @@
 
 struct imsgev {
 	struct imsgbuf	 ibuf;
-	void		(*handler)(int, short, void *);
-	struct event	 ev;
+	void		(*handler)(int, int, void *);
 	short		 events;
 };
 
@@ -206,7 +205,7 @@ struct tab {
 
 	short			 loading_anim;
 	short			 loading_anim_step;
-	struct event		 loadingev;
+	unsigned long		 loading_timer;
 };
 
 extern TAILQ_HEAD(proxylist, proxy) proxies;
blob - c80797e2f3b87797fb9e8964e4826553f82d7192
blob + 67514fe337cc20f21f8eb58d02e1c55d9912bdbf
--- ui.c
+++ ui.c
@@ -32,6 +32,8 @@
 
 #include "compat.h"
 
+#include <sys/time.h>
+
 #include <assert.h>
 #include <curses.h>
 #include <locale.h>
@@ -43,6 +45,7 @@
 
 #include "cmd.h"
 #include "defaults.h"
+#include "ev.h"
 #include "hist.h"
 #include "keymap.h"
 #include "minibuffer.h"
@@ -51,17 +54,15 @@
 #include "ui.h"
 #include "utf8.h"
 
-static struct event	stdioev, winchev;
-
 static void		 set_scroll_position(struct tab *, size_t, size_t);
 
 static void		 restore_curs_x(struct buffer *);
 
 static int		 readkey(void);
-static void		 dispatch_stdio(int, short, void*);
-static void		 handle_resize(int, short, void*);
-static void		 handle_resize_nodelay(int, short, void*);
-static void		 handle_download_refresh(int, short, void *);
+static void		 dispatch_stdio(int, int, void*);
+static void		 handle_resize(int, int, void*);
+static void		 handle_resize_nodelay(int, int, void*);
+static void		 handle_download_refresh(int, int, void *);
 static void		 rearrange_windows(void);
 static void		 line_prefix_and_text(struct vline *, char *, size_t, const char **, const char **, int *);
 static void		 print_vline(int, int, WINDOW*, struct vline*);
@@ -77,7 +78,7 @@ static void		 do_redraw_minibuffer(void);
 static void		 do_redraw_minibuffer_compl(void);
 static void		 place_cursor(int);
 static void		 redraw_tab(struct tab*);
-static void		 update_loading_anim(int, short, void*);
+static void		 update_loading_anim(int, int, void*);
 static void		 stop_loading_anim(struct tab*);
 
 static int		 should_rearrange_windows;
@@ -88,10 +89,10 @@ static int		 x_offset;
 struct thiskey	 thiskey;
 struct tab	*current_tab;
 
-static struct event	resizeev;
-static struct timeval	resize_timer = { 0, 250000 };
+static unsigned int	resize_timer;
+static struct timeval	resize_tv = { 0, 250000 };
 
-static struct event	download_refreshev;
+static unsigned int	download_timer;
 static struct timeval	download_refresh_timer = { 0, 250000 };
 
 static WINDOW	*tabline, *body, *modeline, *echoarea, *minibuffer;
@@ -112,7 +113,7 @@ int			 download_cols;
 static int		 side_window;
 static int		 in_side_window;
 
-static struct timeval	loadingev_timer = { 0, 250000 };
+static struct timeval	loading_tv = { 0, 250000 };
 
 static char	keybuf[64];
 
@@ -296,7 +297,7 @@ readkey(void)
 }
 
 static void
-dispatch_stdio(int fd, short ev, void *d)
+dispatch_stdio(int fd, int ev, void *d)
 {
 	int		 lk;
 	const char	*keyname;
@@ -344,17 +345,14 @@ dispatch_stdio(int fd, short ev, void *d)
 }
 
 static void
-handle_resize(int sig, short ev, void *d)
+handle_resize(int sig, int ev, void *d)
 {
-	if (event_pending(&resizeev, EV_TIMEOUT, NULL)) {
-		event_del(&resizeev);
-	}
-	evtimer_set(&resizeev, handle_resize_nodelay, NULL);
-	evtimer_add(&resizeev, &resize_timer);
+	ev_timer_cancel(resize_timer);
+	resize_timer = ev_timer(&resize_tv, handle_resize_nodelay, NULL);
 }
 
 static void
-handle_resize_nodelay(int s, short ev, void *d)
+handle_resize_nodelay(int s, int ev, void *d)
 {
 	endwin();
 	refresh();
@@ -364,7 +362,7 @@ handle_resize_nodelay(int s, short ev, void *d)
 }
 
 static void
-handle_download_refresh(int s, short v, void *d)
+handle_download_refresh(int s, int v, void *d)
 {
 	if (side_window & SIDE_WINDOW_BOTTOM) {
 		recompute_downloads();
@@ -1055,12 +1053,13 @@ start_loading_anim(struct tab *tab)
 	if (tab->loading_anim)
 		return;
 	tab->loading_anim = 1;
-	evtimer_set(&tab->loadingev, update_loading_anim, tab);
-	evtimer_add(&tab->loadingev, &loadingev_timer);
+
+	ev_timer_cancel(tab->loading_timer);
+	tab->loading_timer = ev_timer(&loading_tv, update_loading_anim, tab);
 }
 
 static void
-update_loading_anim(int fd, short ev, void *d)
+update_loading_anim(int fd, int ev, void *d)
 {
 	struct tab	*tab = d;
 
@@ -1074,7 +1073,7 @@ update_loading_anim(int fd, short ev, void *d)
 			wrefresh(echoarea);
 	}
 
-	evtimer_add(&tab->loadingev, &loadingev_timer);
+	tab->loading_timer = ev_timer(&loading_tv, update_loading_anim, tab);
 }
 
 static void
@@ -1082,7 +1081,8 @@ stop_loading_anim(struct tab *tab)
 {
 	if (!tab->loading_anim)
 		return;
-	evtimer_del(&tab->loadingev);
+
+	ev_timer_cancel(tab->loading_timer);
 	tab->loading_anim = 0;
 	tab->loading_anim_step = 0;
 
@@ -1173,19 +1173,14 @@ ui_init(void)
 void
 ui_main_loop(void)
 {
-	evtimer_set(&resizeev, handle_resize, NULL);
-	evtimer_set(&download_refreshev, handle_download_refresh, NULL);
+	if (ev_signal(SIGWINCH, handle_resize, NULL) == -1 ||
+	    ev_add(0, EV_READ, dispatch_stdio, NULL) == -1)
+		err(1, "ev_signal or ev_add failed");
 
-	event_set(&stdioev, 0, EV_READ | EV_PERSIST, dispatch_stdio, NULL);
-	event_add(&stdioev, NULL);
-
-	signal_set(&winchev, SIGWINCH, handle_resize, NULL);
-	signal_add(&winchev, NULL);
-
 	switch_to_tab(current_tab);
 	rearrange_windows();
 
-	event_dispatch();
+	ev_loop();
 }
 
 void
@@ -1224,11 +1219,11 @@ ui_on_tab_refresh(struct tab *tab)
 void
 ui_on_download_refresh(void)
 {
-	if (event_pending(&download_refreshev, EV_TIMEOUT, NULL))
+	if (ev_timer_pending(download_timer))
 		return;
 
-	evtimer_set(&download_refreshev, handle_download_refresh, NULL);
-	evtimer_add(&download_refreshev, &download_refresh_timer);
+	download_timer = ev_timer(&download_refresh_timer,
+	    handle_download_refresh, NULL);
 }
 
 void
blob - fc40369b423e13aa044a9089bfec117e2354b22c
blob + 43ca40e36f48cabe9b52c27e38cf142dff07ccd9
--- utils.c
+++ utils.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "ev.h"
 #include "telescope.h"
 #include "utils.h"
 
@@ -77,9 +78,7 @@ imsg_event_add(struct imsgev *iev)
 	if (iev->ibuf.w.queued)
 		iev->events |= EV_WRITE;
 
-	event_del(&iev->ev);
-	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
-	event_add(&iev->ev, NULL);
+	ev_add(iev->ibuf.fd, iev->events, iev->handler, iev);
 }
 
 int