Index: Makefile.am
===================================================================
--- Makefile.am	(revision ce35060c0b8f538bfc2659317d8bf70e8a2adbf7)
+++ Makefile.am	(revision 3535a6e36df4c6beed134aef3f7f81c7f9ea6dd2)
@@ -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: README
===================================================================
--- README	(revision aeadc748f353cd0be4ebfa805d85577c9c7498fa)
+++ README	(revision 13ee8f2b200092c55bc8af13b46ca948c16dcd94)
@@ -18,4 +18,5 @@
 BarnOwl currently requires the following perl modules off of CPAN:
 
+AnyEvent
 PAR
 Net::DNS
@@ -30,5 +31,5 @@
 The IRC module requires:
 
-Net::IRC
+AnyEvent::IRC
 Class::Accessor
 
Index: aim.c
===================================================================
--- aim.c	(revision 3472845ecdabe9cd1549dd2b2355ca745059ea36)
+++ aim.c	(revision dc1edbded39e57c67e8049c2960f0c235cd5afb5)
@@ -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: cmd.c
===================================================================
--- cmd.c	(revision 6a7111370ea0e1de2c3eb86a85f34cd68848d4b1)
+++ cmd.c	(revision 4c7c21f786a0416a755645b78db0b851f75f8748)
@@ -12,12 +12,11 @@
 
 int owl_cmddict_setup(owl_cmddict *cd) {
-  if (0 != owl_cmddict_init(cd)) return(-1);
+  owl_cmddict_init(cd);
   if (0 != owl_cmddict_add_from_list(cd, commands_to_init)) return(-1);
   return(0);
 }
 
-int owl_cmddict_init(owl_cmddict *cd) {
-  if (owl_dict_create(cd)) return(-1);
-  return(0);
+void owl_cmddict_init(owl_cmddict *cd) {
+  owl_dict_create(cd);
 }
 
@@ -135,9 +134,9 @@
 void owl_cmd_cleanup(owl_cmd *cmd)
 {
-  if (cmd->name) g_free(cmd->name);
-  if (cmd->summary) g_free(cmd->summary);
-  if (cmd->usage) g_free(cmd->usage);
-  if (cmd->description) g_free(cmd->description);
-  if (cmd->cmd_aliased_to) g_free(cmd->cmd_aliased_to);
+  g_free(cmd->name);
+  g_free(cmd->summary);
+  g_free(cmd->usage);
+  g_free(cmd->description);
+  g_free(cmd->cmd_aliased_to);
   if (cmd->cmd_perl) owl_perlconfig_cmd_cleanup(cmd);
 }
Index: commands.c
===================================================================
--- commands.c	(revision c809f5e8a3cc2745724f518c89b09279b5508b02)
+++ commands.c	(revision 697221fc51f0060043c93821f0358317f6bc6ab0)
@@ -281,6 +281,6 @@
   OWLCMD_ARGS("punt", owl_command_punt, OWL_CTX_ANY,
 	      "suppress an arbitrary filter",
-	      "punt <filter-text>",
-	      "punt <filter-text (multiple words)>\n"
+	      "punt <filter-name>\n"
+	      "punt <filter-text (multiple words)>",
 	      "The punt command will suppress messages to the specified\n"
 	      "filter\n\n"
@@ -289,10 +289,10 @@
   OWLCMD_ARGS("unpunt", owl_command_unpunt, OWL_CTX_ANY,
 	      "remove an entry from the punt list",
-	      "unpunt <filter-text>\n"
-	      "unpunt <filter-text>\n"
-	      "unpunt <number>\n",
+	      "unpunt <number>\n"
+	      "unpunt <filter-name>\n"
+	      "unpunt <filter-text (multiple words)>",
 	      "The unpunt command will remove an entry from the puntlist.\n"
-	      "The first two forms correspond to the first two forms of the :punt\n"
-	      "command. The latter allows you to remove a specific entry from the\n"
+	      "The last two forms correspond to the two forms of the :punt\n"
+	      "command. The first allows you to remove a specific entry from\n"
 	      "the list (see :show zpunts)\n\n"
 	      "SEE ALSO:  punt, zpunt, zunpunt, show zpunts\n"),
@@ -1288,5 +1288,5 @@
   }
   owl_function_nextmsg_full(filter, skip_deleted, last_if_none);
-  if (filter) g_free(filter);
+  g_free(filter);
   return(NULL);
 }
@@ -1318,5 +1318,5 @@
   }
   owl_function_prevmsg_full(filter, skip_deleted, first_if_none);
-  if (filter) g_free(filter);
+  g_free(filter);
   return(NULL);
 }
@@ -1691,7 +1691,5 @@
   commands = g_strsplit_set(newbuff, ";", 0);
   for (i = 0; commands[i] != NULL; i++) {
-    if (lastrv) {
-      g_free(lastrv);
-    }
+    g_free(lastrv);
     lastrv = owl_function_command(commands[i]);
   }
@@ -2466,5 +2464,4 @@
   owl_list * fl;
   owl_filter * f;
-  char * text;
   int i;
 
@@ -2482,12 +2479,12 @@
         return;
       } else {
-        owl_function_error("No such filter number: %d", i+1);
+        owl_function_makemsg("No such filter number: %d.", i+1);
       }
     }
-    text = owl_string_build_quoted("filter %q", argv[1]);
-    owl_function_punt(text, unpunt);
-    g_free(text);
+    const char *filter[] = {"filter", argv[1]};
+    owl_function_punt(2, filter, unpunt);
   } else {
-    owl_function_punt(skiptokens(buff, 1), unpunt);
+    /* Pass in argv[1]..argv[argc-1]. */
+    owl_function_punt(argc - 1, argv + 1, unpunt);
   }
 }
Index: configure.ac
===================================================================
--- configure.ac	(revision 28da2daf4548256b44d1fd586200032e96462d57)
+++ configure.ac	(revision 125536565b016a2dc0130569f6192cfebbe39f49)
@@ -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 >= 2.12 gobject-2.0 gthread-2.0])
 
 AC_MSG_NOTICE([Adding glib-2.0 CFLAGS ${GLIB_CFLAGS}])
Index: dict.c
===================================================================
--- dict.c	(revision f25df21f6284aedd48c098bc68cee9896da5ce98)
+++ dict.c	(revision 4c7c21f786a0416a755645b78db0b851f75f8748)
@@ -15,10 +15,8 @@
 #define GROWBY 3 / 2
 
-int owl_dict_create(owl_dict *d) {
+void owl_dict_create(owl_dict *d) {
   d->size=0;
   d->els=g_new(owl_dict_el, INITSIZE);
   d->avail=INITSIZE;
-  if (d->els==NULL) return(-1);
-  return(0);
 }
 
@@ -60,12 +58,9 @@
 /* Appends dictionary keys to a list.  Duplicates the keys,
  * so they will need to be freed by the caller. */
-int owl_dict_get_keys(const owl_dict *d, owl_list *l) {
+void owl_dict_get_keys(const owl_dict *d, owl_list *l) {
   int i;
-  char *dupk;
   for (i=0; i<d->size; i++) {
-    if ((dupk = g_strdup(d->els[i].k)) == NULL) return(-1);
-    owl_list_append_element(l, dupk);
+    owl_list_append_element(l, g_strdup(d->els[i].k));
   }
-  return(0);
 }
 
@@ -84,5 +79,4 @@
 {
   int pos, found;
-  char *dupk;
   found = _owl_dict_find_pos(d, k, &pos);
   if (found && delete_on_replace) {
@@ -99,5 +93,4 @@
       if (d->els==NULL) return(-1);
     }
-    if ((dupk = g_strdup(k)) == NULL) return(-1);
     if (pos!=d->size) {
       /* shift forward to leave us a slot */
@@ -106,5 +99,5 @@
     }
     d->size++;
-    d->els[pos].k = dupk;
+    d->els[pos].k = g_strdup(k);
     d->els[pos].v = v;    
     return(0);
Index: editwin.c
===================================================================
--- editwin.c	(revision 47e0a6a50d1c08ec05030b2507b3f1a84b618a9f)
+++ editwin.c	(revision 3b8a563ae4e07b1064fe005714793565ac720dc4)
@@ -317,6 +317,5 @@
   }
 
-  if (locktext)
-    g_free(locktext);
+  g_free(locktext);
 
   oe_set_index(e, lock);
Index: filterproc.c
===================================================================
--- filterproc.c	(revision 8bd190d63b2f2c09ea416e7be7c51d974f56bfaf)
+++ filterproc.c	(revision e2cc8487b365b026214922326e880825057eb9e6)
@@ -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: fmtext.c
===================================================================
--- fmtext.c	(revision 4479497945518a0fe5ddc9e5cf08f98bc7990cd9)
+++ fmtext.c	(revision 7b4f3bea704ed82f2c36ba19fcf354d7442a5bb7)
@@ -184,14 +184,7 @@
 }
 
-static void _owl_fmtext_update_colorpair(short fg, short bg, short *pair)
-{
-  if (owl_global_get_hascolors(&g)) {
-    *pair = owl_fmtext_get_colorpair(fg, bg);
-  }
-}
-
 static void _owl_fmtext_wcolor_set(WINDOW *w, short pair)
 {
-  if (owl_global_get_hascolors(&g)) {
+  if (has_colors()) {
       wcolor_set(w,pair,NULL);
       wbkgdset(w, COLOR_PAIR(pair));
@@ -221,5 +214,5 @@
   bg = default_bgcolor;
   _owl_fmtext_wattrset(w, attr);
-  _owl_fmtext_update_colorpair(fg, bg, &pair);
+  pair = owl_fmtext_get_colorpair(fg, bg);
   _owl_fmtext_wcolor_set(w, pair);
 
@@ -270,5 +263,5 @@
       if (bg == OWL_COLOR_DEFAULT) bg = default_bgcolor;
       _owl_fmtext_wattrset(w, attr);
-      _owl_fmtext_update_colorpair(fg, bg, &pair);
+      pair = owl_fmtext_get_colorpair(fg, bg);
       _owl_fmtext_wcolor_set(w, pair);
 
@@ -662,5 +655,4 @@
            continue */
       } else if (!strcasecmp(buff, "@color") 
-                 && owl_global_get_hascolors(&g)
                  && owl_global_is_colorztext(&g)) {
         g_free(buff);
@@ -820,5 +812,5 @@
     }
   }
-  if (owl_global_get_hascolors(&g)) {
+  if (has_colors()) {
     for(i = 0; i < 8; i++) {
       short fg, bg;
@@ -835,4 +827,7 @@
   owl_colorpair_mgr *cpmgr;
   short pair;
+
+  if (!has_colors())
+    return 0;
 
   /* Sanity (Bounds) Check */
@@ -852,5 +847,5 @@
   if (!(pair != -1 && pair < cpmgr->next)) {
     /* If we didn't find a pair, search for a free one to assign. */
-    pair = (cpmgr->next < COLOR_PAIRS) ? cpmgr->next : -1;
+    pair = (cpmgr->next < owl_util_get_colorpairs()) ? cpmgr->next : -1;
     if (pair != -1) {
       /* We found a free pair, initialize it. */
Index: functions.c
===================================================================
--- functions.c	(revision 4fd211f3a533efcedaf4d0dc7c01e02087ad2e39)
+++ functions.c	(revision 259e60a8abc2d2d41bc7a484b02a334fdd13857f)
@@ -32,5 +32,5 @@
   char *rv;
   rv=owl_function_command(cmdbuff);
-  if (rv) g_free(rv);
+  g_free(rv);
 }
 
@@ -453,5 +453,5 @@
 
   if (rv || status) {
-    if(cryptmsg) g_free(cryptmsg);
+    g_free(cryptmsg);
     g_free(old_msg);
     owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
@@ -991,5 +991,5 @@
   /* execute the commands in shutdown */
   ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
-  if (ret) g_free(ret);
+  g_free(ret);
 
   /* signal our child process, if any */
@@ -1408,6 +1408,6 @@
 #ifdef HAVE_LIBZEPHYR
     if (owl_message_is_direction_in(m)) {
-      char *ptr, tmpbuff[1024];
-      int i, j, fields, len;
+      char *tmpbuff;
+      int i, fields;
 
       n=owl_message_get_notice(m);
@@ -1453,23 +1453,12 @@
 	owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
 
-	for (i=0; i<fields; i++) {
-	  ptr=owl_zephyr_get_field_as_utf8(n, i+1);
-	  len=strlen(ptr);
-	  if (len<30) {
-	    strncpy(tmpbuff, ptr, len);
-	    tmpbuff[len]='\0';
-	  } else {
-	    strncpy(tmpbuff, ptr, 30);
-	    tmpbuff[30]='\0';
-	    strcat(tmpbuff, "...");
-	  }
-	  g_free(ptr);
-
-	  for (j=0; j<strlen(tmpbuff); j++) {
-	    if (tmpbuff[j]=='\n') tmpbuff[j]='~';
-	    if (tmpbuff[j]=='\r') tmpbuff[j]='!';
-	  }
-
-	  owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i+1, tmpbuff);
+	for (i = 0; i < fields; i++) {
+	  tmpbuff = owl_zephyr_get_field_as_utf8(n, i + 1);
+
+	  g_strdelimit(tmpbuff, "\n", '~');
+	  g_strdelimit(tmpbuff, "\r", '!');
+
+	  owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i + 1, tmpbuff);
+	  g_free(tmpbuff);
 	}
 	owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
@@ -1817,7 +1806,7 @@
 	  owl_global_get_cols(&g));
 
-  if (owl_global_get_hascolors(&g)) {
+  if (has_colors()) {
     owl_fmtext_append_normal(&fm, "Color: Yes\n");
-    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
+    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_util_get_colorpairs());
     owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
   } else {
@@ -2226,9 +2215,7 @@
     f = fl->data;
     owl_fmtext_append_normal(&fm, "   ");
-    if (owl_global_get_hascolors(&g)) {
-      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
-    } else {
-      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
-    }
+    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
+                                   owl_filter_get_fgcolor(f),
+                                   owl_filter_get_bgcolor(f));
     owl_fmtext_append_normal(&fm, "\n");
   }
@@ -2360,7 +2347,5 @@
 done:
   g_free(class);
-  if (instance) {
-    g_free(instance);
-  }
+  g_free(instance);
   return(filtname);
 }
@@ -2523,5 +2508,5 @@
 
   filtname = g_strdup_printf("conversation-%s", ccs);
-  owl_text_tr(filtname, ' ', '-');
+  g_strdelimit(filtname, " ", '-');
 
   if (owl_global_get_filter(&g, filtname)) {
@@ -2785,24 +2770,30 @@
 void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
 {
-  GString *buf;
+  GPtrArray *argv;
   char *quoted;
 
-  buf = g_string_new("");
+  argv = g_ptr_array_new();
   if (!strcmp(class, "*")) {
-    g_string_append(buf, "class .*");
+    g_ptr_array_add(argv, g_strdup("class"));
+    g_ptr_array_add(argv, g_strdup(".*"));
   } else {
     quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
-    owl_string_appendf_quoted(buf, "class ^(un)*%q(\\.d)*$", quoted);
+    g_ptr_array_add(argv, g_strdup("class"));
+    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
     g_free(quoted);
   }
   if (!strcmp(inst, "*")) {
-    g_string_append(buf, " and instance .*");
+    g_ptr_array_add(argv, g_strdup("and"));
+    g_ptr_array_add(argv, g_strdup("instance"));
+    g_ptr_array_add(argv, g_strdup(".*"));
   } else {
     quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
-    owl_string_appendf_quoted(buf, " and instance ^(un)*%q(\\.d)*$", quoted);
+    g_ptr_array_add(argv, g_strdup("and"));
+    g_ptr_array_add(argv, g_strdup("instance"));
+    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
     g_free(quoted);
   }
   if (!strcmp(recip, "*")) {
-    /* g_string_append(buf, ""); */
+    /* nothing */
   } else {
     if(!strcmp(recip, "%me%")) {
@@ -2810,13 +2801,16 @@
     }
     quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
-    owl_string_appendf_quoted(buf, " and recipient ^%q$", quoted);
+    g_ptr_array_add(argv, g_strdup("and"));
+    g_ptr_array_add(argv, g_strdup("recipient"));
+    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
     g_free(quoted);
   }
 
-  owl_function_punt(buf->str, direction);
-  g_string_free(buf, true);
-}
-
-void owl_function_punt(const char *filter, int direction)
+  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
+  g_ptr_array_foreach(argv, (GFunc)g_free, NULL);
+  g_ptr_array_free(argv, true);
+}
+
+void owl_function_punt(int argc, const char *const *argv, int direction)
 {
   owl_filter *f;
@@ -2826,6 +2820,5 @@
 
   /* first, create the filter */
-  owl_function_debugmsg("About to filter %s", filter);
-  f = owl_filter_new_fromstring("punt-filter", filter);
+  f = owl_filter_new("punt-filter", argc, argv);
   if (f == NULL) {
     owl_function_error("Error creating filter for zpunt");
@@ -2854,9 +2847,11 @@
   }
 
-  owl_function_debugmsg("punting");
-  /* If we're punting, add the filter to the global punt list */
-  if (direction==0) {
+  if (direction == 0) {
+    owl_function_debugmsg("punting");
+    /* If we're punting, add the filter to the global punt list */
     owl_list_append_element(fl, f);
-  }
+  } else if (direction == 1) {
+    owl_function_makemsg("No matching punt filter");
+ }
 }
 
@@ -2995,13 +2990,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 +3077,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) {
@@ -3256,6 +3242,7 @@
   filename=owl_global_get_startupfile(&g);
 
-  /* delete earlier copies */
-  owl_util_file_deleteline(filename, buff, 1);
+  /* delete earlier copies, if the file exists */
+  if (g_file_test(filename, G_FILE_TEST_EXISTS))
+    owl_util_file_deleteline(filename, buff, 1);
 
   file=fopen(filename, "a");
@@ -3500,20 +3487,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 dc1edbded39e57c67e8049c2960f0c235cd5afb5)
@@ -16,4 +16,7 @@
 
   g_type_init();
+  g_thread_init(NULL);
+
+  owl_select_init();
 
   g->lines=LINES;
@@ -51,8 +54,4 @@
   g->direction=OWL_DIRECTION_DOWNWARDS;
   g->zaway=0;
-  if (has_colors()) {
-    g->hascolors=1;
-  }
-  g->colorpairs=COLOR_PAIRS;
   owl_fmtext_init_colorpair_mgr(&(g->cpmgr));
   g->debug=OWL_DEBUG;
@@ -99,5 +98,4 @@
 
   owl_errqueue_init(&(g->errqueue));
-  g->got_err_signal=0;
 
   owl_zbuddylist_create(&(g->zbuddies));
@@ -108,8 +106,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 +347,5 @@
 
 void owl_global_set_resize_pending(owl_global *g) {
-  g->resizepending=1;
+  g->resizepending = true;
 }
 
@@ -448,5 +447,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);
@@ -509,5 +508,5 @@
 
 void owl_global_set_startupargs(owl_global *g, int argc, char **argv) {
-  if (g->startupargs) g_free(g->startupargs);
+  g_free(g->startupargs);
   g->startupargs = g_strjoinv(" ", argv);
 }
@@ -574,17 +573,4 @@
 owl_view *owl_global_get_current_view(owl_global *g) {
   return(&(g->current_view));
-}
-
-/* has colors */
-
-int owl_global_get_hascolors(const owl_global *g) {
-  if (g->hascolors) return(1);
-  return(0);
-}
-
-/* color pairs */
-
-int owl_global_get_colorpairs(const owl_global *g) {
-  return(g->colorpairs);
 }
 
@@ -693,18 +679,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;
 }
 
@@ -764,6 +756,6 @@
 }
 
-int owl_global_get_style_names(const owl_global *g, owl_list *l) {
-  return owl_dict_get_keys(&(g->styledict), l);
+void owl_global_get_style_names(const owl_global *g, owl_list *l) {
+  owl_dict_get_keys(&(g->styledict), l);
 }
 
@@ -824,28 +816,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 +846,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 +916,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: keybinding.c
===================================================================
--- keybinding.c	(revision d07af84683f667d7c34814c1b0a12823b08f6df0)
+++ keybinding.c	(revision 3b8a563ae4e07b1064fe005714793565ac720dc4)
@@ -69,7 +69,7 @@
 void owl_keybinding_delete(owl_keybinding *kb)
 {
-  if (kb->keys) g_free(kb->keys);
-  if (kb->desc) g_free(kb->desc);
-  if (kb->command) g_free(kb->command);
+  g_free(kb->keys);
+  g_free(kb->desc);
+  g_free(kb->command);
   g_free(kb);
 }
Index: keymap.c
===================================================================
--- keymap.c	(revision 47e0a6a50d1c08ec05030b2507b3f1a84b618a9f)
+++ keymap.c	(revision 4c7c21f786a0416a755645b78db0b851f75f8748)
@@ -9,7 +9,7 @@
 {
   if (!name || !desc) return(-1);
-  if ((km->name = g_strdup(name)) == NULL) return(-1);
-  if ((km->desc = g_strdup(desc)) == NULL) return(-1);
-  if (0 != owl_list_create(&km->bindings)) return(-1);
+  km->name = g_strdup(name);
+  km->desc = g_strdup(desc);
+  owl_list_create(&km->bindings);
   km->parent = NULL;
   km->default_fn = default_fn;
@@ -179,10 +179,9 @@
 /* NOTE: keyhandler has private access to the internals of keymap */
 
-int owl_keyhandler_init(owl_keyhandler *kh)
-{
-  if (0 != owl_dict_create(&kh->keymaps)) return(-1); 
+void owl_keyhandler_init(owl_keyhandler *kh)
+{
+  owl_dict_create(&kh->keymaps);
   kh->active = NULL;
   owl_keyhandler_reset(kh);
-  return(0);
 }
 
Index: list.c
===================================================================
--- list.c	(revision ddbbcffae8064f4d266a9735a16225e4feef1a3c)
+++ list.c	(revision fda61d3fe64381f30e32bc0091ab5ab59d95524f)
@@ -5,11 +5,9 @@
 #define GROWBY 3 / 2
 
-int owl_list_create(owl_list *l)
+void owl_list_create(owl_list *l)
 {
   l->size=0;
   l->list=g_new(void *, INITSIZE);
   l->avail=INITSIZE;
-  if (l->list==NULL) return(-1);
-  return(0);
 }
 
Index: logging.c
===================================================================
--- logging.c	(revision fe3b017371488552616f09bfed6345a51e6fe0fa)
+++ logging.c	(revision cc305b53e93deb26ad26d3eb1beaf34d53c0d6ac)
@@ -4,4 +4,14 @@
 #include <ctype.h>
 #include <sys/param.h>
+
+typedef struct _owl_log_entry { /* noproto */
+  char *filename;
+  char *message;
+} owl_log_entry;
+
+
+static GMainContext *log_context;
+static GMainLoop *log_loop;
+static GThread *logging_thread;
 
 /* This is now the one function that should be called to log a
@@ -70,56 +80,127 @@
 }
 
-void owl_log_zephyr(const owl_message *m, FILE *file) {
-    char *tmp;
-    tmp=short_zuser(owl_message_get_sender(m));
-    fprintf(file, "Class: %s Instance: %s", owl_message_get_class(m), owl_message_get_instance(m));
-    if (strcmp(owl_message_get_opcode(m), "")) fprintf(file, " Opcode: %s", owl_message_get_opcode(m));
-    fprintf(file, "\n");
-    fprintf(file, "Time: %s Host: %s\n", owl_message_get_timestr(m), owl_message_get_hostname(m));
-    fprintf(file, "From: %s <%s>\n\n", owl_message_get_zsig(m), tmp);
-    fprintf(file, "%s\n\n", owl_message_get_body(m));
+char *owl_log_zephyr(const owl_message *m) {
+    char *tmp = NULL;
+    GString *buffer = NULL;
+    buffer = g_string_new("");
+    tmp = short_zuser(owl_message_get_sender(m));
+    g_string_append_printf(buffer, "Class: %s Instance: %s", 
+                           owl_message_get_class(m), 
+                           owl_message_get_instance(m));
+    if (strcmp(owl_message_get_opcode(m), "")) {
+      g_string_append_printf(buffer, " Opcode: %s", 
+                             owl_message_get_opcode(m));
+    }
+    g_string_append_printf(buffer, "\n");
+    g_string_append_printf(buffer, "Time: %s Host: %s\n", 
+                           owl_message_get_timestr(m), 
+                           owl_message_get_hostname(m));
+    g_string_append_printf(buffer, "From: %s <%s>\n\n", 
+                           owl_message_get_zsig(m), tmp);
+    g_string_append_printf(buffer, "%s\n\n", owl_message_get_body(m));
     g_free(tmp);
-}
-
-void owl_log_aim(const owl_message *m, FILE *file) {
-    fprintf(file, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m));
-    fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m));
-    if (owl_message_is_login(m))
-        fprintf(file, "LOGIN\n\n");
-    else if (owl_message_is_logout(m))
-        fprintf(file, "LOGOUT\n\n");
-    else
-        fprintf(file, "%s\n\n", owl_message_get_body(m));
-}
-
-void owl_log_jabber(const owl_message *m, FILE *file) {
-    fprintf(file, "From: <%s> To: <%s>\n",owl_message_get_sender(m), owl_message_get_recipient(m));
-    fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m));
-    fprintf(file, "%s\n\n",owl_message_get_body(m));
-}
-
-void owl_log_generic(const owl_message *m, FILE *file) {
-    fprintf(file, "From: <%s> To: <%s>\n", owl_message_get_sender(m), owl_message_get_recipient(m));
-    fprintf(file, "Time: %s\n\n", owl_message_get_timestr(m));
-    fprintf(file, "%s\n\n", owl_message_get_body(m));
+    return g_string_free(buffer, FALSE);
+}
+
+char *owl_log_aim(const owl_message *m) {
+    GString *buffer = NULL;
+    buffer = g_string_new("");
+    g_string_append_printf(buffer, "From: <%s> To: <%s>\n", 
+                           owl_message_get_sender(m), owl_message_get_recipient(m));
+    g_string_append_printf(buffer, "Time: %s\n\n", 
+                           owl_message_get_timestr(m));
+    if (owl_message_is_login(m)) {
+        g_string_append_printf(buffer, "LOGIN\n\n");
+    } else if (owl_message_is_logout(m)) {
+        g_string_append_printf(buffer, "LOGOUT\n\n");
+    } else {
+        g_string_append_printf(buffer, "%s\n\n", owl_message_get_body(m));
+    }
+    return g_string_free(buffer, FALSE);
+}
+
+char *owl_log_jabber(const owl_message *m) {
+    GString *buffer = NULL;
+    buffer = g_string_new("");
+    g_string_append_printf(buffer, "From: <%s> To: <%s>\n",
+                           owl_message_get_sender(m), 
+                           owl_message_get_recipient(m));
+    g_string_append_printf(buffer, "Time: %s\n\n", 
+                           owl_message_get_timestr(m));
+    g_string_append_printf(buffer, "%s\n\n", owl_message_get_body(m));
+    return g_string_free(buffer, FALSE);
+}
+
+char *owl_log_generic(const owl_message *m) {
+    GString *buffer;
+    buffer = g_string_new("");
+    g_string_append_printf(buffer, "From: <%s> To: <%s>\n", 
+                           owl_message_get_sender(m), 
+                           owl_message_get_recipient(m));
+    g_string_append_printf(buffer, "Time: %s\n\n", 
+                           owl_message_get_timestr(m));
+    g_string_append_printf(buffer, "%s\n\n", 
+                           owl_message_get_body(m));
+    return g_string_free(buffer, FALSE);
+}
+
+static void owl_log_error_main_thread(gpointer data)
+{
+  owl_function_error("%s", (const char*)data);
+}
+
+static void owl_log_error(const char *message)
+{
+  char *data = g_strdup(message);
+  owl_select_post_task(owl_log_error_main_thread,
+		       data, g_free, g_main_context_default());
+}
+
+static void owl_log_write_entry(gpointer data)
+{
+  owl_log_entry *msg = (owl_log_entry*)data;
+  FILE *file = NULL;
+  file = fopen(msg->filename, "a");
+  if (!file) {
+    owl_log_error("Unable to open file for logging");
+    return;
+  }
+  fprintf(file, "%s", msg->message);
+  fclose(file);
+}
+
+static void owl_log_entry_free(void *data)
+{
+  owl_log_entry *msg = (owl_log_entry*)data;
+  if (msg) {
+    g_free(msg->message);
+    g_free(msg->filename);
+    g_free(msg);
+  }
+}
+
+void owl_log_enqueue_message(const char *buffer, const char *filename)
+{
+  owl_log_entry *log_msg = NULL; 
+  log_msg = g_new(owl_log_entry,1);
+  log_msg->message = g_strdup(buffer);
+  log_msg->filename = g_strdup(filename);
+  owl_select_post_task(owl_log_write_entry, log_msg, 
+		       owl_log_entry_free, log_context);
 }
 
 void owl_log_append(const owl_message *m, const char *filename) {
-    FILE *file;
-    file=fopen(filename, "a");
-    if (!file) {
-        owl_function_error("Unable to open file for logging");
-        return;
-    }
-    if (owl_message_is_type_zephyr(m)) {
-        owl_log_zephyr(m, file);
-    } else if (owl_message_is_type_jabber(m)) {
-        owl_log_jabber(m, file);
-    } else if (owl_message_is_type_aim(m)) {
-        owl_log_aim(m, file);
-    } else {
-        owl_log_generic(m, file);
-    }
-    fclose(file);
+  char *buffer = NULL;
+  if (owl_message_is_type_zephyr(m)) {
+    buffer = owl_log_zephyr(m);
+  } else if (owl_message_is_type_jabber(m)) {
+    buffer = owl_log_jabber(m);
+  } else if (owl_message_is_type_aim(m)) {
+    buffer = owl_log_aim(m);
+  } else {
+    buffer = owl_log_generic(m);
+  }
+  owl_log_enqueue_message(buffer, filename);
+  g_free(buffer);
 }
 
@@ -151,5 +232,5 @@
   } else if (owl_message_is_type_jabber(m)) {
     to = g_strdup_printf("jabber:%s", owl_message_get_recipient(m));
-    owl_text_tr(to, '/', '_');
+    g_strdelimit(to, "/", '_');
   } else if (owl_message_is_type_aim(m)) {
     char *temp2;
@@ -177,9 +258,8 @@
 void owl_log_outgoing_zephyr_error(const owl_zwrite *zw, const char *text)
 {
-  FILE *file;
   char *filename, *logpath;
   char *tobuff, *recip;
   owl_message *m;
-
+  GString *msgbuf;
   /* create a present message so we can pass it to
    * owl_log_shouldlog_message(void)
@@ -201,34 +281,22 @@
   /* expand ~ in path names */
   logpath = owl_util_makepath(owl_global_get_logpath(&g));
-
   filename = g_strdup_printf("%s/%s", logpath, tobuff);
-  file=fopen(filename, "a");
-  g_free(filename);
-  if (!file) {
-    owl_function_error("Unable to open file for outgoing logging");
-    g_free(logpath);
-    g_free(tobuff);
-    return;
-  }
-  fprintf(file, "ERROR (owl): %s\n%s\n", tobuff, text);
-  if (text[strlen(text)-1]!='\n') {
-    fprintf(file, "\n");
-  }
-  fclose(file);
+  msgbuf = g_string_new("");
+  g_string_printf(msgbuf, "ERROR (owl): %s\n%s\n", tobuff, text);
+  if (text[strlen(text)-1] != '\n') {
+    g_string_append_printf(msgbuf, "\n");
+  }
+  owl_log_enqueue_message(msgbuf->str, filename);
+  g_string_free(msgbuf, TRUE);
 
   filename = g_strdup_printf("%s/all", logpath);
   g_free(logpath);
-  file=fopen(filename, "a");
-  g_free(filename);
-  if (!file) {
-    owl_function_error("Unable to open file for outgoing logging");
-    g_free(tobuff);
-    return;
-  }
-  fprintf(file, "ERROR (owl): %s\n%s\n", tobuff, text);
-  if (text[strlen(text)-1]!='\n') {
-    fprintf(file, "\n");
-  }
-  fclose(file);
+  msgbuf = g_string_new("");
+  g_string_printf(msgbuf, "ERROR (owl): %s\n%s\n", tobuff, text);
+  if (text[strlen(text)-1] != '\n') {
+    g_string_append_printf(msgbuf, "\n");
+  }
+  owl_log_enqueue_message(msgbuf->str, filename);
+  g_string_free(msgbuf, TRUE);
 
   g_free(tobuff);
@@ -284,7 +352,9 @@
   } else if (owl_message_is_type_jabber(m)) {
     if (personal) {
-      from=frombuff=g_strdup_printf("jabber:%s",owl_message_get_sender(m));
-    } else {
-      from=frombuff=g_strdup_printf("jabber:%s",owl_message_get_recipient(m));
+      from=frombuff=g_strdup_printf("jabber:%s", 
+				    owl_message_get_sender(m));
+    } else {
+      from=frombuff=g_strdup_printf("jabber:%s", 
+				    owl_message_get_recipient(m));
     }
   } else {
@@ -355,2 +425,38 @@
   g_free(logpath);
 }
+
+static gpointer owl_log_thread_func(gpointer data)
+{
+  log_context = g_main_context_new();
+  log_loop = g_main_loop_new(log_context, FALSE);
+  g_main_loop_run(log_loop);
+  return NULL;
+}
+
+void owl_log_init(void) 
+{
+  GError *error = NULL;
+  logging_thread = g_thread_create(owl_log_thread_func,
+                                   NULL,
+                                   TRUE,
+                                   &error);
+  if (error) {
+    endwin();
+    fprintf(stderr, "Error spawning logging thread: %s\n", error->message);
+    fflush(stderr);
+    exit(1);
+  }
+  
+}
+
+static void owl_log_quit_func(gpointer data)
+{
+  g_main_loop_quit(log_loop);
+}
+
+void owl_log_shutdown(void)
+{
+  owl_select_post_task(owl_log_quit_func, NULL,
+		       NULL, log_context);
+  g_thread_join(logging_thread);
+}
Index: message.c
===================================================================
--- message.c	(revision 3a3863ecf31b96ba91331ec699146c242ae061c3)
+++ message.c	(revision 259e60a8abc2d2d41bc7a484b02a334fdd13857f)
@@ -120,5 +120,5 @@
   int i, j;
   owl_pair *p;
-  char *buff;
+  char *buff, *tmpbuff;
 
   owl_fmtext_init_null(fm);
@@ -127,7 +127,13 @@
   for (i=0; i<j; i++) {
     p=owl_list_get_element(&(m->attributes), i);
-    buff=g_strdup_printf("  %-15.15s: %-35.35s\n", owl_pair_get_key(p), owl_pair_get_value(p));
+
+    tmpbuff = g_strdup(owl_pair_get_value(p));
+    g_strdelimit(tmpbuff, "\n", '~');
+    g_strdelimit(tmpbuff, "\r", '!');
+    buff = g_strdup_printf("  %-15.15s: %s\n", owl_pair_get_key(p), tmpbuff);
+    g_free(tmpbuff);
+
     if(buff == NULL) {
-      buff=g_strdup_printf("  %-15.15s: %-35.35s\n", owl_pair_get_key(p), "<error>");
+      buff = g_strdup_printf("  %-15.15s: %s\n", owl_pair_get_key(p), "<error>");
       if(buff == NULL)
         buff=g_strdup("   <error>\n");
Index: owl.c
===================================================================
--- owl.c	(revision a2a883313ecb42f7eb3fc95a46c710acb4a171ea)
+++ owl.c	(revision cc305b53e93deb26ad26d3eb1beaf34d53c0d6ac)
@@ -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,69 @@
 }
 
-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, g_main_context_default());
+}
+
+#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);
 }
 
@@ -424,9 +465,8 @@
   }
   bread = read(rfd, buf, navail);
-  if (buf[navail-1] != '\0') {
-    buf[navail] = '\0';
-  }
-
-  err = g_strdup_printf("[stderr]\n%s", buf);
+  if (bread == -1)
+    return;
+
+  err = g_strdup_printf("[stderr]\n%.*s", bread, buf);
 
   owl_function_log_err(err);
@@ -435,27 +475,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();
 
@@ -493,4 +510,6 @@
   owl_global_set_haveaim(&g);
 
+  owl_register_signal_handlers();
+
   /* register STDIN dispatch; throw away return, we won't need it */
   owl_select_add_io_dispatch(STDIN_FILENO, OWL_IO_READ, &owl_process_input, NULL, NULL);
@@ -551,5 +570,5 @@
   owl_function_debugmsg("startup: executing perl startup, if applicable");
   perlout = owl_perlconfig_execute("BarnOwl::Hooks::_startup();");
-  if (perlout) g_free(perlout);
+  g_free(perlout);
 
   /* welcome message */
@@ -584,7 +603,13 @@
   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_log_init();
 
   owl_function_debugmsg("startup: entering main loop");
@@ -593,5 +618,7 @@
   /* Shut down everything. */
   owl_zephyr_shutdown();
+  owl_signal_shutdown();
   owl_shutdown_curses();
+  owl_log_shutdown();
   return 0;
 }
Index: owl.h
===================================================================
--- owl.h	(revision 28da2daf4548256b44d1fd586200032e96462d57)
+++ owl.h	(revision 33b6431b480426ea3c8334c4c6de3f19ad477071)
@@ -57,4 +57,5 @@
 #include "window.h"
 
+#ifndef OWL_VERSION_STRING
 #ifdef  GIT_VERSION
 #define stringify(x)       __stringify(x)
@@ -64,4 +65,5 @@
 #define OWL_VERSION_STRING PACKAGE_VERSION
 #endif
+#endif /* !OWL_VERSION_STRING */
 
 /* Feature that is being tested to redirect stderr through a pipe. 
@@ -537,16 +539,11 @@
   int fd;                                     /* FD to watch for dispatch. */
   int mode;
+  bool valid;
   int needs_gc;
   void (*callback)(const struct _owl_io_dispatch *, void *); /* C function to dispatch to. */
   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 {
@@ -557,6 +554,4 @@
   const owl_io_dispatch *dispatch;
 } owl_popexec;
-
-typedef struct _OwlGlobalNotifier OwlGlobalNotifier;
 
 typedef struct _owl_global {
@@ -584,5 +579,5 @@
   gulong typwin_erase_id;
   int rightshift;
-  volatile sig_atomic_t resizepending;
+  bool resizepending;
   char *homedir;
   char *confdir;
@@ -601,6 +596,4 @@
   char *startupargs;
   int nextmsgid;
-  int hascolors;
-  int colorpairs;
   owl_colorpair_mgr cpmgr;
   pid_t newmsgproc_pid;
@@ -609,5 +602,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 */
@@ -619,6 +612,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;
@@ -626,11 +617,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: perl/lib/BarnOwl.pm
===================================================================
--- perl/lib/BarnOwl.pm	(revision b120bd3c1bd6d9da691c8e4f11a3a1ba56d34c83)
+++ perl/lib/BarnOwl.pm	(revision f2d71cfa06117d6e28806432db20b584c4596a19)
@@ -38,4 +38,8 @@
 use BarnOwl::Completion;
 use BarnOwl::Help;
+use BarnOwl::AnyEvent;
+
+unshift @AnyEvent::REGISTRY, [BarnOwl => BarnOwl::AnyEvent::];
+require AnyEvent;
 
 use List::Util qw(max);
Index: perl/lib/BarnOwl/AnyEvent.pm
===================================================================
--- perl/lib/BarnOwl/AnyEvent.pm	(revision 21bab95d0968516d39d01ad621819e0be316e181)
+++ perl/lib/BarnOwl/AnyEvent.pm	(revision 21bab95d0968516d39d01ad621819e0be316e181)
@@ -0,0 +1,42 @@
+use strict;
+use warnings;
+package BarnOwl::AnyEvent;
+use BarnOwl::Timer;
+
+sub io {
+    my ($class, %arg) = @_;
+    my $fd = fileno($arg{fh});
+    my $mode = $arg{poll};
+    my $cb = $arg{cb};
+    my $self = {cb => $cb,
+                mode => $mode,
+                fd => $fd };
+    bless($self, $class);
+    $self->{dispatch} = BarnOwl::add_io_dispatch($fd, $mode, $cb);
+    return $self;
+}
+
+sub BarnOwl::AnyEvent::io::DESTROY
+{
+    my ($self) = @_;
+    BarnOwl::remove_io_dispatch($self->{fd});
+}
+
+sub timer {
+    my ($class, %arg) = @_;
+    return BarnOwl::Timer->new(\%arg);
+}
+
+sub DESTROY { }
+
+# sub idle {
+#     my ($class, %arg) = @_;
+#     my $cb = $arg{cb};
+# }
+
+# sub child {
+#     my ($class, %arg) = @_;
+#     my $cb = $arg{cb};
+# }
+
+1;
Index: perl/modules/IRC/lib/BarnOwl/Message/IRC.pm
===================================================================
--- perl/modules/IRC/lib/BarnOwl/Message/IRC.pm	(revision e04b7a18dd7a84eace5694f556a164a5128333a6)
+++ perl/modules/IRC/lib/BarnOwl/Message/IRC.pm	(revision 60b49a7d6017a553f86ef3036247a58587f313b9)
@@ -66,4 +66,5 @@
 sub action {shift->{action}}
 sub reason {shift->{reason}}
+sub old_nick {shift->{old_nick}};
 
 # display
@@ -80,8 +81,10 @@
 }
 
-sub login_extra { 
+sub login_extra {
     my $self = shift;
     if ($self->action eq "quit") {
         return $self->reason;
+    } elsif ($self->action eq 'nick change') {
+        return "was: " . $self->old_nick;
     } else {
         return $self->channel;
Index: perl/modules/IRC/lib/BarnOwl/Module/IRC.pm
===================================================================
--- perl/modules/IRC/lib/BarnOwl/Module/IRC.pm	(revision 9620c8d92ecfed30b4b6c4d5d6861179e2ab418d)
+++ perl/modules/IRC/lib/BarnOwl/Module/IRC.pm	(revision 5c6d661cb25bf113e3f1b18aaed92753a61a9f96)
@@ -20,5 +20,5 @@
 use BarnOwl::Module::IRC::Completion;
 
-use Net::IRC;
+use AnyEvent::IRC;
 use Getopt::Long;
 use Encode;
@@ -30,6 +30,4 @@
 # Hash alias -> BarnOwl::Module::IRC::Connection object
 our %ircnets;
-our %channels;
-our %reconnect;
 
 sub startup {
@@ -70,5 +68,4 @@
 
     register_commands();
-    register_handlers();
     BarnOwl::filter(qw{irc type ^IRC$ or ( type ^admin$ and adminheader ^IRC$ )});
 }
@@ -76,5 +73,5 @@
 sub shutdown {
     for my $conn (values %ircnets) {
-        $conn->conn->disconnect();
+        $conn->conn->disconnect('Quitting');
     }
 }
@@ -98,6 +95,5 @@
         $list .= "\n";
 
-        for my $chan (keys %channels) {
-            next unless grep $_ eq $conn, @{$channels{$chan}};
+        for my $chan (keys %{$conn->conn->{channel_list}}) {
             $list .= "  $chan\n";
         }
@@ -105,28 +101,4 @@
 
     return $list;
-}
-
-#sub mainloop_hook {
-#    return unless defined $irc;
-#    eval {
-#        $irc->do_one_loop();
-#    };
-#    return;
-#}
-
-sub OwlProcess {
-    return unless defined $irc;
-    eval {
-        $irc->do_one_loop();
-    };
-    return;
-}
-
-
-sub register_handlers {
-    if(!$irc) {
-        $irc = Net::IRC->new;
-        $irc->timeout(0);
-    }
 }
 
@@ -406,21 +378,13 @@
     }
 
-    my $conn = BarnOwl::Module::IRC::Connection->new($irc, $alias,
-        Nick      => $nick,
-        Server    => $host,
-        Port      => $port,
-        Username  => $username,
-        Ircname   => $ircname,
-        Port      => $port,
-        Password  => $password,
-        SSL       => $ssl
-       );
-
-    if ($conn->conn->connected) {
-        $conn->connected("Connected to $alias as $nick");
-    } else {
-        die("IRC::Connection->connect failed: $!");
-    }
-
+    my $conn = BarnOwl::Module::IRC::Connection->new($alias, $host, $port, {
+        nick      => $nick,
+        user      => $username,
+        real      => $ircname,
+        password  => $password,
+        SSL       => $ssl,
+        timeout   => sub {0}
+       });
+    $ircnets{$alias} = $conn;
     return;
 }
@@ -429,10 +393,12 @@
     my $cmd = shift;
     my $conn = shift;
-    if ($conn->conn->connected) {
-        $conn->conn->disconnect;
-    } elsif ($reconnect{$conn->alias}) {
+    if ($conn->conn->{socket}) {
+        $conn->did_quit(1);
+        $conn->conn->disconnect("Goodbye!");
+    } elsif ($conn->{reconnect_timer}) {
         BarnOwl::admin_message('IRC',
                                "[" . $conn->alias . "] Reconnect cancelled");
         $conn->cancel_reconnect;
+        delete $ircnets{$conn->alias};
     }
 }
@@ -463,8 +429,8 @@
     for my $body (@msgs) {
 	if ($body =~ /^\/me (.*)/) {
-	    $conn->conn->me($to, Encode::encode('utf-8', $1));
+	    $conn->me($to, Encode::encode('utf-8', $1));
 	    $body = '* '.$conn->nick.' '.$1;
 	} else {
-	    $conn->conn->privmsg($to, Encode::encode('utf-8', $body));
+	    $conn->conn->send_msg('privmsg', $to, Encode::encode('utf-8', $body));
 	}
 	my $msg = BarnOwl::Message->new(
@@ -491,5 +457,5 @@
     my $target = shift;
     $target ||= shift;
-    $conn->conn->mode($target, @_);
+    $conn->conn->send_msg(mode => $target, @_);
     return;
 }
@@ -499,7 +465,5 @@
     my $conn = shift;
     my $chan = shift or die("Usage: $cmd channel\n");
-    $channels{$chan} ||= [];
-    push @{$channels{$chan}}, $conn;
-    $conn->conn->join($chan, @_);
+    $conn->conn->send_msg(join => $chan, @_);
     return;
 }
@@ -509,6 +473,5 @@
     my $conn = shift;
     my $chan = shift;
-    $channels{$chan} = [grep {$_ ne $conn} @{$channels{$chan} || []}];
-    $conn->conn->part($chan);
+    $conn->conn->send_msg(part => $chan);
     return;
 }
@@ -518,5 +481,5 @@
     my $conn = shift;
     my $nick = shift or die("Usage: $cmd <new nick>\n");
-    $conn->conn->nick($nick);
+    $conn->conn->send_msg(nick => $nick);
     return;
 }
@@ -527,5 +490,5 @@
     my $chan = shift;
     $conn->names_tmp([]);
-    $conn->conn->names($chan);
+    $conn->conn->send_msg(names => $chan);
     return;
 }
@@ -535,5 +498,5 @@
     my $conn = shift;
     my $who = shift || die("Usage: $cmd <user>\n");
-    $conn->conn->whois($who);
+    $conn->conn->send_msg(whois => $who);
     return;
 }
@@ -542,5 +505,5 @@
     my $cmd = shift;
     my $conn = shift;
-    $conn->conn->motd;
+    $conn->conn->send_msg('motd');
     return;
 }
@@ -560,6 +523,5 @@
     my $conn = shift;
     my $who = shift || die("Usage: $cmd <user>\n");
-    BarnOwl::error("WHO $cmd $conn $who");
-    $conn->conn->who($who);
+    $conn->conn->send_msg(who => $who);
     return;
 }
@@ -569,5 +531,5 @@
     my $conn = shift;
     my $type = shift || die("Usage: $cmd <chiklmouy> [server] \n");
-    $conn->conn->stats($type, @_);
+    $conn->conn->send_msg(stats => $type, @_);
     return;
 }
@@ -577,5 +539,5 @@
     my $conn = shift;
     my $chan = shift;
-    $conn->conn->topic($chan, @_ ? join(" ", @_) : undef);
+    $conn->conn->send_msg(topic => $chan, @_ ? join(" ", @_) : undef);
     return;
 }
@@ -584,5 +546,5 @@
     my $cmd = shift;
     my $conn = shift;
-    $conn->conn->sl(join(" ", @_));
+    $conn->conn->send_msg(@_);
     return;
 }
@@ -591,4 +553,15 @@
 ########################### Utilities/Helpers ##################################
 ################################################################################
+
+sub find_channel {
+    my $channel = shift;
+    my @found;
+    for my $conn (values %ircnets) {
+        if($conn->conn->{channel_list}{lc $channel}) {
+            push @found, $conn;
+        }
+    }
+    return $found[0] if(scalar @found == 1);
+}
 
 sub mk_irc_command {
@@ -614,7 +587,7 @@
             $channel = $ARGV[0];
             if(defined($channel) && $channel =~ /^#/) {
-                if($channels{$channel} && @{$channels{$channel}} == 1) {
+                if(my $c = find_channel($channel)) {
                     shift @ARGV;
-                    $conn = $channels{$channel}[0] unless $conn;
+                    $conn ||= $c;
                 }
             } elsif ($m && $m->type eq 'IRC' && !$m->is_private) {
@@ -654,7 +627,10 @@
     my $allow_disconnected = shift;
 
-    return $ircnets{$key} if exists $ircnets{$key};
-    return $reconnect{$key} if $allow_disconnected && exists $reconnect{$key};
-    die("No such ircnet: $key\n")
+    my $conn = $ircnets{$key};
+    die("No such ircnet: $key\n") unless $conn;
+    if ($conn->conn->{registered} || $allow_disconnected) {
+        return $conn;
+    }
+    die("[@{[$conn->alias]}] Not currently connected.");
 }
 
Index: perl/modules/IRC/lib/BarnOwl/Module/IRC/Completion.pm
===================================================================
--- perl/modules/IRC/lib/BarnOwl/Module/IRC/Completion.pm	(revision 955a36ef84bad0cfeb1e445598bf61de3856e4fb)
+++ perl/modules/IRC/lib/BarnOwl/Module/IRC/Completion.pm	(revision dace02afa8202c17375f083df8247cc4f9307dcb)
@@ -11,5 +11,13 @@
 sub complete_networks { keys %BarnOwl::Module::IRC::ircnets }
 sub complete_dests    { keys %users, complete_channels() }
-sub complete_channels { keys %BarnOwl::Module::IRC::channels }
+sub complete_channels {
+    my %channels;
+    for my $conn (values %BarnOwl::Module::IRC::ircnets) {
+        for my $chan (keys %{$conn->conn->{channel_list}}) {
+            $channels{$chan} = 1;
+        }
+    }
+    return keys %channels;
+}
 sub complete_nicks    { keys %users }
 sub complete_servers  { keys %servers }
Index: perl/modules/IRC/lib/BarnOwl/Module/IRC/Connection.pm
===================================================================
--- perl/modules/IRC/lib/BarnOwl/Module/IRC/Connection.pm	(revision fb6e8e31c0b7bb318c002069222e2759783a9069)
+++ perl/modules/IRC/lib/BarnOwl/Module/IRC/Connection.pm	(revision 13ee8f2b200092c55bc8af13b46ca948c16dcd94)
@@ -11,70 +11,101 @@
 =head1 DESCRIPTION
 
-This module is a wrapper around Net::IRC::Connection for BarnOwl's IRC
+This module is a wrapper around AnyEvent::IRC::Client for BarnOwl's IRC
 support
 
 =cut
 
-use Net::IRC::Connection;
-
-use base qw(Class::Accessor Exporter);
-__PACKAGE__->mk_accessors(qw(conn alias channels motd names_tmp whois_tmp));
-our @EXPORT_OK = qw(&is_private);
+use AnyEvent::IRC::Client;
+use AnyEvent::IRC::Util qw(split_prefix prefix_nick encode_ctcp);
+
+use base qw(Class::Accessor);
+use Exporter 'import';
+__PACKAGE__->mk_accessors(qw(conn alias motd names_tmp whois_tmp
+                             server autoconnect_channels
+                             connect_args backoff did_quit));
+our @EXPORT_OK = qw(is_private);
 
 use BarnOwl;
 use Scalar::Util qw(weaken);
 
-BEGIN {
-    no strict 'refs';
-    my @delegate = qw(nick server);
-    for my $meth (@delegate) {
-        *{"BarnOwl::Module::IRC::Connection::$meth"} = sub {
-            shift->conn->$meth(@_);
-        }
-    }
-};
-
 sub new {
     my $class = shift;
-    my $irc = shift;
     my $alias = shift;
-    my %args = (@_);
-    my $conn = Net::IRC::Connection->new($irc, %args);
+    my $host  = shift;
+    my $port  = shift;
+    my $args  = shift;
+    my $nick = $args->{nick};
+    my $conn = AnyEvent::IRC::Client->new();
     my $self = bless({}, $class);
     $self->conn($conn);
+    $self->autoconnect_channels([]);
     $self->alias($alias);
-    $self->channels([]);
+    $self->server($host);
     $self->motd("");
     $self->names_tmp(0);
+    $self->backoff(0);
     $self->whois_tmp("");
-
-    $self->conn->add_default_handler(sub { shift; $self->on_event(@_) });
-    $self->conn->add_handler(['msg', 'notice', 'public', 'caction'],
-            sub { shift; $self->on_msg(@_) });
-    $self->conn->add_handler(['welcome', 'yourhost', 'created',
-                              'luserclient', 'luserop', 'luserchannels', 'luserme',
-                              'error'],
-            sub { shift; $self->on_admin_msg(@_) });
-    $self->conn->add_handler(['myinfo', 'map', 'n_local', 'n_global',
-            'luserconns'],
-            sub { });
-    $self->conn->add_handler(motdstart => sub { shift; $self->on_motdstart(@_) });
-    $self->conn->add_handler(motd      => sub { shift; $self->on_motd(@_) });
-    $self->conn->add_handler(endofmotd => sub { shift; $self->on_endofmotd(@_) });
-    $self->conn->add_handler(join      => sub { shift; $self->on_join(@_) });
-    $self->conn->add_handler(part      => sub { shift; $self->on_part(@_) });
-    $self->conn->add_handler(quit      => sub { shift; $self->on_quit(@_) });
-    $self->conn->add_handler(disconnect => sub { shift; $self->on_disconnect(@_) });
-    $self->conn->add_handler(nicknameinuse => sub { shift; $self->on_nickinuse(@_) });
-    $self->conn->add_handler(cping     => sub { shift; $self->on_ping(@_) });
-    $self->conn->add_handler(topic     => sub { shift; $self->on_topic(@_) });
-    $self->conn->add_handler(topicinfo => sub { shift; $self->on_topicinfo(@_) });
-    $self->conn->add_handler(namreply  => sub { shift; $self->on_namreply(@_) });
-    $self->conn->add_handler(endofnames=> sub { shift; $self->on_endofnames(@_) });
-    $self->conn->add_handler(endofwhois=> sub { shift; $self->on_endofwhois(@_) });
-    $self->conn->add_handler(mode      => sub { shift; $self->on_mode(@_) });
-    $self->conn->add_handler(nosuchchannel => sub { shift; $self->on_nosuchchannel(@_) });
+    $self->did_quit(0);
+
+    if(delete $args->{SSL}) {
+        $conn->enable_ssl;
+    }
+    $self->connect_args([$host, $port, $args]);
+    $conn->connect($host, $port, $args);
+    $conn->{heap}{parent} = $self;
+    weaken($conn->{heap}{parent});
+
+    sub on {
+        my $meth = "on_" . shift;
+        return sub {
+            my $conn = shift;
+            return unless $conn->{heap}{parent};
+            $conn->{heap}{parent}->$meth(@_);
+        }
+    }
+
+    # $self->conn->add_default_handler(sub { shift; $self->on_event(@_) });
+    $self->conn->reg_cb(registered => on("connect"),
+                        connfail   => sub { BarnOwl::error("Connection to $host failed!") },
+                        disconnect => on("disconnect"),
+                        publicmsg  => on("msg"),
+                        privatemsg => on("msg"),
+                        irc_error  => on("error"));
+    for my $m (qw(welcome yourhost created
+                  luserclient luserop luserchannels luserme
+                  error)) {
+        $self->conn->reg_cb("irc_$m" => on("admin_msg"));
+    }
+    $self->conn->reg_cb(irc_375       => on("motdstart"),
+                        irc_372       => on("motd"),
+                        irc_376       => on("endofmotd"),
+                        irc_join      => on("join"),
+                        irc_part      => on("part"),
+                        irc_quit      => on("quit"),
+                        irc_433       => on("nickinuse"),
+                        channel_topic => on("topic"),
+                        irc_333       => on("topicinfo"),
+                        irc_353       => on("namreply"),
+                        irc_366       => on("endofnames"),
+                        irc_311       => on("whois"),
+                        irc_312       => on("whois"),
+                        irc_319       => on("whois"),
+                        irc_320       => on("whois"),
+                        irc_318       => on("endofwhois"),
+                        irc_mode      => on("mode"),
+                        irc_401       => on("nosuch"),
+                        irc_402       => on("nosuch"),
+                        irc_403       => on("nosuch"),
+                        nick_change   => on("nick"),
+                        ctcp_action   => on("ctcp_action"),
+                        'irc_*' => sub { BarnOwl::debug("IRC: " . $_[1]->{command} . " " .
+                                                        join(" ", @{$_[1]->{params}})) });
 
     return $self;
+}
+
+sub nick {
+    my $self = shift;
+    return $self->conn->nick;
 }
 
@@ -83,4 +114,10 @@
     my $self = shift;
     return $self->conn->socket;
+}
+
+sub me {
+    my ($self, $to, $msg) = @_;
+    $self->conn->send_msg('privmsg', $to,
+                          encode_ctcp(['ACTION', $msg]))
 }
 
@@ -92,32 +129,50 @@
     my $self = shift;
     my $evt = shift;
-    return BarnOwl::Message->new(
+    my %args = (
         type        => 'IRC',
         server      => $self->server,
         network     => $self->alias,
-        sender      => $evt->nick,
-        hostname    => $evt->host,
-        from        => $evt->from,
         @_
        );
+    if ($evt) {
+        my ($nick, $user, $host) = split_prefix($evt);
+        $args{sender}   ||= $nick;
+        $args{hostname} ||= $host if defined($host);
+        $args{from}     ||= $evt->{prefix};
+        $args{params}   ||= join(' ', @{$evt->{params}})
+    }
+    return BarnOwl::Message->new(%args);
 }
 
 sub on_msg {
-    my ($self, $evt) = @_;
-    my ($recipient) = $evt->to;
-    my $body = strip_irc_formatting([$evt->args]->[0]);
-    my $nick = $self->nick;
-    $body = '* '.$evt->nick.' '.$body if $evt->type eq 'caction';
+    my ($self, $recipient, $evt) = @_;
+    my $body = strip_irc_formatting($evt->{params}->[1]);
+    $self->handle_message($recipient, $evt, $body);
+}
+
+sub on_ctcp_action {
+    my ($self, $src, $target, $msg) = @_;
+    my $body = strip_irc_formatting($msg);
+    my $evt = {
+        params => [$src],
+        type   => 'privmsg',
+        prefix => $src
+       };
+    $self->handle_message($target, $evt, "* $body");
+}
+
+sub handle_message {
+    my ($self, $recipient, $evt, $body) = @_;
     my $msg = $self->new_message($evt,
         direction   => 'in',
         recipient   => $recipient,
-        body => $body,
-        $evt->type eq 'notice' ?
+        body        => $body,
+        ($evt->{command}||'') eq 'notice' ?
           (notice     => 'true') : (),
         is_private($recipient) ?
           (private  => 'true') : (channel => $recipient),
         replycmd    => BarnOwl::quote('irc-msg', '-a', $self->alias,
-          (is_private($recipient) ? $evt->nick : $recipient)),
-        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, $evt->nick),
+           (is_private($recipient) ? prefix_nick($evt) : $recipient)),
+        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, prefix_nick($evt)),
        );
 
@@ -125,31 +180,27 @@
 }
 
-sub on_ping {
-    my ($self, $evt) = @_;
-    $self->conn->ctcp_reply($evt->nick, join (' ', ($evt->args)));
-}
 
 sub on_admin_msg {
     my ($self, $evt) = @_;
-    return if BarnOwl::Module::IRC->skip_msg($evt->type);
-    BarnOwl::admin_message("IRC",
-            BarnOwl::Style::boldify('IRC ' . $evt->type . ' message from '
+    return if BarnOwl::Module::IRC->skip_msg($evt->{command});
+    BarnOwl::admin_message("IRC",
+            BarnOwl::Style::boldify('IRC ' . $evt->{command} . ' message from '
                 . $self->alias) . "\n"
-            . strip_irc_formatting(join ' ', cdr($evt->args)));
+            . strip_irc_formatting(join ' ', cdr($evt->{params})));
 }
 
 sub on_motdstart {
     my ($self, $evt) = @_;
-    $self->motd(join "\n", cdr($evt->args));
+    $self->motd(join "\n", cdr(@{$evt->{params}}));
 }
 
 sub on_motd {
     my ($self, $evt) = @_;
-    $self->motd(join "\n", $self->motd, cdr($evt->args));
+    $self->motd(join "\n", $self->motd, cdr(@{$evt->{params}}));
 }
 
 sub on_endofmotd {
     my ($self, $evt) = @_;
-    $self->motd(join "\n", $self->motd, cdr($evt->args));
+    $self->motd(join "\n", $self->motd, cdr(@{$evt->{params}}));
     BarnOwl::admin_message("IRC",
             BarnOwl::Style::boldify('MOTD for ' . $self->alias) . "\n"
@@ -159,26 +210,26 @@
 sub on_join {
     my ($self, $evt) = @_;
+    my $chan = $evt->{params}[0];
     my $msg = $self->new_message($evt,
         loginout   => 'login',
         action     => 'join',
-        channel    => $evt->to,
-        replycmd   => BarnOwl::quote('irc-msg', '-a', $self->alias, $evt->to),
-        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, $evt->nick),
+        channel    => $chan,
+        replycmd   => BarnOwl::quote('irc-msg', '-a', $self->alias, $chan),
+        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, prefix_nick($evt)),
         );
     BarnOwl::queue_message($msg);
-    push @{$self->channels}, $evt->to;
 }
 
 sub on_part {
     my ($self, $evt) = @_;
+    my $chan = $evt->{params}[0];
     my $msg = $self->new_message($evt,
         loginout   => 'logout',
         action     => 'part',
-        channel    => $evt->to,
-        replycmd   => BarnOwl::quote('irc-msg', '-a', $self->alias, $evt->to),
-        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, $evt->nick),
+        channel    => $chan,
+        replycmd   => BarnOwl::quote('irc-msg', '-a', $self->alias, $chan),
+        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, prefix_nick($evt)),
         );
     BarnOwl::queue_message($msg);
-    $self->channels([ grep {$_ ne $evt->to} @{$self->channels}]);
 }
 
@@ -188,8 +239,8 @@
         loginout   => 'logout',
         action     => 'quit',
-        from       => $evt->to,
-        reason     => [$evt->args]->[0],
-        replycmd   => BarnOwl::quote('irc-msg', '-a', $self->alias, $evt->nick),
-        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, $evt->nick),
+        from       => $evt->{prefix},
+        reason     => $evt->{params}->[0],
+        replycmd   => BarnOwl::quote('irc-msg', '-a', $self->alias, prefix_nick($evt)),
+        replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias, prefix_nick($evt)),
         );
     BarnOwl::queue_message($msg);
@@ -198,27 +249,24 @@
 sub disconnect {
     my $self = shift;
-    delete $BarnOwl::Module::IRC::ircnets{$self->alias};
-    for my $k (keys %BarnOwl::Module::IRC::channels) {
-        my @conns = grep {$_ ne $self} @{$BarnOwl::Module::IRC::channels{$k}};
-        if(@conns) {
-            $BarnOwl::Module::IRC::channels{$k} = \@conns;
-        } else {
-            delete $BarnOwl::Module::IRC::channels{$k};
-        }
-    }
-    BarnOwl::remove_io_dispatch($self->{FD});
+    $self->conn->disconnect;
+}
+
+sub on_disconnect {
+    my ($self, $why) = @_;
+    BarnOwl::admin_message('IRC',
+                           "[" . $self->alias . "] Disconnected from server: $why");
     $self->motd("");
-}
-
-sub on_disconnect {
-    my ($self, $evt) = @_;
-    $self->disconnect;
-    BarnOwl::admin_message('IRC',
-                           "[" . $self->alias . "] Disconnected from server");
-    if ($evt->format and $evt->format eq "error") {
+    if (!$self->did_quit) {
         $self->schedule_reconnect;
     } else {
-        $self->channels([]);
-    }
+        delete $BarnOwl::Module::IRC::ircnets{$self->alias};
+    }
+}
+
+sub on_error {
+    my ($self, $evt) = @_;
+    BarnOwl::admin_message('IRC',
+                           "[" . $self->alias . "] " .
+                           "Error: " . join(" ", @{$evt->{params}}));
 }
 
@@ -227,17 +275,36 @@
     BarnOwl::admin_message("IRC",
                            "[" . $self->alias . "] " .
-                           [$evt->args]->[1] . ": Nick already in use");
-    $self->disconnect unless $self->motd;
+                           $evt->{params}->[1] . ": Nick already in use");
+}
+
+sub on_nick {
+    my ($self, $old_nick, $new_nick, $is_me) = @_;
+    if ($is_me) {
+        BarnOwl::admin_message("IRC",
+                               "[" . $self->alias . "] " .
+                               "You are now known as $new_nick");
+    } else {
+        my $msg = $self->new_message('',
+            loginout   => 'login',
+            action     => 'nick change',
+            from       => $new_nick,
+            sender     => $new_nick,
+            replycmd   => BarnOwl::quote('irc-msg', '-a', $self->alias,
+                                         $new_nick),
+            replysendercmd => BarnOwl::quote('irc-msg', '-a', $self->alias,
+                                             $new_nick),
+            old_nick   => $old_nick);
+        BarnOwl::queue_message($msg);
+    }
 }
 
 sub on_topic {
-    my ($self, $evt) = @_;
-    my @args = $evt->args;
-    if (scalar @args > 1) {
+    my ($self, $channel, $topic, $who) = @_;
+    if ($channel) {
         BarnOwl::admin_message("IRC",
-                "Topic for $args[1] on " . $self->alias . " is $args[2]");
+                "Topic for $channel on " . $self->alias . " is $topic");
     } else {
         BarnOwl::admin_message("IRC",
-                "Topic changed to $args[0]");
+                "Topic changed to $channel");
     }
 }
@@ -245,5 +312,5 @@
 sub on_topicinfo {
     my ($self, $evt) = @_;
-    my @args = $evt->args;
+    my @args = @{$evt->{params}};
     BarnOwl::admin_message("IRC",
         "Topic for $args[1] set by $args[2] at " . localtime($args[3]));
@@ -257,5 +324,6 @@
     my ($self, $evt) = @_;
     return unless $self->names_tmp;
-    $self->names_tmp([@{$self->names_tmp}, split(' ', [$evt->args]->[3])]);
+    $self->names_tmp([@{$self->names_tmp},
+                      map {prefix_nick($_)} split(' ', $evt->{params}[3])]);
 }
 
@@ -272,5 +340,5 @@
     my ($self, $evt) = @_;
     return unless $self->names_tmp;
-    my $names = BarnOwl::Style::boldify("Members of " . [$evt->args]->[1] . ":\n");
+    my $names = BarnOwl::Style::boldify("Members of " . $evt->{params}->[1] . ":\n");
     for my $name (sort {cmp_user($a, $b)} @{$self->names_tmp}) {
         $names .= "  $name\n";
@@ -282,8 +350,14 @@
 sub on_whois {
     my ($self, $evt) = @_;
+    my %names = (
+        311 => 'user',
+        312 => 'server',
+        319 => 'channels',
+        330 => 'whowas',
+       );
     $self->whois_tmp(
-      $self->whois_tmp . "\n" . $evt->type . ":\n  " .
-      join("\n  ", cdr(cdr($evt->args))) . "\n"
-    );
+        $self->whois_tmp . "\n" . $names{$evt->{command}} . ":\n  " .
+        join("\n  ", cdr(cdr(@{$evt->{params}}))) . "\n"
+       );
 }
 
@@ -291,5 +365,5 @@
     my ($self, $evt) = @_;
     BarnOwl::popless_ztext(
-        BarnOwl::Style::boldify("/whois for " . [$evt->args]->[1] . ":\n") .
+        BarnOwl::Style::boldify("/whois for " . $evt->{params}->[1] . ":\n") .
         $self->whois_tmp
     );
@@ -300,14 +374,15 @@
     my ($self, $evt) = @_;
     BarnOwl::admin_message("IRC",
-                           "[" . $self->alias . "] User " . ($evt->nick) . + " set mode " .
-                           join(" ", $evt->args) . "on " . $evt->to->[0]
+                           "[" . $self->alias . "] User " . (prefix_nick($evt)) . + " set mode " .
+                           join(" ", cdr(@{$evt->{params}})) . " on " . $evt->{params}->[0]
                           );
 }
 
-sub on_nosuchchannel {
-    my ($self, $evt) = @_;
+sub on_nosuch {
+    my ($self, $evt) = @_;
+    my %things = (401 => 'nick', 402 => 'server', 403 => 'channel');
     BarnOwl::admin_message("IRC",
                            "[" . $self->alias . "] " .
-                           "No such channel: " . [$evt->args]->[1])
+                           "No such @{[$things{$evt->{command}}]}: @{[$evt->{params}->[1]]}")
 }
 
@@ -323,7 +398,13 @@
 sub schedule_reconnect {
     my $self = shift;
-    my $interval = shift || 5;
-    delete $BarnOwl::Module::IRC::ircnets{$self->alias};
-    $BarnOwl::Module::IRC::reconnect{$self->alias} = $self;
+    my $interval = $self->backoff;
+    if ($interval) {
+        $interval *= 2;
+        $interval = 60*5 if $interval > 60*5;
+    } else {
+        $interval = 5;
+    }
+    $self->backoff($interval);
+
     my $weak = $self;
     weaken($weak);
@@ -343,9 +424,14 @@
 sub cancel_reconnect {
     my $self = shift;
-    delete $BarnOwl::Module::IRC::reconnect{$self->alias};
+
     if (defined $self->{reconnect_timer}) {
         $self->{reconnect_timer}->stop;
     }
     delete $self->{reconnect_timer};
+}
+
+sub on_connect {
+    my $self = shift;
+    $self->connected("Connected to " . $self->alias . " as " . $self->nick)
 }
 
@@ -355,26 +441,23 @@
     BarnOwl::admin_message("IRC", $msg);
     $self->cancel_reconnect;
-    $BarnOwl::Module::IRC::ircnets{$self->alias} = $self;
-    my $fd = $self->getSocket()->fileno();
-    BarnOwl::add_io_dispatch($fd, 'r', \&BarnOwl::Module::IRC::OwlProcess);
-    $self->{FD} = $fd;
+    if ($self->autoconnect_channels) {
+        for my $c (@{$self->autoconnect_channels}) {
+            $self->conn->send_msg(join => $c);
+        }
+        $self->autoconnect_channels([]);
+    }
+    $self->conn->enable_ping(60, sub {
+                                 $self->on_disconnect("Connection timed out.");
+                                 $self->schedule_reconnect;
+                             });
+    $self->backoff(0);
 }
 
 sub reconnect {
     my $self = shift;
-    my $backoff = shift;
-
-    $self->conn->connect;
-    if ($self->conn->connected) {
-        $self->connected("Reconnected to ".$self->alias);
-        my @channels = @{$self->channels};
-        $self->channels([]);
-        $self->conn->join($_) for @channels;
-        return;
-    }
-
-    $backoff *= 2;
-    $backoff = 60*5 if $backoff > 60*5;
-    $self->schedule_reconnect( $backoff );
+    my $backoff = $self->backoff;
+
+    $self->autoconnect_channels([keys(%{$self->{channel_list}})]);
+    $self->conn->connect(@{$self->connect_args});
 }
 
Index: perl/modules/Jabber/lib/BarnOwl/Message/Jabber.pm
===================================================================
--- perl/modules/Jabber/lib/BarnOwl/Message/Jabber.pm	(revision b401ef2a4168c51ac175a844d6257759ce198efa)
+++ perl/modules/Jabber/lib/BarnOwl/Message/Jabber.pm	(revision a27acf7e0eeeab9f58eed28146c6c99cc161b3b0)
@@ -161,8 +161,9 @@
     my ($recip, $account, $subject) = @_;
     if (defined $recip) {
-        my @cmd = ('jwrite', $recip, '-a', $account);
+        my @cmd = ('jwrite', '-a', $account);
         if (defined $subject) {
             push @cmd, '-s', $subject;
         }
+        push @cmd, '--', $recip;
         return BarnOwl::quote(@cmd);
     } else {
Index: perlconfig.c
===================================================================
--- perlconfig.c	(revision f25df21f6284aedd48c098bc68cee9896da5ce98)
+++ perlconfig.c	(revision a9237aa760c6d3733054c2bbe29c7ae89e81e902)
@@ -403,4 +403,6 @@
   }
 
+  sv_setpv(get_sv("BarnOwl::VERSION", TRUE), OWL_VERSION_STRING);
+
   /* Add the system lib path to @INC */
   inc = get_av("INC", 0);
@@ -465,5 +467,5 @@
                                            :"BarnOwl::_receive_msg_legacy_wrap", m);
   }
-  if (ptr) g_free(ptr);
+  g_free(ptr);
 }
 
@@ -476,5 +478,5 @@
                                            :"BarnOwl::Hooks::_new_msg", m);
   }
-  if (ptr) g_free(ptr);
+  g_free(ptr);
 }
 
@@ -490,5 +492,5 @@
   PUTBACK;
 
-  call_pv("BarnOwl::Hooks::_new_command", G_SCALAR|G_VOID|G_EVAL);
+  call_pv("BarnOwl::Hooks::_new_command", G_VOID|G_EVAL);
 
   SPAGAIN;
Index: perlglue.xs
===================================================================
--- perlglue.xs	(revision 6a7111370ea0e1de2c3eb86a85f34cd68848d4b1)
+++ perlglue.xs	(revision 3b8a563ae4e07b1064fe005714793565ac720dc4)
@@ -57,5 +57,5 @@
 		RETVAL
 	CLEANUP:
-		if (rv) g_free(rv);
+		g_free(rv);
 
 SV *
@@ -114,5 +114,5 @@
 		RETVAL
 	CLEANUP:
-		if (rv) g_free(rv);
+		g_free(rv);
 
 const utf8 *
@@ -141,5 +141,5 @@
 		RETVAL
     CLEANUP:
-		if (rv) g_free(rv);
+		g_free(rv);
 
 void
@@ -324,6 +324,5 @@
 		RETVAL
 	CLEANUP:
-		if (rv)
-			g_free(rv);
+		g_free(rv);
 
 void
Index: select.c
===================================================================
--- select.c	(revision d4927a70549407fe7f56b4ae1d3949663a370205)
+++ select.c	(revision f0781ba39249518a5e78dc0ec433738ef80f581a)
@@ -1,14 +1,13 @@
 #include "owl.h"
 
+static GMainLoop *loop = NULL;
+static GMainContext *main_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) {
   return t1->time - t2->time;
-}
-
-int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
-  return t1 == t2;
 }
 
@@ -43,19 +42,64 @@
 }
 
-void owl_select_process_timers(struct timespec *timeout)
-{
-  time_t now = time(NULL);
-  GList **timers = owl_global_get_timerlist(&g);
-
+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)
+    if(t->time > now.tv_sec)
       break;
 
     /* Reschedule if appropriate */
     if(t->interval > 0) {
-      t->time = now + t->interval;
+      t->time = now.tv_sec + t->interval;
       *timers = g_list_remove(*timers, t);
       *timers = g_list_insert_sorted(*timers, t,
@@ -71,18 +115,16 @@
     }
   }
-
-  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 const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
+  return TRUE;
+}
+
+static GSourceFuncs owl_timer_funcs = {
+  owl_timer_prepare,
+  owl_timer_check,
+  owl_timer_dispatch,
+  NULL
+};
+
+/* Returns the valid owl_io_dispatch for a given file descriptor. */
+static owl_io_dispatch *owl_select_find_valid_io_dispatch_by_fd(const int fd)
 {
   int i, len;
@@ -93,5 +135,5 @@
   for(i = 0; i < len; i++) {
     d = owl_list_get_element(dl, i);
-    if (d->fd == fd) return d;
+    if (d->fd == fd && d->valid) return d;
   }
   return NULL;
@@ -112,4 +154,12 @@
   }
   return -1;
+}
+
+static void owl_select_invalidate_io_dispatch(owl_io_dispatch *d)
+{
+  if (d == NULL || !d->valid)
+    return;
+  d->valid = false;
+  g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
 }
 
@@ -125,4 +175,5 @@
         d->needs_gc = 1;
       else {
+	owl_select_invalidate_io_dispatch(d);
         owl_list_remove_element(dl, elt);
         if (d->destroy)
@@ -134,5 +185,5 @@
 }
 
-void owl_select_io_dispatch_gc(void)
+static void owl_select_io_dispatch_gc(void)
 {
   int i;
@@ -152,5 +203,5 @@
 }
 
-/* Each FD may have at most one dispatcher.
+/* Each FD may have at most one valid dispatcher.
  * If a new dispatch is added for an FD, the old one is removed.
  * mode determines what types of events are watched for, and may be any combination of:
@@ -161,6 +212,8 @@
   owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
   owl_list *dl = owl_global_get_io_dispatch_list(&g);
+  owl_io_dispatch *other;
 
   d->fd = fd;
+  d->valid = true;
   d->needs_gc = 0;
   d->mode = mode;
@@ -169,5 +222,19 @@
   d->data = data;
 
-  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
+  /* 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);
+
+
+  other = owl_select_find_valid_io_dispatch_by_fd(fd);
+  if (other)
+    owl_select_invalidate_io_dispatch(other);
   owl_list_append_element(dl, d);
 
@@ -175,37 +242,39 @@
 }
 
-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;
+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++) {
+    owl_io_dispatch *d = owl_list_get_element(dl, i);
+    if (!d->valid) continue;
+    if (d->pollfd.revents & G_IO_NVAL) {
+      owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
+      owl_select_invalidate_io_dispatch(d);
+    }
+    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++) {
-    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)))) {
+    owl_io_dispatch *d = owl_list_get_element(dl, i);
+    if (!d->valid) continue;
+    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
       d->callback(d, d->data);
     }
@@ -213,21 +282,48 @@
   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
+};
 
 int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
 {
-  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
+  const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
   if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
     /* Don't mess with non-perl dispatch functions from here. */
     return 1;
   }
+  /* Also remove any invalidated perl dispatch functions that may have
+   * stuck around. */
+  owl_select_remove_perl_io_dispatch(fd);
   owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
   return 0;
 }
 
+static owl_io_dispatch *owl_select_find_perl_io_dispatch(int fd)
+{
+  int i, len;
+  const owl_list *dl;
+  owl_io_dispatch *d;
+  dl = owl_global_get_io_dispatch_list(&g);
+  len = owl_list_get_size(dl);
+  for(i = 0; i < len; i++) {
+    d = owl_list_get_element(dl, i);
+    if (d->fd == fd && d->callback == owl_perlconfig_io_dispatch)
+      return d;
+  }
+  return NULL;
+}
+
 int owl_select_remove_perl_io_dispatch(int fd)
 {
-  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
-  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
+  owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
+  if (d != NULL) {
     /* Only remove perl io dispatchers from here. */
     owl_select_remove_io_dispatch(d);
@@ -237,217 +333,61 @@
 }
 
-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();
-  }
+  main_context = g_main_context_default();
+  loop = g_main_loop_new(main_context, FALSE);
+  g_main_loop_run(loop);
 }
 
 void owl_select_quit_loop(void)
 {
-  loop_active = 0;
-}
+  if (loop) {
+    g_main_loop_quit(loop);
+    g_main_loop_unref(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*), GMainContext *context)
+{
+  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: sepbar.c
===================================================================
--- sepbar.c	(revision 044f19f7c86e3f63b353e7fff2865f139319a45a)
+++ sepbar.c	(revision 6eb3ed9ee6c4b94d35628e5b6b2bbaeccca565a9)
@@ -16,4 +16,5 @@
   int x, y, i;
   const char *foo, *appendtosepbar;
+  int cur_numlines, cur_totallines;
 
   ml=owl_global_get_msglist(&g);
@@ -50,12 +51,4 @@
       wattron(sepwin, A_REVERSE);
 
-  if (owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    wattron(sepwin, A_BOLD);
-    waddstr(sepwin, " <truncated> ");
-    wattroff(sepwin, A_BOLD);
-  }
-
   i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
   if ((i != -1) &&
@@ -90,4 +83,14 @@
   }
 
+  if (owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    cur_numlines = owl_global_get_curmsg_vert_offset(&g) + 1;
+    cur_totallines = owl_message_get_numlines(owl_view_get_element(v, owl_global_get_curmsg(&g)));
+    wprintw(sepwin, " <truncated: %d/%d> ", cur_numlines, cur_totallines);
+    wattroff(sepwin, A_BOLD);
+  }
+
   if (owl_global_get_curmsg_vert_offset(&g)) {
     getyx(sepwin, y, x);
Index: signal.c
===================================================================
--- signal.c	(revision 1d21d9fd6efdecb79b0bef96aabee3943d6a955a)
+++ signal.c	(revision 1d21d9fd6efdecb79b0bef96aabee3943d6a955a)
@@ -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: tester.c
===================================================================
--- tester.c	(revision f25df21f6284aedd48c098bc68cee9896da5ce98)
+++ tester.c	(revision e93dd785a2e76aa0dc14ecd56fd2c61b3598bc96)
@@ -32,4 +32,5 @@
   char *perlerr;
   int status = 0;
+  SCREEN *screen;
 
   if (argc <= 1) {
@@ -41,5 +42,5 @@
   wnull = fopen("/dev/null", "w");
   rnull = fopen("/dev/null", "r");
-  newterm("xterm", wnull, rnull);
+  screen = newterm("xterm", wnull, rnull);
   /* initialize global structures */
   owl_global_init(&g);
@@ -91,4 +92,5 @@
   /* probably not necessary, but tear down the screen */
   endwin();
+  delscreen(screen);
   fclose(rnull);
   fclose(wnull);
@@ -218,6 +220,6 @@
   owl_string_appendf_quoted(g, "%q foo %q%q %s %", "hello", "world is", "can't");
   FAIL_UNLESS("owl_string_appendf",
-              !strcmp(g_string_free(g, false),
-                      "hello foo 'world is'\"can't\" %s %"));
+              !strcmp(g->str, "hello foo 'world is'\"can't\" %s %"));
+  g_string_free(g, true);
 
   /* if (numfailed) printf("*** WARNING: failures encountered with owl_util\n"); */
@@ -233,5 +235,5 @@
 
   printf("# BEGIN testing owl_dict\n");
-  FAIL_UNLESS("create", 0==owl_dict_create(&d));
+  owl_dict_create(&d);
   FAIL_UNLESS("insert b", 0==owl_dict_insert_element(&d, "b", bv, owl_dict_noop_delete));
   FAIL_UNLESS("insert d", 0==owl_dict_insert_element(&d, "d", dv, owl_dict_noop_delete));
@@ -249,5 +251,5 @@
   FAIL_UNLESS("get_size", 3==owl_dict_get_size(&d));
   owl_list_create(&l);
-  FAIL_UNLESS("get_keys", 0==owl_dict_get_keys(&d, &l));
+  owl_dict_get_keys(&d, &l);
   FAIL_UNLESS("get_keys result size", 3==owl_list_get_size(&l));
   
Index: text.c
===================================================================
--- text.c	(revision 42ee1befb24d0ac6b39443512cd22be47a782563)
+++ text.c	(revision 7865479440dd042766ecbbc1119b45e5eb7cd6a6)
@@ -275,18 +275,4 @@
   g_strfreev(split);
   return out;
-}
-
-/* replace all instances of character a in buff with the character
- * b.  buff must be null terminated.
- */
-void owl_text_tr(char *buff, char a, char b)
-{
-  int i;
-
-  owl_function_debugmsg("In: %s", buff);
-  for (i=0; buff[i]!='\0'; i++) {
-    if (buff[i]==a) buff[i]=b;
-  }
-  owl_function_debugmsg("Out: %s", buff);
 }
 
Index: util.c
===================================================================
--- util.c	(revision e56303f7b60f05cc30e74453b06725fc031e9384)
+++ util.c	(revision 83a4af3422d232d9ef981a5a009e31549c839a14)
@@ -455,5 +455,5 @@
     g_free(newfile);
     fclose(old);
-    free(actual_filename);
+    g_free(actual_filename);
     return -1;
   }
@@ -466,5 +466,5 @@
     g_free(newfile);
     fclose(old);
-    free(actual_filename);
+    g_free(actual_filename);
     return -1;
   }
@@ -715,4 +715,15 @@
 }
 
+int owl_util_get_colorpairs(void) {
+#ifndef NCURSES_EXT_COLORS
+  /* Without ext-color support (an ABI change), ncurses only supports 256
+   * different color pairs. However, it gives us a larger number even if your
+   * ncurses is compiled without ext-color. */
+  return MIN(COLOR_PAIRS, 256);
+#else
+  return COLOR_PAIRS;
+#endif
+}
+
 gulong owl_dirty_window_on_signal(owl_window *w, gpointer sender, const gchar *detailed_signal)
 {
Index: variable.c
===================================================================
--- variable.c	(revision f203cad04d71e968a11be5e72e6c9d805e02010d)
+++ variable.c	(revision 4c7c21f786a0416a755645b78db0b851f75f8748)
@@ -559,5 +559,5 @@
 int owl_variable_dict_setup(owl_vardict *vd) {
   owl_variable *var, *cur;
-  if (owl_dict_create(vd)) return(-1);
+  owl_dict_create(vd);
   for (var = variables_to_init; var->name != NULL; var++) {
     cur = g_new(owl_variable, 1);
@@ -641,7 +641,7 @@
 
 void owl_variable_update(owl_variable *var, const char *summary, const char *desc) {
-  if(var->summary) g_free(var->summary);
+  g_free(var->summary);
   var->summary = g_strdup(summary);
-  if(var->description) g_free(var->description);
+  g_free(var->description);
   var->description = g_strdup(desc);
 }
@@ -651,5 +651,5 @@
   if(old) {
     owl_variable_update(old, summ, desc);
-    if(old->pval_default) g_free(old->pval_default);
+    g_free(old->pval_default);
     old->pval_default = g_strdup(initval);
   } else {
@@ -944,5 +944,5 @@
 void owl_variable_delete_default(owl_variable *v)
 {
-  if (v->val) g_free(v->val);
+  g_free(v->val);
 }
 
@@ -1076,5 +1076,5 @@
     if (!v->validate_fn(v, newval)) return(-1);
   }
-  if (v->val) g_free(v->val);
+  g_free(v->val);
   v->val = g_strdup(newval);
   return(0);
Index: view.c
===================================================================
--- view.c	(revision d4927a70549407fe7f56b4ae1d3949663a370205)
+++ view.c	(revision 3b8a563ae4e07b1064fe005714793565ac720dc4)
@@ -160,4 +160,4 @@
 {
   owl_list_cleanup(&v->ml.list, NULL);
-  if (v->name) g_free(v->name);
+  g_free(v->name);
 }
Index: window.c
===================================================================
--- window.c	(revision b31f1c9929b526b6d90b18b7847a024ff3d0c77d)
+++ window.c	(revision 4cc49bce5bdc20cd8da2b4e305f60d04ae879d4f)
@@ -524,2 +524,53 @@
   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 we
+   * still overflow, this be useless work. With 8-colors, we get 64
+   * pairs. With 256-colors, we get 32768 pairs with ext-colors
+   * support and 256 otherwise. */
+  cpmgr = owl_global_get_colorpair_mgr(&g);
+  if (cpmgr->overflow) {
+    owl_function_debugmsg("colorpairs: used all %d pairs; reset pairs and redraw.",
+                          owl_util_get_colorpairs());
+    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 28da2daf4548256b44d1fd586200032e96462d57)
+++ window.h	(revision 4cc49bce5bdc20cd8da2b4e305f60d04ae879d4f)
@@ -62,4 +62,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: zcrypt.c
===================================================================
--- zcrypt.c	(revision 1dd285b0045528991a838942c7ba925dbcf64128)
+++ zcrypt.c	(revision 5b197f71370f5a5b3c3c09935c5679d6c90fa4c2)
@@ -11,5 +11,4 @@
 #include <unistd.h>
 #include <sys/types.h>
-#include <zephyr/zephyr.h>
 #include <glib.h>
 #include <string.h>
@@ -476,12 +475,8 @@
 
   for(i = 0; i < MAX_SEARCH; i++) {
-    if(varname[i] != NULL) {
-      g_free(varname[i]);
-    }
-  }
-
-  if(filename != NULL) {
-    g_free(filename);
-  }
+    g_free(varname[i]);
+  }
+
+  g_free(filename);
 
   return keyfile;
@@ -773,5 +768,5 @@
   err = call_filter("gpg", argv, in, &out, &status);
   if(err || status) {
-    if(out) g_free(out);
+    g_free(out);
     return FALSE;
   }
@@ -856,5 +851,5 @@
   err = call_filter("gpg", argv, in, &out, &status);
   if(err || status) {
-    if(out) g_free(out);
+    g_free(out);
     return FALSE;
   }
Index: zephyr.c
===================================================================
--- zephyr.c	(revision f203cad04d71e968a11be5e72e6c9d805e02010d)
+++ zephyr.c	(revision b848e308e9f8f212ddbf787f08111af994000512)
@@ -8,4 +8,10 @@
 
 #ifdef HAVE_LIBZEPHYR
+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);
+
 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;
@@ -417,6 +447,5 @@
     return 0;
   }
-  if (buffer)
-    g_free(buffer);
+  g_free(buffer);
 
   return owl_zephyr_loadsubs_helper(subs, count);
@@ -1422,8 +1451,8 @@
 #define OWL_MAX_ZEPHYRGRAMS_TO_PROCESS 20
 
+#ifdef HAVE_LIBZEPHYR
 static int _owl_zephyr_process_events(void)
 {
   int zpendcount=0;
-#ifdef HAVE_LIBZEPHYR
   ZNotice_t notice;
   Code_t code;
@@ -1466,15 +1495,40 @@
     }
   }
-#endif
   return zpendcount;
 }
 
-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;
+}
+#endif
Index: zwrite.c
===================================================================
--- zwrite.c	(revision 3f52e1446b6b705636f09d02d657c28d38ef29a2)
+++ zwrite.c	(revision 3b8a563ae4e07b1064fe005714793565ac720dc4)
@@ -185,5 +185,5 @@
 void owl_zwrite_set_message_raw(owl_zwrite *z, const char *msg)
 {
-  if (z->message) g_free(z->message);
+  g_free(z->message);
   z->message = owl_validate_utf8(msg);
 }
@@ -195,5 +195,5 @@
   char *tmp = NULL, *tmp2;
 
-  if (z->message) g_free(z->message);
+  g_free(z->message);
 
   j=owl_list_get_size(&(z->recips));
@@ -289,5 +289,5 @@
 void owl_zwrite_set_opcode(owl_zwrite *z, const char *opcode)
 {
-  if (z->opcode) g_free(z->opcode);
+  g_free(z->opcode);
   z->opcode=owl_validate_utf8(opcode);
 }
@@ -306,5 +306,5 @@
 void owl_zwrite_set_zsig(owl_zwrite *z, const char *zsig)
 {
-  if(z->zsig) g_free(z->zsig);
+  g_free(z->zsig);
   z->zsig = g_strdup(zsig);
 }
@@ -353,12 +353,12 @@
 {
   owl_list_cleanup(&(z->recips), &g_free);
-  if (z->cmd) g_free(z->cmd);
-  if (z->zwriteline) g_free(z->zwriteline);
-  if (z->class) g_free(z->class);
-  if (z->inst) g_free(z->inst);
-  if (z->opcode) g_free(z->opcode);
-  if (z->realm) g_free(z->realm);
-  if (z->message) g_free(z->message);
-  if (z->zsig) g_free(z->zsig);
+  g_free(z->cmd);
+  g_free(z->zwriteline);
+  g_free(z->class);
+  g_free(z->inst);
+  g_free(z->opcode);
+  g_free(z->realm);
+  g_free(z->message);
+  g_free(z->zsig);
 }
 
