Index: src/progress.c
===================================================================
--- src/progress.c	(revision 1396)
+++ src/progress.c	(working copy)
@@ -35,7 +35,6 @@
 
 static GladeXML *glade_dialog;
 static ZenityData *zen_data;
-static GIOChannel *channel;
 
 static gboolean autokill;
 
@@ -51,126 +50,99 @@
   return TRUE;
 }
 
-static gboolean
-zenity_progress_handle_stdin (GIOChannel   *channel,
-                              GIOCondition  condition,
-                              gpointer      data)
+static gint pulsate_timeout = -1;
+
+static void
+zenity_progress_stdin_handle_line (gpointer data,
+                                   GString *string)
 {
-  static ZenityProgressData *progress_data;
-  static GtkWidget *progress_bar;
-  static GtkWidget *progress_label;
-  static gint pulsate_timeout = -1;
+  ZenityProgressData *progress_data;
+  GtkWidget *progress_bar;
+  GtkWidget *progress_label;
   float percentage = 0.0;
   
   progress_data = (ZenityProgressData *) data;
   progress_bar = glade_xml_get_widget (glade_dialog, "zenity_progress_bar");
   progress_label = glade_xml_get_widget (glade_dialog, "zenity_progress_text");
 
-  if ((condition == G_IO_IN) || (condition == G_IO_IN + G_IO_HUP)) {
-    GString *string;
-    GError *error = NULL;
+  if (progress_data->pulsate) {
+    if (pulsate_timeout == -1)
+      pulsate_timeout = g_timeout_add (100, zenity_progress_pulsate_progress_bar, progress_bar);
+  }
 
-    string = g_string_new (NULL);
+  if (!g_ascii_strncasecmp (string->str, "#", 1)) {
+    gchar *match;
 
-    if (progress_data->pulsate) {
-      if (pulsate_timeout == -1)
-        pulsate_timeout = g_timeout_add (100, zenity_progress_pulsate_progress_bar, progress_bar);
-    }
+    /* We have a comment, so let's try to change the label */
+    match = g_strstr_len (string->str, strlen (string->str), "#");
+    match++;
+    gtk_label_set_text (GTK_LABEL (progress_label), g_strcompress(g_strchomp (g_strchug (match))));
+  } else {
 
-    while (channel->is_readable != TRUE)
-      ;
-    do {
-      gint status;
+    if (!g_ascii_isdigit (*(string->str)))
+      return;
 
-      do {
-        status = g_io_channel_read_line_string (channel, string, NULL, &error);
-
-        while (gtk_events_pending ())
-          gtk_main_iteration ();
-
-      } while (status == G_IO_STATUS_AGAIN);
-
-      if (status != G_IO_STATUS_NORMAL) {
-        if (error) {
-          g_warning ("zenity_progress_handle_stdin () : %s", error->message);
-          g_error_free (error);
-          error = NULL;
-        }
-        continue;
+    /* Now try to convert the thing to a number */
+    percentage = atoi (string->str);
+    if (percentage >= 100) {
+      GtkWidget *button;
+      button = glade_xml_get_widget( glade_dialog,"zenity_progress_ok_button");
+      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), 1.0);
+      gtk_widget_set_sensitive(GTK_WIDGET (button), TRUE);
+      gtk_widget_grab_focus(GTK_WIDGET (button));
+      if (progress_data->autoclose) {
+            zen_data->exit_code = zenity_util_return_exit_code (ZENITY_OK);
+            gtk_main_quit();
+            
       }
+    } else
+      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), percentage / 100.0);
+  }
+}
 
-      if (!g_ascii_strncasecmp (string->str, "#", 1)) {
-        gchar *match;
+static void
+zenity_progress_stdin_finish (gpointer data)
+{
+  ZenityProgressData *progress_data;
+  GtkWidget *progress_bar;
 
-        /* We have a comment, so let's try to change the label */
-        match = g_strstr_len (string->str, strlen (string->str), "#");
-        match++;
-        gtk_label_set_text (GTK_LABEL (progress_label), g_strcompress(g_strchomp (g_strchug (match))));
-      } else {
+  progress_data = (ZenityProgressData *) data;
+  progress_bar = glade_xml_get_widget (glade_dialog, "zenity_progress_bar");
 
-        if (!g_ascii_isdigit (*(string->str)))
-          continue;
+  /* We assume that we are done, so stop the pulsating and de-sensitize the buttons */
+  GtkWidget *button;
 
-        /* Now try to convert the thing to a number */
-        percentage = atoi (string->str);
-        if (percentage >= 100) {
-	  GtkWidget *button;
-	  button = glade_xml_get_widget( glade_dialog,"zenity_progress_ok_button");
-          gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), 1.0);
-	  gtk_widget_set_sensitive(GTK_WIDGET (button), TRUE);
-	  gtk_widget_grab_focus(GTK_WIDGET (button));
-	  if (progress_data->autoclose) {
-		zen_data->exit_code = zenity_util_return_exit_code (ZENITY_OK);
-		gtk_main_quit();
-		
-	  }
-	} else
-          gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), percentage / 100.0);
-      }
+  button = glade_xml_get_widget (glade_dialog, "zenity_progress_ok_button");
+  gtk_widget_set_sensitive (button, TRUE);
+  gtk_widget_grab_focus (button);
 
-    } while (g_io_channel_get_buffer_condition (channel) == G_IO_IN);
-    g_string_free (string, TRUE);
+  button = glade_xml_get_widget (glade_dialog, "zenity_progress_cancel_button");
+  gtk_widget_set_sensitive (button, FALSE);
+              
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), 1.0);
+
+  if (progress_data->pulsate) {
+    g_source_remove (pulsate_timeout);
+    pulsate_timeout = -1;
   }
 
-  if (condition != G_IO_IN) {
-    /* We assume that we are done, so stop the pulsating and de-sensitize the buttons */
-    GtkWidget *button;
+  if (glade_dialog)
+    g_object_unref (glade_dialog);
 
-    button = glade_xml_get_widget (glade_dialog, "zenity_progress_ok_button");
-    gtk_widget_set_sensitive (button, TRUE);
-    gtk_widget_grab_focus (button);
-
-    button = glade_xml_get_widget (glade_dialog, "zenity_progress_cancel_button");
-    gtk_widget_set_sensitive (button, FALSE);
-		
-    gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), 1.0);
-
-    if (progress_data->pulsate) {
-      g_source_remove (pulsate_timeout);
-      pulsate_timeout = -1;
-    }
-
-    if (glade_dialog)
-      g_object_unref (glade_dialog);
-
-    if (progress_data->autoclose) {
-      zen_data->exit_code = zenity_util_return_exit_code (ZENITY_OK);
-      gtk_main_quit();
-    }
-		
-    g_io_channel_shutdown (channel, TRUE, NULL);
-    return FALSE;
+  if (progress_data->autoclose) {
+    zen_data->exit_code = zenity_util_return_exit_code (ZENITY_OK);
+    gtk_main_quit();
   }
-  return TRUE;
 }
 
 static void
 zenity_progress_read_info (ZenityProgressData *progress_data)
 {
-  channel = g_io_channel_unix_new (0);
-  g_io_channel_set_encoding (channel, NULL, NULL);
-  g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
-  g_io_add_watch (channel, G_IO_IN | G_IO_HUP, zenity_progress_handle_stdin, progress_data);
+  ZenityStdinHandler *handler = g_new(ZenityStdinHandler, 1);
+  handler->handle_line = zenity_progress_stdin_handle_line;
+  handler->finish = zenity_progress_stdin_finish;
+  handler->data = progress_data;
+  zenity_util_stdin_init(handler);
 }
 
 void
Index: src/tree.c
===================================================================
--- src/tree.c	(revision 1396)
+++ src/tree.c	(working copy)
@@ -87,19 +87,18 @@
   gtk_tree_path_free (path);
 }
 
-static gboolean
-zenity_tree_handle_stdin (GIOChannel  *channel,
-                          GIOCondition condition,
-                          gpointer     data)
+static void
+zenity_tree_stdin_handle_line (gpointer data,
+                               GString *string)
 {
-  static GtkTreeView *tree_view;
+  GtkTreeView *tree_view;
+  gint n_columns;
+  gboolean editable;
+  gboolean toggles;
   GtkTreeModel *model;
   static GtkTreeIter iter;
   static gint column_count = 0;
   static gint row_count = 0;
-  static gint n_columns;
-  static gboolean editable;
-  static gboolean toggles;
   static gboolean first_time = TRUE;
 
   tree_view = GTK_TREE_VIEW (data);
@@ -114,95 +113,62 @@
     gtk_list_store_append (GTK_LIST_STORE (model), &iter);
   }
 
-  if ((condition == G_IO_IN) || (condition == G_IO_IN + G_IO_HUP)) {
-    GString *string;
-    GError *error = NULL;
+  if (column_count == n_columns) {
+    /* We're starting a new row */
+    column_count = 0;
+    row_count++;
+    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+  }
 
-    string = g_string_new (NULL);
+  if (toggles && column_count == 0) {
+    if (strcmp (g_strdown (zenity_util_strip_newline (string->str)), "true") == 0)
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter, column_count, TRUE, -1);
+    else
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter, column_count, FALSE, -1);
+  }
+  else {
+    gtk_list_store_set (GTK_LIST_STORE (model), &iter, column_count, zenity_util_strip_newline (string->str), -1);        
+  }
 
-    while (channel->is_readable != TRUE)
-      ;
-    do {
-      gint status;
+  if (editable) {
+    gtk_list_store_set (GTK_LIST_STORE (model), &iter, n_columns, TRUE, -1);
+  }
 
-      do {
-        status = g_io_channel_read_line_string (channel, string, NULL, &error);
+  if (row_count == MAX_ELEMENTS_BEFORE_SCROLLING) {
+    GtkWidget *scrolled_window;
+    GtkRequisition rectangle;
 
-        while (gtk_events_pending ())
-          gtk_main_iteration ();
-
-      } while (status == G_IO_STATUS_AGAIN);
-
-      if (status != G_IO_STATUS_NORMAL) {
-        if (error) {
-          g_warning ("zenity_tree_handle_stdin () : %s", error->message);
-          g_error_free (error);
-          error = NULL;
-        }
-        continue;
-      }
-    
-      if (column_count == n_columns) {
-        /* We're starting a new row */
-        column_count = 0;
-        row_count++;
-        gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-      }
-
-      if (toggles && column_count == 0) {
-        if (strcmp (g_strdown (zenity_util_strip_newline (string->str)), "true") == 0)
-          gtk_list_store_set (GTK_LIST_STORE (model), &iter, column_count, TRUE, -1);
-        else
-          gtk_list_store_set (GTK_LIST_STORE (model), &iter, column_count, FALSE, -1);
-      }
-      else {
-        gtk_list_store_set (GTK_LIST_STORE (model), &iter, column_count, zenity_util_strip_newline (string->str), -1);        
-      }
-
-      if (editable) {
-        gtk_list_store_set (GTK_LIST_STORE (model), &iter, n_columns, TRUE, -1);
-      }
-
-      if (row_count == MAX_ELEMENTS_BEFORE_SCROLLING) {
-        GtkWidget *scrolled_window;
-        GtkRequisition rectangle;
-
-        gtk_widget_size_request (GTK_WIDGET (tree_view), &rectangle);
-        scrolled_window = glade_xml_get_widget (glade_dialog, "zenity_tree_window");
-        gtk_widget_set_size_request (scrolled_window, -1, rectangle.height);
-        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
-                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-      }
-
-      column_count++;
-
-    } while (g_io_channel_get_buffer_condition (channel) == G_IO_IN); 
-    g_string_free (string, TRUE);
+    gtk_widget_size_request (GTK_WIDGET (tree_view), &rectangle);
+    scrolled_window = glade_xml_get_widget (glade_dialog, "zenity_tree_window");
+    gtk_widget_set_size_request (scrolled_window, -1, rectangle.height);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   }
 
-  if (condition != G_IO_IN) {
-    g_io_channel_shutdown (channel, TRUE, NULL);
-    return FALSE;
-  }
-  return TRUE;
+  column_count++;
 }
 
 static void
+zenity_tree_stdin_finish (gpointer data)
+{
+  /* Nothing needs to be done here. */
+}
+
+static void
 zenity_tree_fill_entries_from_stdin (GtkTreeView  *tree_view,
                                      gint          n_columns,
                                      gboolean      toggles,
                                      gboolean      editable)
 {
-  GIOChannel *channel;
-
   g_object_set_data (G_OBJECT (tree_view), "n_columns", GINT_TO_POINTER (n_columns));
   g_object_set_data (G_OBJECT (tree_view), "toggles", GINT_TO_POINTER (toggles));
   g_object_set_data (G_OBJECT (tree_view), "editable", GINT_TO_POINTER (editable)); 
 
-  channel = g_io_channel_unix_new (0);
-  g_io_channel_set_encoding (channel, NULL, NULL);
-  g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
-  g_io_add_watch (channel, G_IO_IN | G_IO_HUP, zenity_tree_handle_stdin, tree_view);
+  ZenityStdinHandler *handler = g_new(ZenityStdinHandler, 1);
+  handler->handle_line = zenity_tree_stdin_handle_line;
+  handler->finish = zenity_tree_stdin_finish;
+  handler->data = tree_view;
+  zenity_util_stdin_init(handler);
 }
 
 static void
Index: src/util.c
===================================================================
--- src/util.c	(revision 1396)
+++ src/util.c	(working copy)
@@ -37,6 +37,7 @@
 #include "config.h"
 #include "util.h"
 #include "zenity.h"
+#include <glib/gmain.h>
 
 #ifdef GDK_WINDOWING_X11
 #include <gdk/gdkx.h>
@@ -380,3 +381,60 @@
   exit(ZENITY_TIMEOUT);
   return FALSE;
 }
+
+static gboolean
+zenity_util_stdin_io_func (GIOChannel   *channel,
+                           GIOCondition  condition,
+                           gpointer      data)
+{
+  ZenityStdinHandler *handler = (ZenityStdinHandler *) data;
+  GString *string;
+  GError *error = NULL;
+  gint status;
+
+  /* Read and handle a line, if possible. */
+  string = g_string_new (NULL);
+  status = g_io_channel_read_line_string (channel, string, NULL, &error);
+  if (status == G_IO_STATUS_NORMAL)
+    handler->handle_line(handler->data, string);
+  g_string_free (string, TRUE);
+
+  if (status == G_IO_STATUS_NORMAL)
+    /* We handled one line.  Give other events a chance, and then call us again
+     * if there's more input. */
+    return TRUE;
+  else if (status == G_IO_STATUS_AGAIN) {
+    /* We did all we could with the currently available input.  Don't call this
+     * function again until more input becomes available. */
+    g_io_watch_set_ignore_read_buffer(handler->source, TRUE);
+    return TRUE;
+  } else {
+    /* Otherwise we have error or EOF, so finish. */
+    if (error) {
+      g_warning ("Error reading from stdin: %s", error->message);
+      g_error_free (error);
+    }
+    handler->finish(handler->data);
+    g_free(handler);
+    g_io_channel_shutdown (channel, TRUE, NULL);
+    return FALSE;
+  }
+}
+
+void
+zenity_util_stdin_init (ZenityStdinHandler *handler)
+{
+  GIOChannel *channel;
+  guint id;
+
+  channel = g_io_channel_unix_new (0);
+  g_io_channel_set_encoding (channel, NULL, NULL);
+  /* If we read a partial line, we must not block waiting for the rest. */
+  g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
+  /* G_IO_IN | G_IO_HUP ensures that zenity_stdin_io_func is called whenever
+   * there's data (G_IO_IN) or EOF (G_IO_HUP for pipes, G_IO_IN for regular
+   * files).  But instead of testing the condition, we just attempt a read and
+   * see whether it gave EOF. */
+  id = g_io_add_watch (channel, G_IO_IN | G_IO_HUP, zenity_util_stdin_io_func, handler);
+  handler->source = g_main_context_find_source_by_id (NULL, id);
+}
Index: src/util.h
===================================================================
--- src/util.h	(revision 1396)
+++ src/util.h	(working copy)
@@ -30,6 +30,19 @@
 
 gboolean        zenity_util_timeout_handle                (void);
 
+/* Generalized stdin-handling mechanism used by progress and tree modes.
+ * Allocate a ZenityStdinHandler, fill the first three fields, and pass it to
+ * zenity_util_stdin_init. */
+typedef struct {
+  /* Function to handle a line of input (including any trailing newline). */
+  void   (*handle_line)(gpointer data, GString *string);
+  /* Function called when the input is finished (EOF or error). */
+  void   (*finish)     (gpointer data);
+  gpointer data;          /* Passed to handle_line and finish. */
+  GSource *source;    /* Filled by zenity_util_stdin_init. */
+} ZenityStdinHandler;
+void            zenity_util_stdin_init                    (ZenityStdinHandler *handler);
+
 G_END_DECLS
 
 #endif /* UTIL_H */
