[gtk+] Revamp and modernize X error traps



commit b837ef5a6d2ce003eae3dd558ac1ac9934e9d72c
Author: Havoc Pennington <hp pobox com>
Date:   Sat Sep 18 18:19:27 2010 -0400

    Revamp and modernize X error traps
    
    * add per-display gdk_x11_display_error_trap_push()
      (X11-specific because gdk_error_trap_push() probably
      should have been)
    * make gdk_error_trap_push() handle only GDK displays
      not displays opened without a GDK wrapper
    * make gdk_error_trap_pop() and gdk_x11_display_error_trap_pop()
      automatically sync only if needed, so manual gdk_flush() is not
      required
    * add gdk_error_trap_pop_ignored() which just asynchronously
      ignores errors, so never needs to sync
    * add G_GNUC_WARN_UNUSED_RESULT to plain pop(), because
      if you use plain pop() and don't need the return value,
      the async gdk_error_trap_pop_ignored() should be used
      instead. This results in lots of warnings to clean
      up in a later patch.
    
    The main objective here was to avoid the need to sync just
    to ignore an error. Now, syncing is automatic, and only
    happens when we need to know the error code.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=629608

 docs/reference/gdk/tmpl/general.sgml |   36 -----
 gdk/gdk.h                            |    7 +-
 gdk/gdkglobals.c                     |    2 -
 gdk/gdkinternals.h                   |    2 -
 gdk/quartz/gdkmain-quartz.c          |    5 +
 gdk/win32/gdkmain-win32.c            |    5 +
 gdk/x11/gdkdisplay-x11.c             |  265 +++++++++++++++++++++++++++++++
 gdk/x11/gdkdisplay-x11.h             |    8 +-
 gdk/x11/gdkmain-x11.c                |  284 +++++++++++++++++++++++-----------
 gdk/x11/gdkprivate-x11.h             |    2 +
 gdk/x11/gdkx.h                       |    6 +
 11 files changed, 489 insertions(+), 133 deletions(-)
---
diff --git a/docs/reference/gdk/tmpl/general.sgml b/docs/reference/gdk/tmpl/general.sgml
index a2aaf3d..1b8080b 100644
--- a/docs/reference/gdk/tmpl/general.sgml
+++ b/docs/reference/gdk/tmpl/general.sgml
@@ -326,42 +326,6 @@ available.
 
 @void: 
 
-
-<!-- ##### FUNCTION gdk_error_trap_push ##### -->
-<para>
-This function allows X errors to be trapped instead of the normal behavior
-of exiting the application. It should only be used if it is not possible to
-avoid the X error in any other way.
-</para>
-<example>
-<title>Trapping an X error</title>
-<programlisting>
-  gdk_error_trap_push (<!-- -->);
-
-  /* ... Call the X function which may cause an error here ... */
-
-  /* Flush the X queue to catch errors now. */
-  gdk_flush (<!-- -->);
-
-  if (gdk_error_trap_pop (<!-- -->))
-    {
-      /* ... Handle the error here ... */
-    }
-</programlisting>
-</example>
-
- void: 
-
-
-<!-- ##### FUNCTION gdk_error_trap_pop ##### -->
-<para>
-Removes the X error trap installed with gdk_error_trap_push().
-</para>
-
- void: 
- Returns: the X error code, or 0 if no error occurred.
-
-
 <!-- ##### MACRO GDK_WINDOWING_X11 ##### -->
 <para>
 This macro is defined if GDK is configured to use the X11 backend.
diff --git a/gdk/gdk.h b/gdk/gdk.h
index a6c7578..ea7926a 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -81,8 +81,11 @@ void                 gdk_set_program_class (const char *program_class);
 
 /* Push and pop error handlers for X errors
  */
-void      gdk_error_trap_push           (void);
-gint      gdk_error_trap_pop            (void);
+void                           gdk_error_trap_push        (void);
+/* warn unused because you could use pop_ignored otherwise */
+G_GNUC_WARN_UNUSED_RESULT gint gdk_error_trap_pop         (void);
+void                           gdk_error_trap_pop_ignored (void);
+
 
 gchar*	                  gdk_get_display		(void);
 G_CONST_RETURN gchar*	  gdk_get_display_arg_name	(void);
diff --git a/gdk/gdkglobals.c b/gdk/gdkglobals.c
index b9d86e1..fc73495 100644
--- a/gdk/gdkglobals.c
+++ b/gdk/gdkglobals.c
@@ -33,8 +33,6 @@
 
 
 guint               _gdk_debug_flags = 0;
-gint                _gdk_error_code = 0;
-gint                _gdk_error_warnings = TRUE;
 GList              *_gdk_default_filters = NULL;
 gchar              *_gdk_display_name = NULL;
 gint                _gdk_screen_number = -1;
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index 4f4aa1d..7740bce 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -88,8 +88,6 @@ typedef enum {
 
 extern GList            *_gdk_default_filters;
 extern GdkWindow  	*_gdk_parent_root;
-extern gint		 _gdk_error_code;
-extern gint		 _gdk_error_warnings;
 
 extern guint _gdk_debug_flags;
 extern gboolean _gdk_native_windows;
diff --git a/gdk/quartz/gdkmain-quartz.c b/gdk/quartz/gdkmain-quartz.c
index af765d0..6e84756 100644
--- a/gdk/quartz/gdkmain-quartz.c
+++ b/gdk/quartz/gdkmain-quartz.c
@@ -56,6 +56,11 @@ gdk_error_trap_pop (void)
   return 0;
 }
 
+void
+gdk_error_trap_pop_ignored (void)
+{
+}
+
 gchar *
 gdk_get_display (void)
 {
diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c
index 02ab4ea..cc8c6fc 100644
--- a/gdk/win32/gdkmain-win32.c
+++ b/gdk/win32/gdkmain-win32.c
@@ -215,6 +215,11 @@ gdk_error_trap_pop (void)
 }
 
 void
+gdk_error_trap_pop_ignored (void)
+{
+}
+
+void
 gdk_notify_startup_complete (void)
 {
 }
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 31f9ec1..9ad4178 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -24,6 +24,7 @@
 
 #include "config.h"
 
+#include <glib/gprintf.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -67,6 +68,23 @@
 #include <X11/extensions/Xrandr.h>
 #endif
 
+typedef struct _GdkErrorTrap  GdkErrorTrap;
+
+struct _GdkErrorTrap
+{
+  /* Next sequence when trap was pushed, i.e. first sequence to
+   * ignore
+   */
+  gulong start_sequence;
+
+  /* Next sequence when trap was popped, i.e. first sequence
+   * to not ignore. 0 if trap is still active.
+   */
+  gulong end_sequence;
+
+  /* Most recent error code within the sequence */
+  int error_code;
+};
 
 static void   gdk_display_x11_dispose            (GObject            *object);
 static void   gdk_display_x11_finalize           (GObject            *object);
@@ -2657,3 +2675,250 @@ gdk_x11_register_standard_event_type (GdkDisplay *display,
 
   display_x11->event_types = g_slist_prepend (display_x11->event_types, event_type);
 }
+
+/* compare X sequence numbers handling wraparound */
+#define SEQUENCE_COMPARE(a,op,b) (((long) (a) - (long) (b)) op 0)
+
+/* delivers an error event from the error handler in gdkmain-x11.c */
+void
+_gdk_x11_display_error_event (GdkDisplay  *display,
+                              XErrorEvent *error)
+{
+  GdkDisplayX11 *display_x11;
+  GSList *tmp_list;
+  gboolean ignore;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  ignore = FALSE;
+  for (tmp_list = display_x11->error_traps;
+       tmp_list != NULL;
+       tmp_list = tmp_list->next)
+    {
+      GdkErrorTrap *trap;
+
+      trap = tmp_list->data;
+
+      if (SEQUENCE_COMPARE (trap->start_sequence, <=, error->serial) &&
+          (trap->end_sequence == 0 ||
+           SEQUENCE_COMPARE (trap->end_sequence, >, error->serial)))
+        {
+          ignore = TRUE;
+          trap->error_code = error->error_code;
+        }
+    }
+
+  if (!ignore)
+    {
+      gchar buf[64];
+      gchar *msg;
+
+      XGetErrorText (display_x11->xdisplay, error->error_code, buf, 63);
+
+      msg =
+        g_strdup_printf ("The program '%s' received an X Window System error.\n"
+                         "This probably reflects a bug in the program.\n"
+                         "The error was '%s'.\n"
+                         "  (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
+                         "  (Note to programmers: normally, X errors are reported asynchronously;\n"
+                         "   that is, you will receive the error a while after causing it.\n"
+                         "   To debug your program, run it with the --sync command line\n"
+                         "   option to change this behavior. You can then get a meaningful\n"
+                         "   backtrace from your debugger if you break on the gdk_x_error() function.)",
+                         g_get_prgname (),
+                         buf,
+                         error->serial,
+                         error->error_code,
+                         error->request_code,
+                         error->minor_code);
+
+#ifdef G_ENABLE_DEBUG
+      g_error ("%s", msg);
+#else /* !G_ENABLE_DEBUG */
+      g_fprintf (stderr, "%s\n", msg);
+
+      exit (1);
+#endif /* G_ENABLE_DEBUG */
+    }
+}
+
+static void
+delete_outdated_error_traps (GdkDisplayX11 *display_x11)
+{
+  GSList *tmp_list;
+  gulong processed_sequence;
+
+  processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
+
+  tmp_list = display_x11->error_traps;
+  while (tmp_list != NULL)
+    {
+      GdkErrorTrap *trap = tmp_list->data;
+
+      if (trap->end_sequence != 0 &&
+          SEQUENCE_COMPARE (trap->end_sequence, <, processed_sequence))
+        {
+          GSList *free_me = tmp_list;
+
+          tmp_list = tmp_list->next;
+          display_x11->error_traps =
+            g_slist_delete_link (display_x11->error_traps, free_me);
+          g_slice_free (GdkErrorTrap, trap);
+        }
+      else
+        {
+          tmp_list = tmp_list->next;
+        }
+    }
+}
+
+/**
+ * gdk_x11_display_error_trap_push:
+ *
+ * Begins a range of X requests for which X error events will be
+ * ignored. Unignored errors (when no trap is pushed) will abort the
+ * application.
+ *
+ * See also gdk_error_trap_push() to push a trap on all displays.
+ *
+ * Since: 3.0
+ */
+void
+gdk_x11_display_error_trap_push (GdkDisplay *display)
+{
+  GdkDisplayX11 *display_x11;
+  GdkErrorTrap *trap;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  delete_outdated_error_traps (display_x11);
+
+  /* set up the Xlib callback to tell us about errors */
+  _gdk_x11_error_handler_push ();
+
+  trap = g_slice_new0 (GdkErrorTrap);
+
+  trap->start_sequence = XNextRequest (display_x11->xdisplay);
+  trap->error_code = Success;
+
+  display_x11->error_traps =
+    g_slist_prepend (display_x11->error_traps, trap);
+}
+
+static gint
+gdk_x11_display_error_trap_pop_internal (GdkDisplay *display,
+                                         gboolean    need_code)
+{
+  GdkDisplayX11 *display_x11;
+  GdkErrorTrap *trap;
+  GSList *tmp_list;
+  int result;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  g_return_val_if_fail (display_x11->error_traps != NULL, Success);
+
+  /* Find the first trap that hasn't been popped already */
+  trap = NULL; /* quiet gcc */
+  for (tmp_list = display_x11->error_traps;
+       tmp_list != NULL;
+       tmp_list = tmp_list->next)
+    {
+      trap = tmp_list->data;
+
+      if (trap->end_sequence == 0)
+        break;
+    }
+
+  g_return_val_if_fail (trap != NULL, Success);
+  g_assert (trap->end_sequence == 0);
+
+  /* May need to sync to fill in trap->error_code if we care about
+   * getting an error code.
+   */
+  if (need_code)
+    {
+      gulong processed_sequence;
+      gulong next_sequence;
+
+      next_sequence = XNextRequest (display_x11->xdisplay);
+      processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
+
+      /* If our last request was already processed, there is no point
+       * in syncing. i.e. if last request was a round trip (or even if
+       * we got an event with the serial of a non-round-trip)
+       */
+      if ((next_sequence - 1) != processed_sequence)
+        {
+          XSync (display_x11->xdisplay, False);
+        }
+
+      result = trap->error_code;
+    }
+  else
+    {
+      result = Success;
+    }
+
+  /* record end of trap, giving us a range of
+   * error sequences we'll ignore.
+   */
+  trap->end_sequence = XNextRequest (display_x11->xdisplay);
+
+  /* remove the Xlib callback */
+  _gdk_x11_error_handler_pop ();
+
+  /* we may already be outdated */
+  delete_outdated_error_traps (display_x11);
+
+  return result;
+}
+
+/**
+ * gdk_x11_display_error_trap_pop:
+ * @display: the display
+ *
+ * Pops the error trap pushed by gdk_x11_display_error_trap_push().
+ * Will XSync() if necessary and will always block until
+ * the error is known to have occurred or not occurred,
+ * so the error code can be returned.
+ *
+ * If you don't need to use the return value,
+ * gdk_x11_display_error_trap_pop_ignored() would be more efficient.
+ *
+ * See gdk_error_trap_pop() for the all-displays-at-once
+ * equivalent.
+ *
+ * Since: 3.0
+ *
+ * Return value: X error code or 0 on success
+ */
+gint
+gdk_x11_display_error_trap_pop (GdkDisplay *display)
+{
+  g_return_val_if_fail (GDK_IS_DISPLAY_X11 (display), Success);
+
+  return gdk_x11_display_error_trap_pop_internal (display, TRUE);
+}
+
+/**
+ * gdk_x11_display_error_trap_pop_ignored:
+ * @display: the display
+ *
+ * Pops the error trap pushed by gdk_x11_display_error_trap_push().
+ * Does not block to see if an error occurred; merely records the
+ * range of requests to ignore errors for, and ignores those errors
+ * if they arrive asynchronously.
+ *
+ * See gdk_error_trap_pop_ignored() for the all-displays-at-once
+ * equivalent.
+ *
+ * Since: 3.0
+ */
+void
+gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display)
+{
+  g_return_if_fail (GDK_IS_DISPLAY_X11 (display));
+
+  gdk_x11_display_error_trap_pop_internal (display, FALSE);
+}
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index 87496d4..b4036cb 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -141,6 +141,8 @@ struct _GdkDisplayX11
 
   /* The offscreen window that has the pointer in it (if any) */
   GdkWindow *active_offscreen_window;
+
+  GSList *error_traps;
 };
 
 struct _GdkDisplayX11Class
@@ -149,8 +151,10 @@ struct _GdkDisplayX11Class
 };
 
 GType      _gdk_display_x11_get_type            (void);
-GdkScreen *_gdk_x11_display_screen_for_xrootwin (GdkDisplay *display,
-						 Window      xrootwin);
+GdkScreen *_gdk_x11_display_screen_for_xrootwin (GdkDisplay  *display,
+						 Window       xrootwin);
+void       _gdk_x11_display_error_event         (GdkDisplay  *display,
+                                                 XErrorEvent *error);
 
 G_END_DECLS
 
diff --git a/gdk/x11/gdkmain-x11.c b/gdk/x11/gdkmain-x11.c
index 238c52e..996715c 100644
--- a/gdk/x11/gdkmain-x11.c
+++ b/gdk/x11/gdkmain-x11.c
@@ -52,8 +52,8 @@
 
 #include <gdk/gdkdeviceprivate.h>
 
-typedef struct _GdkPredicate  GdkPredicate;
-typedef struct _GdkErrorTrap  GdkErrorTrap;
+typedef struct _GdkPredicate        GdkPredicate;
+typedef struct _GdkGlobalErrorTrap  GdkGlobalErrorTrap;
 
 struct _GdkPredicate
 {
@@ -61,11 +61,14 @@ struct _GdkPredicate
   gpointer data;
 };
 
-struct _GdkErrorTrap
+/* non-GDK previous error handler */
+static int (*_gdk_old_error_handler) (Display *, XErrorEvent *);
+/* number of times we've pushed the GDK error handler */
+static int _gdk_error_handler_push_count = 0;
+
+struct _GdkGlobalErrorTrap
 {
-  int (*old_handler) (Display *, XErrorEvent *);
-  gint error_warnings;
-  gint error_code;
+  GSList *displays;
 };
 
 /* 
@@ -286,72 +289,6 @@ _gdk_windowing_exit (void)
 
 /*
  *--------------------------------------------------------------
- * gdk_x_error
- *
- *   The X error handling routine.
- *
- * Arguments:
- *   "display" is the X display the error originated from.
- *   "error" is the XErrorEvent that we are handling.
- *
- * Results:
- *   Either we were expecting some sort of error to occur,
- *   in which case we set the "_gdk_error_code" flag, or this
- *   error was unexpected, in which case we will print an
- *   error message and exit. (Since trying to continue will
- *   most likely simply lead to more errors).
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
-
-static int
-gdk_x_error (Display	 *display,
-	     XErrorEvent *error)
-{
-  if (error->error_code)
-    {
-      if (_gdk_error_warnings)
-	{
-	  gchar buf[64];
-          gchar *msg;
-
-	  XGetErrorText (display, error->error_code, buf, 63);
-
-          msg =
-            g_strdup_printf ("The program '%s' received an X Window System error.\n"
-                             "This probably reflects a bug in the program.\n"
-                             "The error was '%s'.\n"
-                             "  (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
-                             "  (Note to programmers: normally, X errors are reported asynchronously;\n"
-                             "   that is, you will receive the error a while after causing it.\n"
-                             "   To debug your program, run it with the --sync command line\n"
-                             "   option to change this behavior. You can then get a meaningful\n"
-                             "   backtrace from your debugger if you break on the gdk_x_error() function.)",
-                             g_get_prgname (),
-                             buf,
-                             error->serial,
-                             error->error_code,
-                             error->request_code,
-                             error->minor_code);
-
-#ifdef G_ENABLE_DEBUG
-	  g_error ("%s", msg);
-#else /* !G_ENABLE_DEBUG */
-	  g_fprintf (stderr, "%s\n", msg);
-
-	  exit (1);
-#endif /* G_ENABLE_DEBUG */
-	}
-      _gdk_error_code = error->error_code;
-    }
-
-  return 0;
-}
-
-/*
- *--------------------------------------------------------------
  * gdk_x_io_error
  *
  *   The X I/O error handling routine.
@@ -398,43 +335,212 @@ gdk_x_io_error (Display *display)
   exit(1);
 }
 
+/* X error handler. Keep the name the same because people are used to
+ * breaking on it in the debugger.
+ */
+static int
+gdk_x_error (Display	 *xdisplay,
+	     XErrorEvent *error)
+{
+  if (error->error_code)
+    {
+      GdkDisplay *error_display;
+      GdkDisplayManager *manager;
+      GSList *displays;
+
+      /* Figure out which GdkDisplay if any got the error. */
+      error_display = NULL;
+      manager = gdk_display_manager_get ();
+      displays = gdk_display_manager_list_displays (manager);
+      while (displays != NULL)
+        {
+          GdkDisplayX11 *gdk_display = displays->data;
+
+          if (xdisplay == gdk_display->xdisplay)
+            {
+              error_display = GDK_DISPLAY_OBJECT (gdk_display);
+              g_slist_free (displays);
+              displays = NULL;
+            }
+          else
+            {
+              displays = g_slist_delete_link (displays, displays);
+            }
+        }
+
+      if (error_display == NULL)
+        {
+          /* Error on an X display not opened by GDK. Ignore. */
+
+          return 0;
+        }
+      else
+        {
+          _gdk_x11_display_error_event (error_display, error);
+        }
+    }
+
+  return 0;
+}
+
 void
-gdk_error_trap_push (void)
+_gdk_x11_error_handler_push (void)
 {
-  GdkErrorTrap *trap;
+  _gdk_old_error_handler = XSetErrorHandler (gdk_x_error);
 
-  trap = g_slice_new (GdkErrorTrap);
+  if (_gdk_error_handler_push_count > 0)
+    {
+      if (_gdk_old_error_handler != gdk_x_error)
+        g_warning ("XSetErrorHandler() called with a GDK error trap pushed. Don't do that.");
+    }
 
-  trap->old_handler = XSetErrorHandler (gdk_x_error);
-  trap->error_code = _gdk_error_code;
-  trap->error_warnings = _gdk_error_warnings;
+  _gdk_error_handler_push_count += 1;
+}
+
+void
+_gdk_x11_error_handler_pop  (void)
+{
+  g_return_if_fail (_gdk_error_handler_push_count > 0);
+
+  _gdk_error_handler_push_count -= 1;
+
+  if (_gdk_error_handler_push_count == 0)
+    {
+      XSetErrorHandler (_gdk_old_error_handler);
+      _gdk_old_error_handler = NULL;
+    }
+}
+
+/**
+ * gdk_error_trap_push:
+ *
+ * This function allows X errors to be trapped instead of the normal
+ * behavior of exiting the application. It should only be used if it
+ * is not possible to avoid the X error in any other way. Errors are
+ * ignored on all #GdkDisplay currently known to the
+ * #GdkDisplayManager. If you don't care which error happens and just
+ * want to ignore everything, pop with gdk_error_trap_pop_ignored().
+ * If you need the error code, use gdk_error_trap_pop() which may have
+ * to block and wait for the error to arrive from the X server.
+ *
+ * This API exists on all platforms but only does anything on X.
+ *
+ * You can use gdk_x11_display_error_trap_push() to ignore errors
+ * on only a single display.
+ *
+ * <example>
+ * <title>Trapping an X error</title>
+ * <programlisting>
+ * gdk_error_trap_push (<!-- -->);
+ *
+ *  // ... Call the X function which may cause an error here ...
+ *
+ *
+ * if (gdk_error_trap_pop (<!-- -->))
+ *  {
+ *    // ... Handle the error here ...
+ *  }
+ * </programlisting>
+ * </example>
+ *
+ */
+void
+gdk_error_trap_push (void)
+{
+  GdkGlobalErrorTrap *trap;
+  GdkDisplayManager *manager;
+  GSList *tmp_list;
+
+  trap = g_slice_new (GdkGlobalErrorTrap);
+  manager = gdk_display_manager_get ();
+  trap->displays = gdk_display_manager_list_displays (manager);
+
+  g_slist_foreach (trap->displays, (GFunc) g_object_ref, NULL);
+  for (tmp_list = trap->displays;
+       tmp_list != NULL;
+       tmp_list = tmp_list->next)
+    {
+      gdk_x11_display_error_trap_push (tmp_list->data);
+    }
 
   g_queue_push_head (&gdk_error_traps, trap);
-  _gdk_error_code = 0;
-  _gdk_error_warnings = 0;
 }
 
-gint
-gdk_error_trap_pop (void)
+static gint
+gdk_error_trap_pop_internal (gboolean need_code)
 {
-  GdkErrorTrap *trap;
+  GdkGlobalErrorTrap *trap;
   gint result;
+  GSList *tmp_list;
 
   trap = g_queue_pop_head (&gdk_error_traps);
 
-  g_return_val_if_fail (trap != NULL, 0);
+  g_return_val_if_fail (trap != NULL, Success);
+
+  result = Success;
+  for (tmp_list = trap->displays;
+       tmp_list != NULL;
+       tmp_list = tmp_list->next)
+    {
+      gint code = Success;
+
+      if (need_code)
+        code = gdk_x11_display_error_trap_pop (tmp_list->data);
+      else
+        gdk_x11_display_error_trap_pop_ignored (tmp_list->data);
 
-  result = _gdk_error_code;
+      /* we use the error on the last display listed, why not. */
+      if (code != Success)
+        result = code;
+    }
 
-  _gdk_error_code = trap->error_code;
-  _gdk_error_warnings = trap->error_warnings;
-  XSetErrorHandler (trap->old_handler);
+  g_slist_foreach (trap->displays, (GFunc) g_object_unref, NULL);
+  g_slist_free (trap->displays);
 
-  g_slice_free (GdkErrorTrap, trap);
+  g_slice_free (GdkGlobalErrorTrap, trap);
 
   return result;
 }
 
+/**
+ * gdk_error_trap_pop_ignored:
+ *
+ * Removes an error trap pushed with gdk_error_trap_push(), but
+ * without bothering to wait and see whether an error occurred.  If an
+ * error arrives later asynchronously that was triggered while the
+ * trap was pushed, that error will be ignored.
+ *
+ * Since: 3.0
+ */
+void
+gdk_error_trap_pop_ignored (void)
+{
+  gdk_error_trap_pop_internal (FALSE);
+}
+
+/**
+ * gdk_error_trap_pop:
+ *
+ * Removes an error trap pushed with gdk_error_trap_push().
+ * May block until an error has been definitively received
+ * or not received from the X server. gdk_error_trap_pop_ignored()
+ * is preferred if you don't need to know whether an error
+ * occurred, because it never has to block. If you don't
+ * need the return value of gdk_error_trap_pop(), use
+ * gdk_error_trap_pop_ignored().
+ *
+ * Prior to GDK 3.0, this function would not automatically
+ * sync for you, so you had to gdk_flush() if your last
+ * call to Xlib was not a blocking round trip.
+ *
+ * Return value: X error code or 0 on success
+ */
+gint
+gdk_error_trap_pop (void)
+{
+  return gdk_error_trap_pop_internal (TRUE);
+}
+
 gchar *
 gdk_get_display (void)
 {
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index 70693b4..a4066e8 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -60,6 +60,8 @@ struct _GdkVisualPrivate
   GdkScreen *screen;
 };
 
+void _gdk_x11_error_handler_push (void);
+void _gdk_x11_error_handler_pop  (void);
 
 void _gdk_xid_table_insert (GdkDisplay *display,
 			    XID        *xid,
diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h
index ec6ddd5..074ec5f 100644
--- a/gdk/x11/gdkx.h
+++ b/gdk/x11/gdkx.h
@@ -164,6 +164,12 @@ G_CONST_RETURN gchar *gdk_x11_get_xatom_name    (Atom         xatom);
 
 void	    gdk_x11_display_grab	      (GdkDisplay *display);
 void	    gdk_x11_display_ungrab	      (GdkDisplay *display);
+
+void                           gdk_x11_display_error_trap_push        (GdkDisplay *display);
+/* warn unused because you could use pop_ignored otherwise */
+G_GNUC_WARN_UNUSED_RESULT gint gdk_x11_display_error_trap_pop         (GdkDisplay *display);
+void                           gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display);
+
 void        gdk_x11_register_standard_event_type (GdkDisplay *display,
 						  gint        event_base,
 						  gint        n_events);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]