Index: Makefile.am
===================================================================
--- Makefile.am	(revision ce35060c0b8f538bfc2659317d8bf70e8a2adbf7)
+++ Makefile.am	(revision fafb8421a08255122a16854bec2a116e7f6863a0)
@@ -45,5 +45,5 @@
      aim.c buddy.c buddylist.c style.c errqueue.c \
      zbuddylist.c popexec.c select.c wcwidth.c \
-     glib_compat.c mainpanel.c msgwin.c sepbar.c editcontext.c
+     glib_compat.c mainpanel.c msgwin.c sepbar.c editcontext.c signal.c
 
 NORMAL_SRCS = filterproc.c window.c windowcb.c
Index: aim.c
===================================================================
--- aim.c	(revision 3472845ecdabe9cd1549dd2b2355ca745059ea36)
+++ aim.c	(revision 0af5f9d4d08591a0bd37f2e9eb8bcddf2d62cd8f)
@@ -446,7 +446,6 @@
 }
 
-int owl_aim_process_events(void)
-{
-  aim_session_t *aimsess;
+int owl_aim_process_events(aim_session_t *aimsess)
+{
   aim_conn_t *waitingconn = NULL;
   struct timeval tv;
@@ -454,5 +453,4 @@
   struct owlfaim_priv *priv;
 
-  aimsess=owl_global_get_aimsess(&g);
   priv = aimsess->aux_data;
 
@@ -1795,8 +1793,113 @@
 }
 
-void owl_process_aim(void)
-{
-  if (owl_global_is_doaimevents(&g)) {
-    owl_aim_process_events();
-  }
-}
+typedef struct _owl_aim_event_source { /*noproto*/
+  GSource source;
+  aim_session_t *sess;
+  GPtrArray *fds;
+} owl_aim_event_source;
+
+static void truncate_pollfd_list(owl_aim_event_source *event_source, int len)
+{
+  GPollFD *fd;
+  int i;
+  if (len < event_source->fds->len)
+    owl_function_debugmsg("Truncating AIM PollFDs to %d, was %d", len, event_source->fds->len);
+  for (i = len; i < event_source->fds->len; i++) {
+    fd = event_source->fds->pdata[i];
+    g_source_remove_poll(&event_source->source, fd);
+    g_free(fd);
+  }
+  g_ptr_array_remove_range(event_source->fds, len, event_source->fds->len - len);
+}
+
+static gboolean owl_aim_event_source_prepare(GSource *source, int *timeout)
+{
+  owl_aim_event_source *event_source = (owl_aim_event_source*)source;
+  aim_conn_t *cur;
+  GPollFD *fd;
+  int i;
+
+  /* AIM HACK:
+   *
+   *  The problem - I'm not sure where to hook into the owl/faim
+   *  interface to keep track of when the AIM socket(s) open and
+   *  close. In particular, the bosconn thing throws me off. So,
+   *  rather than register particular dispatchers for AIM, I look up
+   *  the relevant FDs and add them to select's watch lists, then
+   *  check for them individually before moving on to the other
+   *  dispatchers. --asedeno
+   */
+  i = 0;
+  for (cur = event_source->sess->connlist; cur; cur = cur->next) {
+    if (cur->fd != -1) {
+      /* Add new GPollFDs as necessary. */
+      if (i == event_source->fds->len) {
+	fd = g_new0(GPollFD, 1);
+	g_ptr_array_add(event_source->fds, fd);
+	g_source_add_poll(source, fd);
+        owl_function_debugmsg("Allocated new AIM PollFD, len = %d", event_source->fds->len);
+      }
+      fd = event_source->fds->pdata[i];
+      fd->fd = cur->fd;
+      fd->events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
+      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
+        /* Yes, we're checking writable sockets here. Without it, AIM
+           login is really slow. */
+	fd->events |= G_IO_OUT;
+      }
+      i++;
+    }
+  }
+  /* If the number of GPollFDs went down, clean up. */
+  truncate_pollfd_list(event_source, i);
+
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean owl_aim_event_source_check(GSource *source)
+{
+  owl_aim_event_source *event_source = (owl_aim_event_source*)source;
+  int i;
+
+  for (i = 0; i < event_source->fds->len; i++) {
+    GPollFD *fd = event_source->fds->pdata[i];
+    if (fd->revents & fd->events)
+      return TRUE;
+  }
+  return FALSE;
+}
+
+static gboolean owl_aim_event_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
+{
+  owl_aim_event_source *event_source = (owl_aim_event_source*)source;
+  owl_aim_process_events(event_source->sess);
+  return TRUE;
+}
+
+static void owl_aim_event_source_finalize(GSource *source)
+{
+  owl_aim_event_source *event_source = (owl_aim_event_source*)source;
+  truncate_pollfd_list(event_source, 0);
+  g_ptr_array_free(event_source->fds, TRUE);
+}
+
+static GSourceFuncs aim_event_funcs = {
+  owl_aim_event_source_prepare,
+  owl_aim_event_source_check,
+  owl_aim_event_source_dispatch,
+  owl_aim_event_source_finalize,
+};
+
+GSource *owl_aim_event_source_new(aim_session_t *sess)
+{
+  GSource *source;
+  owl_aim_event_source *event_source;
+
+  source = g_source_new(&aim_event_funcs, sizeof(owl_aim_event_source));
+  event_source = (owl_aim_event_source *)source;
+  event_source->sess = sess;
+  /* TODO: When we depend on glib 2.22+, use g_ptr_array_new_with_free_func. */
+  event_source->fds = g_ptr_array_new();
+  return source;
+}
Index: configure.ac
===================================================================
--- configure.ac	(revision 4479497945518a0fe5ddc9e5cf08f98bc7990cd9)
+++ configure.ac	(revision e9b037f5d790e44fb6e6434462c7c624bafaedc3)
@@ -115,5 +115,5 @@
 
 dnl Add CFLAGS and LIBS for glib-2.0
-PKG_CHECK_MODULES(GLIB,[glib-2.0 gobject-2.0])
+PKG_CHECK_MODULES(GLIB,[glib-2.0 gobject-2.0 gthread-2.0])
 
 AC_MSG_NOTICE([Adding glib-2.0 CFLAGS ${GLIB_CFLAGS}])
Index: filterproc.c
===================================================================
--- filterproc.c	(revision 8bd190d63b2f2c09ea416e7be7c51d974f56bfaf)
+++ filterproc.c	(revision b9c7424374688ab07ae995e0555be89adf301521)
@@ -17,5 +17,4 @@
   int err = 0;
   struct pollfd fds[2];
-  struct sigaction sig = {.sa_handler = SIG_IGN}, old;
 
   fcntl(rfd, F_SETFL, O_NONBLOCK | fcntl(rfd, F_GETFL));
@@ -27,6 +26,4 @@
   fds[1].events = POLLOUT;
 
-  sigaction(SIGPIPE, &sig, &old);
-  
   while(1) {
     if(out && *out) {
@@ -67,5 +64,4 @@
 
   *in = g_string_free(str, err < 0);
-  sigaction(SIGPIPE, &old, NULL);
   return err;
 }
Index: functions.c
===================================================================
--- functions.c	(revision 4fd211f3a533efcedaf4d0dc7c01e02087ad2e39)
+++ functions.c	(revision 4fd211f3a533efcedaf4d0dc7c01e02087ad2e39)
@@ -2995,13 +2995,9 @@
       i--;
     }
-    owl_function_mask_sigint(NULL);
-    if(owl_global_is_interrupted(&g)) {
-      owl_global_unset_interrupted(&g);
-      owl_function_unmask_sigint(NULL);
+    if (owl_global_take_interrupt(&g)) {
       owl_function_makemsg("Search interrupted!");
       owl_mainwin_redisplay(owl_global_get_mainwin(&g));
       return;
     }
-    owl_function_unmask_sigint(NULL);
   }
   owl_mainwin_redisplay(owl_global_get_mainwin(&g));
@@ -3086,14 +3082,9 @@
           ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
 
-          owl_function_mask_sigint(NULL);
-          if(owl_global_is_interrupted(&g)) {
+	  if (owl_global_take_interrupt(&g)) {
             interrupted = 1;
-            owl_global_unset_interrupted(&g);
-            owl_function_unmask_sigint(NULL);
             owl_function_makemsg("Interrupted!");
             break;
           }
-
-          owl_function_unmask_sigint(NULL);
 
           if (ret!=ZERR_NONE) {
@@ -3500,20 +3491,4 @@
 }
 
-void owl_function_mask_sigint(sigset_t *oldmask) {
-  sigset_t intr;
-
-  sigemptyset(&intr);
-  sigaddset(&intr, SIGINT);
-  sigprocmask(SIG_BLOCK, &intr, oldmask);
-}
-
-void owl_function_unmask_sigint(sigset_t *oldmask) {
-  sigset_t intr;
-
-  sigemptyset(&intr);
-  sigaddset(&intr, SIGINT);
-  sigprocmask(SIG_UNBLOCK, &intr, oldmask);
-}
-
 void _owl_function_mark_message(const owl_message *m)
 {
Index: global.c
===================================================================
--- global.c	(revision 47e0a6a50d1c08ec05030b2507b3f1a84b618a9f)
+++ global.c	(revision f578d1880dfdaae710755e0db5c3101e89cb0440)
@@ -16,4 +16,7 @@
 
   g_type_init();
+  g_thread_init(NULL);
+
+  owl_select_init();
 
   g->lines=LINES;
@@ -99,5 +102,4 @@
 
   owl_errqueue_init(&(g->errqueue));
-  g->got_err_signal=0;
 
   owl_zbuddylist_create(&(g->zbuddies));
@@ -108,8 +110,9 @@
   owl_message_init_fmtext_cache();
   owl_list_create(&(g->io_dispatch_list));
-  owl_list_create(&(g->psa_list));
   g->timerlist = NULL;
-  g->interrupted = FALSE;
   g->kill_buffer = NULL;
+
+  g->interrupt_count = 0;
+  g->interrupt_lock = g_mutex_new();
 }
 
@@ -348,5 +351,5 @@
 
 void owl_global_set_resize_pending(owl_global *g) {
-  g->resizepending=1;
+  g->resizepending = true;
 }
 
@@ -448,5 +451,5 @@
   /* resize the screen.  If lines or cols is 0 use the terminal size */
   if (!g->resizepending) return;
-  g->resizepending = 0;
+  g->resizepending = false;
 
   owl_global_get_terminal_size(&g->lines, &g->cols);
@@ -693,18 +696,24 @@
 }
 
-int owl_global_is_doaimevents(const owl_global *g)
-{
-  if (g->aim_doprocessing) return(1);
-  return(0);
+bool owl_global_is_doaimevents(const owl_global *g)
+{
+  return g->aim_event_source != NULL;
 }
 
 void owl_global_set_doaimevents(owl_global *g)
 {
-  g->aim_doprocessing=1;
+  if (g->aim_event_source)
+    return;
+  g->aim_event_source = owl_aim_event_source_new(owl_global_get_aimsess(g));
+  g_source_attach(g->aim_event_source, NULL);
 }
 
 void owl_global_set_no_doaimevents(owl_global *g)
 {
-  g->aim_doprocessing=0;
+  if (!g->aim_event_source)
+    return;
+  g_source_destroy(g->aim_event_source);
+  g_source_unref(g->aim_event_source);
+  g->aim_event_source = NULL;
 }
 
@@ -824,28 +833,4 @@
 }
 
-void owl_global_set_errsignal(owl_global *g, int signum, siginfo_t *siginfo)
-{
-  g->got_err_signal = signum;
-  if (siginfo) {
-    g->err_signal_info = *siginfo;
-  } else {
-    siginfo_t si;
-    memset(&si, 0, sizeof(si));
-    g->err_signal_info = si;
-  }
-}
-
-int owl_global_get_errsignal_and_clear(owl_global *g, siginfo_t *siginfo)
-{
-  int signum;
-  if (siginfo && g->got_err_signal) {
-    *siginfo = g->err_signal_info;
-  } 
-  signum = g->got_err_signal;
-  g->got_err_signal = 0;
-  return signum;
-}
-
-
 owl_zbuddylist *owl_global_get_zephyr_buddylist(owl_global *g)
 {
@@ -878,24 +863,7 @@
 }
 
-owl_list *owl_global_get_psa_list(owl_global *g)
-{
-  return &(g->psa_list);
-}
-
 GList **owl_global_get_timerlist(owl_global *g)
 {
   return &(g->timerlist);
-}
-
-int owl_global_is_interrupted(const owl_global *g) {
-  return g->interrupted;
-}
-
-void owl_global_set_interrupted(owl_global *g) {
-  g->interrupted = 1;
-}
-
-void owl_global_unset_interrupted(owl_global *g) {
-  g->interrupted = 0;
 }
 
@@ -965,2 +933,21 @@
   g->kill_buffer = g_strndup(kill, len);
 }
+
+void owl_global_add_interrupt(owl_global *g) {
+  /* TODO: This can almost certainly be done with atomic
+   * operations. Whatever. */
+  g_mutex_lock(g->interrupt_lock);
+  g->interrupt_count++;
+  g_mutex_unlock(g->interrupt_lock);
+}
+
+bool owl_global_take_interrupt(owl_global *g) {
+  bool ans = false;
+  g_mutex_lock(g->interrupt_lock);
+  if (g->interrupt_count > 0) {
+    ans = true;
+    g->interrupt_count--;
+  }
+  g_mutex_unlock(g->interrupt_lock);
+  return ans;
+}
Index: owl.c
===================================================================
--- owl.c	(revision a2a883313ecb42f7eb3fc95a46c710acb4a171ea)
+++ owl.c	(revision 25f7c57210f499abb6780f5a5402c588fc6003eb)
@@ -160,5 +160,5 @@
  * was ignored due to user settings or otherwise.
  */
-int owl_process_message(owl_message *m) {
+static int owl_process_message(owl_message *m) {
   const owl_filter *f;
   /* if this message it on the puntlist, nuke it and continue */
@@ -245,10 +245,17 @@
 }
 
+static gboolean owl_process_messages_prepare(GSource *source, int *timeout) {
+  *timeout = -1;
+  return owl_global_messagequeue_pending(&g);
+}
+
+static gboolean owl_process_messages_check(GSource *source) {
+  return owl_global_messagequeue_pending(&g);
+}
+
 /*
  * Process any new messages we have waiting in the message queue.
- * Returns 1 if any messages were added to the message list, and 0 otherwise.
  */
-int owl_process_messages(owl_ps_action *d, void *p)
-{
+static gboolean owl_process_messages_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
   int newmsgs=0;
   int followlast = owl_global_should_followlast(&g);
@@ -274,5 +281,23 @@
     owl_mainwin_redisplay(owl_global_get_mainwin(&g));
   }
-  return newmsgs;
+  return TRUE;
+}
+
+static GSourceFuncs owl_process_messages_funcs = {
+  owl_process_messages_prepare,
+  owl_process_messages_check,
+  owl_process_messages_dispatch,
+  NULL
+};
+
+void owl_process_input_char(owl_input j)
+{
+  int ret;
+
+  owl_global_set_lastinputtime(&g, time(NULL));
+  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
+  if (ret!=0 && ret!=1) {
+    owl_function_makemsg("Unable to handle keypress");
+  }
 }
 
@@ -334,53 +359,68 @@
 }
 
-void sig_handler(int sig, siginfo_t *si, void *data)
-{
-  if (sig==SIGWINCH) {
-    /* we can't inturrupt a malloc here, so it just sets a flag
-     * schedulding a resize for later
-     */
+static void sig_handler_main_thread(void *data) {
+  int sig = GPOINTER_TO_INT(data);
+
+  owl_function_debugmsg("Got signal %d", sig);
+  if (sig == SIGWINCH) {
     owl_function_resize();
-  } else if (sig==SIGPIPE || sig==SIGCHLD) {
-    /* Set a flag and some info that we got the sigpipe
-     * so we can record that we got it and why... */
-    owl_global_set_errsignal(&g, sig, si);
-  } else if (sig==SIGTERM || sig==SIGHUP) {
+  } else if (sig == SIGTERM || sig == SIGHUP) {
     owl_function_quit();
-  }
-}
-
-void sigint_handler(int sig, siginfo_t *si, void *data)
-{
-  owl_global_set_interrupted(&g);
-}
-
-static int owl_errsignal_pre_select_action(owl_ps_action *a, void *data)
-{
-  siginfo_t si;
-  int signum;
-  if ((signum = owl_global_get_errsignal_and_clear(&g, &si)) > 0) {
-    owl_function_error("Got unexpected signal: %d %s  (code: %d band: %ld  errno: %d)",
-        signum, signum==SIGPIPE?"SIGPIPE":"SIG????",
-        si.si_code, si.si_band, si.si_errno);
-  }
-  return 0;
-}
+  } else if (sig == SIGINT && owl_global_take_interrupt(&g)) {
+    owl_input in;
+    in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
+    owl_process_input_char(in);
+  }
+}
+
+static void sig_handler(const siginfo_t *siginfo, void *data) {
+  /* If it was an interrupt, set a flag so we can handle it earlier if
+   * needbe. sig_handler_main_thread will check the flag to make sure
+   * no one else took it. */
+  if (siginfo->si_signo == SIGINT) {
+    owl_global_add_interrupt(&g);
+  }
+  /* Send a message to the main thread. */
+  owl_select_post_task(sig_handler_main_thread,
+                       GINT_TO_POINTER(siginfo->si_signo), NULL);
+}
+
+#define CHECK_RESULT(s, syscall) \
+  G_STMT_START {		 \
+    if ((syscall) != 0) {	 \
+      perror((s));		 \
+      exit(1);			 \
+    }				 \
+  } G_STMT_END
 
 void owl_register_signal_handlers(void) {
-  struct sigaction sigact;
-
-  /* signal handler */
-  /*sigact.sa_handler=sig_handler;*/
-  sigact.sa_sigaction=sig_handler;
-  sigemptyset(&sigact.sa_mask);
-  sigact.sa_flags=SA_SIGINFO;
-  sigaction(SIGWINCH, &sigact, NULL);
-  sigaction(SIGALRM, &sigact, NULL);
-  sigaction(SIGPIPE, &sigact, NULL);
-  sigaction(SIGTERM, &sigact, NULL);
-  sigaction(SIGHUP, &sigact, NULL);
-
-  sigact.sa_sigaction=sigint_handler;
-  sigaction(SIGINT, &sigact, NULL);
+  struct sigaction sig_ignore = { .sa_handler = SIG_IGN };
+  struct sigaction sig_default = { .sa_handler = SIG_DFL };
+  sigset_t sigset;
+  int ret, i;
+  const int signals[] = { SIGABRT, SIGBUS, SIGCHLD, SIGFPE, SIGHUP, SIGILL,
+                          SIGINT, SIGQUIT, SIGSEGV, SIGTERM, SIGWINCH };
+
+  /* Sanitize our signals; the mask and dispositions from our parent
+   * aren't really useful. Signal list taken from equivalent code in
+   * Chromium. */
+  CHECK_RESULT("sigemptyset", sigemptyset(&sigset));
+  if ((ret = pthread_sigmask(SIG_SETMASK, &sigset, NULL)) != 0) {
+    errno = ret;
+    perror("pthread_sigmask");
+  }
+  for (i = 0; i < G_N_ELEMENTS(signals); i++) {
+    CHECK_RESULT("sigaction", sigaction(signals[i], &sig_default, NULL));
+  }
+
+  /* Turn off SIGPIPE; we check the return value of write. */
+  CHECK_RESULT("sigaction", sigaction(SIGPIPE, &sig_ignore, NULL));
+
+  /* Register some signals with the signal thread. */
+  CHECK_RESULT("sigaddset", sigaddset(&sigset, SIGWINCH));
+  CHECK_RESULT("sigaddset", sigaddset(&sigset, SIGTERM));
+  CHECK_RESULT("sigaddset", sigaddset(&sigset, SIGHUP));
+  CHECK_RESULT("sigaddset", sigaddset(&sigset, SIGINT));
+  owl_signal_init(&sigset, sig_handler, NULL);
 }
 
@@ -436,27 +476,4 @@
 #endif /* OWL_STDERR_REDIR */
 
-static int owl_refresh_pre_select_action(owl_ps_action *a, void *data)
-{
-  owl_colorpair_mgr *cpmgr;
-
-  /* if a resize has been scheduled, deal with it */
-  owl_global_check_resize(&g);
-  /* update the terminal if we need to */
-  owl_window_redraw_scheduled();
-  /* On colorpair shortage, reset and redraw /everything/. NOTE: if
-   * the current screen uses too many colorpairs, this draws
-   * everything twice. But this is unlikely; COLOR_PAIRS is 64 with
-   * 8+1 colors, and 256^2 with 256+1 colors. (+1 for default.) */
-  cpmgr = owl_global_get_colorpair_mgr(&g);
-  if (cpmgr->overflow) {
-    owl_function_debugmsg("colorpairs: color shortage; reset pairs and redraw. COLOR_PAIRS = %d", COLOR_PAIRS);
-    owl_fmtext_reset_colorpairs(cpmgr);
-    owl_function_full_redisplay();
-    owl_window_redraw_scheduled();
-  }
-  return 0;
-}
-
-
 int main(int argc, char **argv, char **env)
 {
@@ -467,4 +484,5 @@
   const char *dir;
   owl_options opts;
+  GSource *source;
 
   if (!GLIB_CHECK_VERSION (2, 12, 0))
@@ -481,5 +499,4 @@
   g.load_initial_subs = opts.load_initial_subs;
 
-  owl_register_signal_handlers();
   owl_start_curses();
 
@@ -492,4 +509,6 @@
   g_strfreev(argv_copy);
   owl_global_set_haveaim(&g);
+
+  owl_register_signal_handlers();
 
   /* register STDIN dispatch; throw away return, we won't need it */
@@ -584,7 +603,11 @@
   owl_global_push_context(&g, OWL_CTX_INTERACTIVE|OWL_CTX_RECV, NULL, "recv", NULL);
 
-  owl_select_add_pre_select_action(owl_refresh_pre_select_action, NULL, NULL);
-  owl_select_add_pre_select_action(owl_process_messages, NULL, NULL);
-  owl_select_add_pre_select_action(owl_errsignal_pre_select_action, NULL, NULL);
+  source = owl_window_redraw_source_new();
+  g_source_attach(source, NULL);
+  g_source_unref(source);
+
+  source = g_source_new(&owl_process_messages_funcs, sizeof(GSource));
+  g_source_attach(source, NULL);
+  g_source_unref(source);
 
   owl_function_debugmsg("startup: entering main loop");
@@ -593,4 +616,5 @@
   /* Shut down everything. */
   owl_zephyr_shutdown();
+  owl_signal_shutdown();
   owl_shutdown_curses();
   return 0;
Index: owl.h
===================================================================
--- owl.h	(revision 283ff1e64b51f85106ca81c14d56df397f503009)
+++ owl.h	(revision f578d1880dfdaae710755e0db5c3101e89cb0440)
@@ -550,12 +550,6 @@
   void (*destroy)(const struct _owl_io_dispatch *);  /* Destructor */
   void *data;
+  GPollFD pollfd;
 } owl_io_dispatch;
-
-typedef struct _owl_ps_action {
-  int needs_gc;
-  int (*callback)(struct _owl_ps_action *, void *);
-  void (*destroy)(struct _owl_ps_action *);
-  void *data;
-} owl_ps_action;
 
 typedef struct _owl_popexec {
@@ -566,6 +560,4 @@
   const owl_io_dispatch *dispatch;
 } owl_popexec;
-
-typedef struct _OwlGlobalNotifier OwlGlobalNotifier;
 
 typedef struct _owl_global {
@@ -593,5 +585,5 @@
   gulong typwin_erase_id;
   int rightshift;
-  volatile sig_atomic_t resizepending;
+  bool resizepending;
   char *homedir;
   char *confdir;
@@ -618,5 +610,5 @@
   aim_conn_t bosconn;
   int aim_loggedin;         /* true if currently logged into AIM */
-  int aim_doprocessing;     /* true if we should process AIM events (like pending login) */
+  GSource *aim_event_source; /* where we get our AIM events from */
   char *aim_screenname;     /* currently logged in AIM screen name */
   char *aim_screenname_for_filters;     /* currently logged in AIM screen name */
@@ -628,6 +620,4 @@
   int haveaim;
   int ignoreaimlogin;
-  volatile sig_atomic_t got_err_signal; /* 1 if we got an unexpected signal */
-  volatile siginfo_t err_signal_info;
   owl_zbuddylist zbuddies;
   GList *zaldlist;
@@ -635,11 +625,11 @@
   struct termios startup_tio;
   owl_list io_dispatch_list;
-  owl_list psa_list;
   GList *timerlist;
   owl_timer *aim_nop_timer;
   int load_initial_subs;
-  volatile sig_atomic_t interrupted;
   FILE *debug_file;
   char *kill_buffer;
+  int interrupt_count;
+  GMutex *interrupt_lock;
 } owl_global;
 
Index: select.c
===================================================================
--- select.c	(revision d4927a70549407fe7f56b4ae1d3949663a370205)
+++ select.c	(revision 111850cedcd0ca74df6c84fffa5cd1d939c1f5bc)
@@ -1,14 +1,99 @@
 #include "owl.h"
 
+static GMainLoop *loop = NULL;
+static GMainContext *context;
 static int dispatch_active = 0;
-static int psa_active = 0;
-static int loop_active = 0;
-
-int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
+
+static GSource *owl_timer_source;
+static GSource *owl_io_dispatch_source;
+
+static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2);
+static void owl_select_io_dispatch_gc(void);
+
+static gboolean owl_timer_prepare(GSource *source, int *timeout) {
+  GList **timers = owl_global_get_timerlist(&g);
+  GTimeVal now;
+
+  /* TODO: In the far /far/ future, g_source_get_time is what the cool
+   * kids use to get system monotonic time. */
+  g_source_get_current_time(source, &now);
+
+  /* FIXME: bother with millisecond accuracy now that we can? */
+  if (*timers) {
+    owl_timer *t = (*timers)->data;
+    *timeout = t->time - now.tv_sec;
+    if (*timeout <= 0) {
+      *timeout = 0;
+      return TRUE;
+    }
+    if (*timeout > 60 * 1000)
+      *timeout = 60 * 1000;
+  } else {
+    *timeout = 60 * 1000;
+  }
+  return FALSE;
+}
+
+static gboolean owl_timer_check(GSource *source) {
+  GList **timers = owl_global_get_timerlist(&g);
+  GTimeVal now;
+
+  /* TODO: In the far /far/ future, g_source_get_time is what the cool
+   * kids use to get system monotonic time. */
+  g_source_get_current_time(source, &now);
+
+  /* FIXME: bother with millisecond accuracy now that we can? */
+  if (*timers) {
+    owl_timer *t = (*timers)->data;
+    return t->time >= now.tv_sec;
+  }
+  return FALSE;
+}
+
+
+static gboolean owl_timer_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
+  GList **timers = owl_global_get_timerlist(&g);
+  GTimeVal now;
+
+  /* TODO: In the far /far/ future, g_source_get_time is what the cool
+   * kids use to get system monotonic time. */
+  g_source_get_current_time(source, &now);
+
+  /* FIXME: bother with millisecond accuracy now that we can? */
+  while(*timers) {
+    owl_timer *t = (*timers)->data;
+    int remove = 0;
+
+    if(t->time > now.tv_sec)
+      break;
+
+    /* Reschedule if appropriate */
+    if(t->interval > 0) {
+      t->time = now.tv_sec + t->interval;
+      *timers = g_list_remove(*timers, t);
+      *timers = g_list_insert_sorted(*timers, t,
+                                     (GCompareFunc)_owl_select_timer_cmp);
+    } else {
+      remove = 1;
+    }
+
+    /* Do the callback */
+    t->callback(t, t->data);
+    if(remove) {
+      owl_select_remove_timer(t);
+    }
+  }
+  return TRUE;
+}
+
+static GSourceFuncs owl_timer_funcs = {
+  owl_timer_prepare,
+  owl_timer_check,
+  owl_timer_dispatch,
+  NULL
+};
+
+static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
   return t1->time - t2->time;
-}
-
-int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
-  return t1 == t2;
 }
 
@@ -43,44 +128,49 @@
 }
 
-void owl_select_process_timers(struct timespec *timeout)
-{
-  time_t now = time(NULL);
-  GList **timers = owl_global_get_timerlist(&g);
-
-  while(*timers) {
-    owl_timer *t = (*timers)->data;
-    int remove = 0;
-
-    if(t->time > now)
-      break;
-
-    /* Reschedule if appropriate */
-    if(t->interval > 0) {
-      t->time = now + t->interval;
-      *timers = g_list_remove(*timers, t);
-      *timers = g_list_insert_sorted(*timers, t,
-                                     (GCompareFunc)_owl_select_timer_cmp);
-    } else {
-      remove = 1;
-    }
-
-    /* Do the callback */
-    t->callback(t, t->data);
-    if(remove) {
-      owl_select_remove_timer(t);
-    }
-  }
-
-  if(*timers) {
-    owl_timer *t = (*timers)->data;
-    timeout->tv_sec = t->time - now;
-    if (timeout->tv_sec > 60)
-      timeout->tv_sec = 60;
-  } else {
-    timeout->tv_sec = 60;
-  }
-
-  timeout->tv_nsec = 0;
-}
+
+static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean owl_io_dispatch_check(GSource *source) {
+  int i, len;
+  const owl_list *dl;
+
+  dl = owl_global_get_io_dispatch_list(&g);
+  len = owl_list_get_size(dl);
+  for(i = 0; i < len; i++) {
+    const owl_io_dispatch *d = owl_list_get_element(dl, i);
+    if (d->pollfd.revents & d->pollfd.events)
+      return TRUE;
+  }
+  return FALSE;
+}
+
+static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
+  int i, len;
+  const owl_list *dl;
+  
+  dispatch_active = 1;
+  dl = owl_global_get_io_dispatch_list(&g);
+  len = owl_list_get_size(dl);
+  for (i = 0; i < len; i++) {
+    owl_io_dispatch *d = owl_list_get_element(dl, i);
+    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
+      d->callback(d, d->data);
+    }
+  }
+  dispatch_active = 0;
+  owl_select_io_dispatch_gc();
+
+  return TRUE;
+}
+
+static GSourceFuncs owl_io_dispatch_funcs = {
+  owl_io_dispatch_prepare,
+  owl_io_dispatch_check,
+  owl_io_dispatch_dispatch,
+  NULL
+};
 
 static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
@@ -128,4 +218,5 @@
         if (d->destroy)
           d->destroy(d);
+	g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
         g_free(d);
       }
@@ -134,5 +225,5 @@
 }
 
-void owl_select_io_dispatch_gc(void)
+static void owl_select_io_dispatch_gc(void)
 {
   int i;
@@ -169,48 +260,20 @@
   d->data = data;
 
+  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
+  d->pollfd.fd = fd;
+  d->pollfd.events = 0;
+  if (d->mode & OWL_IO_READ)
+    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
+  if (d->mode & OWL_IO_WRITE)
+    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
+  if (d->mode & OWL_IO_EXCEPT)
+    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
+  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
+
+
   owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
   owl_list_append_element(dl, d);
 
   return d;
-}
-
-int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
-  int i, len, max_fd;
-  owl_io_dispatch *d;
-  owl_list *dl = owl_global_get_io_dispatch_list(&g);
-
-  max_fd = 0;
-  len = owl_list_get_size(dl);
-  for (i = 0; i < len; i++) {
-    d = owl_list_get_element(dl, i);
-    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
-      if (max_fd < d->fd) max_fd = d->fd;
-      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
-      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
-      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
-    }
-  }
-  return max_fd + 1;
-}
-
-void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
-{
-  int i, len;
-  owl_io_dispatch *d;
-  owl_list *dl = owl_global_get_io_dispatch_list(&g);
-
-  dispatch_active = 1;
-  len = owl_list_get_size(dl);
-  for (i = 0; i < len; i++) {
-    d = owl_list_get_element(dl, i);
-    if (d->fd < max_fd && d->callback != NULL &&
-        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
-         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
-         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
-      d->callback(d, d->data);
-    }
-  }
-  dispatch_active = 0;
-  owl_select_io_dispatch_gc();
 }
 
@@ -237,217 +300,60 @@
 }
 
-int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
-{
-  aim_conn_t *cur;
-  aim_session_t *sess;
-  int max_fd;
-
-  max_fd = 0;
-  sess = owl_global_get_aimsess(&g);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->fd != -1) {
-      FD_SET(cur->fd, rfds);
-      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
-        /* Yes, we're checking writable sockets here. Without it, AIM
-           login is really slow. */
-        FD_SET(cur->fd, wfds);
-      }
-      
-      if (cur->fd > max_fd)
-        max_fd = cur->fd;
-    }
-  }
-  return max_fd;
-}
-
-void owl_process_input_char(owl_input j)
-{
-  int ret;
-
-  owl_global_set_lastinputtime(&g, time(NULL));
-  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
-  if (ret!=0 && ret!=1) {
-    owl_function_makemsg("Unable to handle keypress");
-  }
-}
-
-void owl_select_mask_signals(sigset_t *oldmask) {
-  sigset_t set;
-
-  sigemptyset(&set);
-  sigaddset(&set, SIGWINCH);
-  sigaddset(&set, SIGALRM);
-  sigaddset(&set, SIGPIPE);
-  sigaddset(&set, SIGTERM);
-  sigaddset(&set, SIGHUP);
-  sigaddset(&set, SIGINT);
-  sigprocmask(SIG_BLOCK, &set, oldmask);
-}
-
-void owl_select_handle_intr(sigset_t *restore)
-{
-  owl_input in;
-
-  owl_global_unset_interrupted(&g);
-
-  sigprocmask(SIG_SETMASK, restore, NULL);
-
-  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
-  owl_process_input_char(in);
-}
-
-owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
-{
-  owl_ps_action *a = g_new(owl_ps_action, 1);
-  owl_list *psa_list = owl_global_get_psa_list(&g);
-  a->needs_gc = 0;
-  a->callback = cb;
-  a->destroy = destroy;
-  a->data = data;
-  owl_list_append_element(psa_list, a);
-  return a;
-}
-
-void owl_select_psa_gc(void)
-{
-  int i;
-  owl_list *psa_list;
-  owl_ps_action *a;
-
-  psa_list = owl_global_get_psa_list(&g);
-  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
-    a = owl_list_get_element(psa_list, i);
-    if (a->needs_gc) {
-      owl_list_remove_element(psa_list, i);
-      if (a->destroy) {
-        a->destroy(a);
-      }
-      g_free(a);
-    }
-  }
-}
-
-void owl_select_remove_pre_select_action(owl_ps_action *a)
-{
-  a->needs_gc = 1;
-  if (!psa_active)
-    owl_select_psa_gc();
-}
-
-int owl_select_do_pre_select_actions(void)
-{
-  int i, len, ret;
-  owl_list *psa_list;
-
-  psa_active = 1;
-  ret = 0;
-  psa_list = owl_global_get_psa_list(&g);
-  len = owl_list_get_size(psa_list);
-  for (i = 0; i < len; i++) {
-    owl_ps_action *a = owl_list_get_element(psa_list, i);
-    if (a->callback != NULL && a->callback(a, a->data)) {
-      ret = 1;
-    }
-  }
-  psa_active = 0;
-  owl_select_psa_gc();
-  return ret;
-}
-
-void owl_select(void)
-{
-  int i, max_fd, max_fd2, aim_done, ret;
-  fd_set r;
-  fd_set w;
-  fd_set e;
-  fd_set aim_rfds, aim_wfds;
-  struct timespec timeout;
-  sigset_t mask;
-
-  owl_select_process_timers(&timeout);
-
-  owl_select_mask_signals(&mask);
-
-  if(owl_global_is_interrupted(&g)) {
-    owl_select_handle_intr(&mask);
-    return;
-  }
-  FD_ZERO(&r);
-  FD_ZERO(&w);
-  FD_ZERO(&e);
-
-  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
-
-  /* AIM HACK: 
-   *
-   *  The problem - I'm not sure where to hook into the owl/faim
-   *  interface to keep track of when the AIM socket(s) open and
-   *  close. In particular, the bosconn thing throws me off. So,
-   *  rather than register particular dispatchers for AIM, I look up
-   *  the relevant FDs and add them to select's watch lists, then
-   *  check for them individually before moving on to the other
-   *  dispatchers. --asedeno
-   */
-  aim_done = 1;
-  FD_ZERO(&aim_rfds);
-  FD_ZERO(&aim_wfds);
-  if (owl_global_is_doaimevents(&g)) {
-    aim_done = 0;
-    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
-    if (max_fd < max_fd2) max_fd = max_fd2;
-    for(i = 0; i <= max_fd2; i++) {
-      if (FD_ISSET(i, &aim_rfds)) {
-        FD_SET(i, &r);
-        FD_SET(i, &e);
-      }
-      if (FD_ISSET(i, &aim_wfds)) {
-        FD_SET(i, &w);
-        FD_SET(i, &e);
-      }
-    }
-  }
-  /* END AIM HACK */
-
-  if (owl_select_do_pre_select_actions()) {
-    timeout.tv_sec = 0;
-    timeout.tv_nsec = 0;
-  }
-
-  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
-
-  if(ret < 0 && errno == EINTR) {
-    if(owl_global_is_interrupted(&g)) {
-      owl_select_handle_intr(NULL);
-    }
-    sigprocmask(SIG_SETMASK, &mask, NULL);
-    return;
-  }
-
-  sigprocmask(SIG_SETMASK, &mask, NULL);
-
-  if(ret > 0) {
-    /* AIM HACK: process all AIM events at once. */
-    for(i = 0; !aim_done && i <= max_fd; i++) {
-      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
-        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
-          owl_process_aim();
-          aim_done = 1;
-        }
-      }
-    }
-    owl_select_io_dispatch(&r, &w, &e, max_fd);
-  }
+void owl_select_init(void)
+{
+  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
+  g_source_attach(owl_timer_source, NULL);
+
+  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
+  g_source_attach(owl_io_dispatch_source, NULL);
 }
 
 void owl_select_run_loop(void)
 {
-  loop_active = 1;
-  while (loop_active) {
-    owl_select();
-  }
+  context = g_main_context_default();
+  loop = g_main_loop_new(context, FALSE);
+  g_main_loop_run(loop);
 }
 
 void owl_select_quit_loop(void)
 {
-  loop_active = 0;
-}
+  if (loop) {
+    g_main_loop_quit(loop);
+    loop = NULL;
+  }
+}
+
+typedef struct _owl_task { /*noproto*/
+  void (*cb)(void *);
+  void *cbdata;
+  void (*destroy_cbdata)(void *);
+} owl_task;
+
+static gboolean _run_task(gpointer data)
+{
+  owl_task *t = data;
+  if (t->cb)
+    t->cb(t->cbdata);
+  return FALSE;
+}
+
+static void _destroy_task(void *data)
+{
+  owl_task *t = data;
+  if (t->destroy_cbdata)
+    t->destroy_cbdata(t->cbdata);
+  g_free(t);
+}
+
+void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*))
+{
+  GSource *source = g_idle_source_new();
+  owl_task *t = g_new0(owl_task, 1);
+  t->cb = cb;
+  t->cbdata = cbdata;
+  t->destroy_cbdata = destroy_cbdata;
+  g_source_set_priority(source, G_PRIORITY_DEFAULT);
+  g_source_set_callback(source, _run_task, t, _destroy_task);
+  g_source_attach(source, context);
+  g_source_unref(source);
+}
Index: signal.c
===================================================================
--- signal.c	(revision 25f7c57210f499abb6780f5a5402c588fc6003eb)
+++ signal.c	(revision 25f7c57210f499abb6780f5a5402c588fc6003eb)
@@ -0,0 +1,62 @@
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static pthread_t signal_thread;
+static sigset_t signal_set;
+
+static void (*signal_cb)(const siginfo_t*, void*);
+static void *signal_cbdata;
+
+static void *signal_thread_func(void *data);
+
+/* Initializes the signal thread to listen for 'set' on a dedicated
+ * thread. 'callback' is called *on the signal thread* when a signal
+ * is received.
+ *
+ * This function /must/ be called before any other threads are
+ * created. (Otherwise the signals will not get blocked correctly.) */
+void owl_signal_init(const sigset_t *set, void (*callback)(const siginfo_t*, void*), void *data) {
+  int ret;
+
+  signal_set = *set;
+  signal_cb = callback;
+  signal_cbdata = data;
+  /* Block these signals in all threads, so we can get them. */
+  if ((ret = pthread_sigmask(SIG_BLOCK, set, NULL)) != 0) {
+    errno = ret;
+    perror("pthread_sigmask");
+  }
+  /* Spawn a dedicated thread to sigwait. */
+  if ((ret = pthread_create(&signal_thread, NULL,
+                            signal_thread_func, NULL)) != 0) {
+    errno = ret;
+    perror("pthread_create");
+    exit(1);
+  }
+}
+
+static void *signal_thread_func(void *data) {
+  while (1) {
+    siginfo_t siginfo;
+    int ret;
+
+    ret = sigwaitinfo(&signal_set, &siginfo);
+    /* TODO: Print an error? */
+    if (ret < 0)
+      continue;
+
+    signal_cb(&siginfo, signal_cbdata);
+    /* Die on SIGTERM. */
+    if (siginfo.si_signo == SIGTERM)
+      break;
+  }
+  return NULL;
+}
+
+void owl_signal_shutdown(void) {
+  pthread_kill(signal_thread, SIGTERM);
+  pthread_join(signal_thread, NULL);
+}
Index: window.c
===================================================================
--- window.c	(revision b31f1c9929b526b6d90b18b7847a024ff3d0c77d)
+++ window.c	(revision 7b2686d30035a2484dbda0d4f3c7e8b99d6f8527)
@@ -524,2 +524,52 @@
   owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
 }
+
+/** Redrawing main loop hooks **/
+
+static bool _owl_window_should_redraw(void) {
+  return g.resizepending || owl_window_get_screen()->dirty_subtree;
+}
+
+static gboolean _owl_window_redraw_prepare(GSource *source, int *timeout) {
+  *timeout = -1;
+  return _owl_window_should_redraw();
+}
+
+static gboolean _owl_window_redraw_check(GSource *source) {
+  return _owl_window_should_redraw();
+}
+
+static gboolean _owl_window_redraw_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
+  owl_colorpair_mgr *cpmgr;
+
+  /* if a resize has been scheduled, deal with it */
+  owl_global_check_resize(&g);
+  /* update the terminal if we need to */
+  owl_window_redraw_scheduled();
+  /* On colorpair shortage, reset and redraw /everything/. NOTE: if
+   * the current screen uses too many colorpairs, this draws
+   * everything twice. But this is unlikely; COLOR_PAIRS is 64 with
+   * 8+1 colors, and 256^2 with 256+1 colors. (+1 for default.) */
+  cpmgr = owl_global_get_colorpair_mgr(&g);
+  if (cpmgr->overflow) {
+    owl_function_debugmsg("colorpairs: color shortage; reset pairs and redraw. COLOR_PAIRS = %d", COLOR_PAIRS);
+    owl_fmtext_reset_colorpairs(cpmgr);
+    owl_function_full_redisplay();
+    owl_window_redraw_scheduled();
+  }
+  return TRUE;
+}
+
+static GSourceFuncs redraw_funcs = {
+  _owl_window_redraw_prepare,
+  _owl_window_redraw_check,
+  _owl_window_redraw_dispatch,
+  NULL
+};
+
+GSource *owl_window_redraw_source_new(void) {
+  GSource *source;
+  source = g_source_new(&redraw_funcs, sizeof(GSource));
+  /* TODO: priority?? */
+  return source;
+}
Index: window.h
===================================================================
--- window.h	(revision 38e2250d6a6cd2691f017563d2eb56b3304d608a)
+++ window.h	(revision 7b2686d30035a2484dbda0d4f3c7e8b99d6f8527)
@@ -72,4 +72,6 @@
 void owl_window_resize(owl_window *w, int nlines, int ncols);
 
+GSource *owl_window_redraw_source_new(void);
+
 /* Standard callback functions in windowcb.c */
 
Index: zephyr.c
===================================================================
--- zephyr.c	(revision f203cad04d71e968a11be5e72e6c9d805e02010d)
+++ zephyr.c	(revision f203cad04d71e968a11be5e72e6c9d805e02010d)
@@ -7,4 +7,10 @@
 #include "owl.h"
 
+static GSource *owl_zephyr_event_source_new(int fd);
+
+static gboolean owl_zephyr_event_prepare(GSource *source, int *timeout);
+static gboolean owl_zephyr_event_check(GSource *source);
+static gboolean owl_zephyr_event_dispatch(GSource *source, GSourceFunc callback, gpointer user_data);
+
 #ifdef HAVE_LIBZEPHYR
 static GList *deferred_subs = NULL;
@@ -16,4 +22,11 @@
 
 Code_t ZResetAuthentication(void);
+
+static GSourceFuncs zephyr_event_funcs = {
+  owl_zephyr_event_prepare,
+  owl_zephyr_event_check,
+  owl_zephyr_event_dispatch,
+  NULL
+};
 #endif
 
@@ -84,4 +97,5 @@
   Code_t code;
   char *perl;
+  GSource *event_source;
 
   owl_select_remove_io_dispatch(d);
@@ -99,5 +113,7 @@
   }
 
-  owl_select_add_io_dispatch(ZGetFD(), OWL_IO_READ|OWL_IO_EXCEPT, &owl_zephyr_process_events, NULL, NULL);
+  event_source = owl_zephyr_event_source_new(ZGetFD());
+  g_source_attach(event_source, NULL);
+  g_source_unref(event_source);
 
   owl_global_set_havezephyr(&g);
@@ -127,6 +143,4 @@
   perl = owl_perlconfig_execute("BarnOwl::Zephyr::_zephyr_startup()");
   g_free(perl);
-
-  owl_select_add_pre_select_action(owl_zephyr_pre_select_action, NULL, NULL);
 }
 
@@ -180,4 +194,20 @@
     if((code = ZPending()) < 0) {
       owl_function_debugmsg("Error (%s) in ZPending()\n",
+                            error_message(code));
+      return 0;
+    }
+    return code;
+  }
+#endif
+  return 0;
+}
+
+int owl_zephyr_zqlength(void)
+{
+#ifdef HAVE_LIBZEPHYR
+  Code_t code;
+  if(owl_global_is_havezephyr(&g)) {
+    if((code = ZQLength()) < 0) {
+      owl_function_debugmsg("Error (%s) in ZQLength()\n",
                             error_message(code));
       return 0;
@@ -1470,11 +1500,36 @@
 }
 
-void owl_zephyr_process_events(const owl_io_dispatch *d, void *data)
-{
+typedef struct { /*noproto*/
+  GSource source;
+  GPollFD poll_fd;
+} owl_zephyr_event_source;
+
+static GSource *owl_zephyr_event_source_new(int fd) {
+  GSource *source;
+  owl_zephyr_event_source *event_source;
+
+  source = g_source_new(&zephyr_event_funcs, sizeof(owl_zephyr_event_source));
+  event_source = (owl_zephyr_event_source*) source;
+  event_source->poll_fd.fd = fd;
+  event_source->poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_PRI | G_IO_ERR;
+  g_source_add_poll(source, &event_source->poll_fd);
+
+  return source;
+}
+
+static gboolean owl_zephyr_event_prepare(GSource *source, int *timeout) {
+  *timeout = -1;
+  return owl_zephyr_zqlength() > 0;
+}
+
+static gboolean owl_zephyr_event_check(GSource *source) {
+  owl_zephyr_event_source *event_source = (owl_zephyr_event_source*)source;
+  if (event_source->poll_fd.revents & event_source->poll_fd.events)
+    return owl_zephyr_zpending() > 0;
+  return FALSE;
+}
+
+static gboolean owl_zephyr_event_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
   _owl_zephyr_process_events();
-}
-
-int owl_zephyr_pre_select_action(owl_ps_action *a, void *p)
-{
-  return _owl_zephyr_process_events();
-}
+  return TRUE;
+}
