diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index c6173a747f..d9fa2c000a 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -280,6 +280,9 @@ extern gboolean _gdk_disable_multidevice; void _gdk_events_queue (GdkDisplay *display); GdkEvent* _gdk_event_unqueue (GdkDisplay *display); +void _gdk_event_filter_unref (GdkWindow *window, + GdkEventFilter *filter); + void _gdk_event_emit (GdkEvent *event); GList* _gdk_event_queue_find_first (GdkDisplay *display); void _gdk_event_queue_remove_link (GdkDisplay *display, diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index d5e5e1488d..da50f994d4 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -1794,19 +1794,55 @@ gdk_window_ensure_native (GdkWindow *window) return TRUE; } +/** + * _gdk_event_filter_unref: + * @window: A #GdkWindow, or %NULL to be the global window + * @filter: A window filter + * + * Release a reference to @filter. Note this function may + * mutate the list storage, so you need to handle this + * if iterating over a list of filters. + */ +void +_gdk_event_filter_unref (GdkWindow *window, + GdkEventFilter *filter) +{ + GList **filters; + GList *tmp_list; + + if (window == NULL) + filters = &_gdk_default_filters; + else + filters = &window->filters; + + for (tmp_list = *filters; tmp_list; tmp_list = tmp_list->next) + { + GdkEventFilter *iter_filter = tmp_list->data; + GList *node; + + if (iter_filter != filter) + continue; + + g_assert (iter_filter->ref_count > 0); + + filter->ref_count--; + if (filter->ref_count != 0) + continue; + + node = tmp_list; + tmp_list = tmp_list->next; + + *filters = g_list_remove_link (*filters, node); + g_free (filter); + g_list_free_1 (node); + } +} + static void window_remove_filters (GdkWindow *window) { - if (window->filters) - { - GList *tmp_list; - - for (tmp_list = window->filters; tmp_list; tmp_list = tmp_list->next) - g_free (tmp_list->data); - - g_list_free (window->filters); - window->filters = NULL; - } + while (window->filters) + _gdk_event_filter_unref (window, window->filters->data); } static void @@ -2486,16 +2522,8 @@ gdk_window_remove_filter (GdkWindow *window, if ((filter->function == function) && (filter->data == data)) { filter->flags |= GDK_EVENT_FILTER_REMOVED; - filter->ref_count--; - if (filter->ref_count != 0) - return; - if (window) - window->filters = g_list_remove_link (window->filters, node); - else - _gdk_default_filters = g_list_remove_link (_gdk_default_filters, node); - g_list_free_1 (node); - g_free (filter); + _gdk_event_filter_unref (window, filter); return; } diff --git a/gdk/x11/gdkeventsource.c b/gdk/x11/gdkeventsource.c index 5f0c3a79e6..37275fb183 100644 --- a/gdk/x11/gdkeventsource.c +++ b/gdk/x11/gdkeventsource.c @@ -55,14 +55,17 @@ static GSourceFuncs event_funcs = { static GList *event_sources = NULL; static gint -gdk_event_apply_filters (XEvent *xevent, - GdkEvent *event, - GList **filters) +gdk_event_apply_filters (XEvent *xevent, + GdkEvent *event, + GdkWindow *window) { GList *tmp_list; GdkFilterReturn result; - tmp_list = *filters; + if (window == NULL) + tmp_list = _gdk_default_filters; + else + tmp_list = window->filters; while (tmp_list) { @@ -78,18 +81,12 @@ gdk_event_apply_filters (XEvent *xevent, filter->ref_count++; result = filter->function (xevent, event, filter->data); - /* get the next node after running the function since the - function may add or remove a next node */ - node = tmp_list; - tmp_list = tmp_list->next; + /* Protect against unreffing the filter mutating the list */ + node = tmp_list->next; - filter->ref_count--; - if (filter->ref_count == 0) - { - *filters = g_list_remove_link (*filters, node); - g_list_free_1 (node); - g_free (filter); - } + _gdk_event_filter_unref (window, filter); + + tmp_list = node; if (result != GDK_FILTER_CONTINUE) return result; @@ -162,8 +159,7 @@ gdk_event_source_translate_event (GdkEventSource *event_source, { /* Apply global filters */ - result = gdk_event_apply_filters (xevent, event, - &_gdk_default_filters); + result = gdk_event_apply_filters (xevent, event, NULL); if (result == GDK_FILTER_REMOVE) { @@ -186,7 +182,7 @@ gdk_event_source_translate_event (GdkEventSource *event_source, if (filter_window->filters) { result = gdk_event_apply_filters (xevent, event, - &filter_window->filters); + filter_window); if (result == GDK_FILTER_REMOVE) {