diff options
author | Ray Strode <rstrode@redhat.com> | 2007-04-18 01:51:12 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2007-04-18 01:51:12 -0400 |
commit | fac22f053b37386dd13152f9063cde817a466aa8 (patch) | |
tree | c796eb6e93aa96193f0d43cce510cf39e5d35768 | |
parent | 3594f0a035002ec08bfdf10cc391bc96222f54bd (diff) |
add experiment x reply event source
-rw-r--r-- | src/pop-x-reply-watch.c | 312 | ||||
-rw-r--r-- | src/pop-x-reply-watch.h | 47 |
2 files changed, 359 insertions, 0 deletions
diff --git a/src/pop-x-reply-watch.c b/src/pop-x-reply-watch.c new file mode 100644 index 0000000..9a34bb3 --- /dev/null +++ b/src/pop-x-reply-watch.c @@ -0,0 +1,312 @@ +/* pop-x-reply-watch.c - event source for asynchronous X replies + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include "config.h" +#include <glib.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> + +#include <gtk/gtk.h> + +typedef struct _PopXReplyWatchSource PopXReplyWatchSource; + +struct _PopXReplyWatchSource +{ + GSource source; + GdkDisplay *display; + gulong request_id; + Status status; + XErrorEvent *error; + + GSource *timeout_source; +}; + +static gboolean +pop_x_reply_watch_prepare (GSource *source, + gint *timeout) +{ + /* we don't need to poll because we'll always + * get checked after the X event queue has been + * processed + */ + *timeout = -1; + return FALSE; +} + +static gboolean +pop_x_reply_watch_check (GSource *source) +{ + gulong last_processed_request_id; + PopXReplyWatchSource *x_reply_watch_source; + + x_reply_watch_source = (PopXReplyWatchSource *) source; + + last_processed_request_id = LastKnownRequestProcessed (GDK_DISPLAY_XDISPLAY (x_reply_watch_source->display)); + + return last_processed_request_id >= x_reply_watch_source->request_id; +} + +static gboolean +pop_x_reply_watch_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + PopXReplyWatchSource *x_reply_watch_source; + PopXReplyWatchFunc x_reply_watch_func; + + if (callback == NULL) + return FALSE; + + x_reply_watch_source = (PopXReplyWatchSource *) source; + + x_reply_watch_func = (PopXReplyWatchFunc) callback; + + x_reply_watch_func (x_reply_watch_source->display, + x_reply_watch_source->status, + x_reply_watch_source->error, user_data); + + return FALSE; +} + +static void +pop_x_reply_watch_finalize (GSource *source) +{ + GList *pending_sources; + PopXReplyWatchSource *x_reply_watch_source; + + x_reply_watch_source = (PopXReplyWatchSource *) source; + + pending_sources = g_object_get_data (G_OBJECT (x_reply_watch_source->display), + "pop-x-reply-sources"); + + pending_sources = g_list_remove (pending_sources, source); + g_object_set_data (G_OBJECT (x_reply_watch_source->display), + "pop-x-reply-sources", + pending_sources); + + if (x_reply_watch_source->error != NULL) + g_slice_free (XErrorEvent, x_reply_watch_source->error); + + if (!g_source_is_destroyed (x_reply_watch_source->timeout_source)) + g_source_destroy (x_reply_watch_source->timeout_source); + + g_source_unref (x_reply_watch_source->timeout_source); + x_reply_watch_source->timeout_source = NULL; +} + +static GSourceFuncs pop_x_reply_watch_funcs = +{ + pop_x_reply_watch_prepare, + pop_x_reply_watch_check, + pop_x_reply_watch_dispatch, + pop_x_reply_watch_finalize, +}; + +static XErrorHandler old_error_handler = NULL; + +typedef struct +{ + XErrorEvent *error; + PopXReplyWatchSource *source; +} PopXReplyWatchErrorAndSource; + +static gboolean +add_error_to_source_idle_handler (gpointer data) +{ + PopXReplyWatchErrorAndSource *error_and_source = (PopXReplyWatchErrorAndSource *) data; + + g_assert (error_and_source->source != NULL); + + error_and_source->source->error = error_and_source->error; + error_and_source->source->status = error_and_source->error->error_code; + + g_slice_free (PopXReplyWatchErrorAndSource, error_and_source); + return FALSE; +} + +static int +pop_x_reply_watch_error_handler (Display *x_display, + XErrorEvent *error) +{ + GList *pending_sources, *tmp; + GdkDisplay *display; + + display = gdk_x11_lookup_xdisplay (x_display); + + g_assert (display != NULL); + + pending_sources = g_object_get_data (G_OBJECT (display), + "pop-x-reply-sources"); + + for (tmp = pending_sources; tmp != NULL; tmp = tmp->next) + { + GSource *source; + PopXReplyWatchSource *x_reply_watch_source; + x_reply_watch_source = (PopXReplyWatchSource *) tmp->data; + source = (GSource *) tmp->data; + + if (x_reply_watch_source->request_id == error->serial) + { + GSource *idle_source; + PopXReplyWatchErrorAndSource *error_and_source; + + error_and_source = g_slice_new (PopXReplyWatchErrorAndSource); + error_and_source->error = g_slice_new (XErrorEvent); + *error_and_source->error = *error; + error_and_source->source = x_reply_watch_source; + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, 0); + g_source_set_callback (idle_source, + add_error_to_source_idle_handler, + error_and_source, NULL); + g_source_attach (idle_source, g_source_get_context (source)); + g_source_unref (idle_source); + + return 0; + } + } + + if (old_error_handler != NULL) + return old_error_handler (x_display, error); + + return 0; +} + +static void +pop_x_reply_watch_setup_error_handler (void) +{ + XErrorHandler current_error_handler; + + current_error_handler = XSetErrorHandler (NULL); + + if (current_error_handler != pop_x_reply_watch_error_handler) + { + old_error_handler = current_error_handler; + XSetErrorHandler (pop_x_reply_watch_error_handler); + return; + } + + XSetErrorHandler (current_error_handler); +} + +static void +pop_x_reply_watch_source_register_with_display (GSource *source, + GdkDisplay *display) +{ + GList *pending_sources; + PopXReplyWatchSource *x_reply_watch_source; + + x_reply_watch_source = (PopXReplyWatchSource *) source; + + pending_sources = g_object_get_data (G_OBJECT (display), + "pop-x-reply-sources"); + + pending_sources = g_list_prepend (pending_sources, source); + g_object_set_data (G_OBJECT (x_reply_watch_source->display), + "pop-x-reply-sources", + pending_sources); +} + +GSource * +pop_x_reply_watch_source_new (GdkDisplay *display, + PopXRequestFunc request_func, + gpointer request_func_data) +{ + GSource *source; + gulong request_id, next_request_id; + PopXReplyWatchSource *x_reply_watch_source; + + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + g_return_val_if_fail (request_func != NULL, NULL); + + pop_x_reply_watch_setup_error_handler (); + + request_id = XNextRequest (GDK_DISPLAY ()); + + source = g_source_new (&pop_x_reply_watch_funcs, sizeof (PopXReplyWatchSource)); + x_reply_watch_source = (PopXReplyWatchSource *) source; + + x_reply_watch_source->display = display; + x_reply_watch_source->request_id = request_id; + x_reply_watch_source->error = NULL; + + pop_x_reply_watch_source_register_with_display (source, display); + + x_reply_watch_source->status = request_func (display, request_func_data); + + next_request_id = XNextRequest (GDK_DISPLAY ()); + g_assert (next_request_id >= request_id); + if (request_id == next_request_id) + { + g_warning ("X reply watch source request function didn't " + "make an X request\n"); + g_source_destroy (source); + g_source_unref (source); + return NULL; + } + + /* if we don't get an answer back in a quarter of a second, force + * a round trip to ensure the request id gets incremented so the + * source gets dispatched + */ + x_reply_watch_source->timeout_source = g_timeout_source_new (250); + g_source_set_callback (x_reply_watch_source->timeout_source, + (GSourceFunc) gdk_display_sync, display, NULL); + g_source_attach (x_reply_watch_source->timeout_source, g_source_get_context (source)); + + return source; +} + +guint +pop_x_reply_watch_add_full (gint priority, + GdkDisplay *display, + PopXRequestFunc request_func, + gpointer request_func_data, + PopXReplyWatchFunc reply_func, + gpointer reply_func_data, + GDestroyNotify notify) +{ + GSource *source; + guint id; + + source = pop_x_reply_watch_source_new (display, request_func, request_func_data); + + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (source, priority); + + g_source_set_callback (source, (GSourceFunc) reply_func, reply_func_data, notify); + id = g_source_attach (source, NULL); + g_source_unref (source); + + return id; +} + +guint +pop_x_reply_watch_add (GdkDisplay *display, + PopXRequestFunc request_func, + gpointer request_func_data, + PopXReplyWatchFunc reply_func, + gpointer reply_func_data) +{ + return pop_x_reply_watch_add_full (G_PRIORITY_DEFAULT, + display, request_func, request_func_data, + reply_func, reply_func_data, NULL); +} diff --git a/src/pop-x-reply-watch.h b/src/pop-x-reply-watch.h new file mode 100644 index 0000000..beb1114 --- /dev/null +++ b/src/pop-x-reply-watch.h @@ -0,0 +1,47 @@ +/* pop-x-reply-watch.c - event source for asynchronous X replies + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef POP_X_REPLY_WATCH_H +#define POP_X_REPLY_WATCH_H + +typedef Status (* PopXRequestFunc) (GdkDisplay *display, + gpointer user_data); +typedef void (* PopXReplyWatchFunc) (GdkDisplay *display, + Status status, + XErrorEvent *error, + gpointer user_data); + +GSource *pop_x_reply_watch_source_new (GdkDisplay *display, + PopXRequestFunc request_func, + gpointer request_func_data); +guint pop_x_reply_watch_add_full (gint priority, + GdkDisplay *display, + PopXRequestFunc request_func, + gpointer request_func_data, + PopXReplyWatchFunc reply_func, + gpointer reply_func_data, + GDestroyNotify notify); + +guint pop_x_reply_watch_add (GdkDisplay *display, + PopXRequestFunc request_func, + gpointer request_func_data, + PopXReplyWatchFunc reply_func, + gpointer data); + +#endif |