Events delivered to not-viewable windows



http://bugzilla.gnome.org/show_bug.cgi?id=105642 points out
a very long-standing problem in GTK+ - you can get events
delivered to windows that are no longer viewable. (Viewable
means that the window and all it's parents are mapped.)

The case in the bug report is:

 Application hides window after gtk_dialog_run(), but 
 Escape keypress is queued up and causes the dialog 
 to be destroyed

But you can easily construct other examples as well, for
example:

 User clicks on a notebook tab the dialog, then quickly
  clicks on a window-widget in the dialog before the
  page changes.

The attach patch is an attempt to solve the problem; 
what it does is is that before emitting ::event, 
::<foo>-event ::event-after check that the window is
viewable.

I think this patch considerably improves the current situation;
though I have two small concerns about the re-checking between
the 3 signals:
 
 - The repeated checks don't catch the case where one handler
   for the signal unmaps the window or a parent, so they
   are still imperfect. (The point could be made about
   the current WIDGET_REALIZED_FOR_EVENT() check)

 - It's slightly conceivable that an app might rely on 
   still receiving an event in ::event-after even if the
   main handlers resulted in the widget being hidden.

It's possible that considering these points, it would be
simpler o just check once at the top of the function, and
ignore the possibility of handlers in the course of 
gtk_widget_event() making the widget not-viewable.
   
Regards,
				Owen
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.4220
diff -u -p -r1.4220 ChangeLog
--- ChangeLog	6 Jun 2003 00:52:17 -0000	1.4220
+++ ChangeLog	6 Jun 2003 15:19:20 -0000
@@ -1,6 +1,20 @@
+Fri Jun  6 11:07:33 2003  Owen Taylor  <otaylor redhat com>
+
+	* gtk/gtkwidget.c (event_window_still_viewable): 
+	Before delivering an event to a widget, check that
+	(if relevant), the event's window is still viewable.
+
+	* gdk/gdkwindow.c (_gdk_window_destroy_hierarchy): 
+	NULL out private->parent, since after destruction
+	it might not be valid any more.
+
+	* gdk/gdkwindow.c (gdk_window_is_viewable): Fix some
+	accesses before g_return_val_if_fail(). Treat 
+	DESTROYED windows as unmaped.
+
 Thu Jun  5 20:35:40 2003  Owen Taylor  <otaylor redhat com>
 
-	* demos/Makefile.am: Conditionalize deendencies for
+	* demos/Makefile.am: Conditionalize dependencies for
 	gdk-pixbuf-csource on cross-compilation (#112391, J. Ali Harlowe). 
 	Buildtest-inline-pixbufs.h in srcdir. Don't try to build 
 	test-inline-pixbufs.h if we don't have libpng.
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.344
diff -u -p -r1.344 gtkwidget.c
--- gtk/gtkwidget.c	21 May 2003 23:08:27 -0000	1.344
+++ gtk/gtkwidget.c	6 Jun 2003 15:19:20 -0000
@@ -3040,16 +3040,53 @@ gtk_widget_send_expose (GtkWidget *widge
   return gtk_widget_event_internal (widget, event);
 }
 
+static gboolean
+event_window_is_still_viewable (GdkEvent *event)
+{
+  /* Check that we think the event's window is viewable before
+   * delivering the event, to prevent suprises. We do this here
+   * at the last moment, since the event may have been queued
+   * up behind other events, held over a recursive main loop, etc.
+   */
+  switch (event->type)
+    {
+    case GDK_EXPOSE:
+    case GDK_MOTION_NOTIFY:
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+    case GDK_KEY_PRESS:
+    case GDK_KEY_RELEASE:
+    case GDK_ENTER_NOTIFY:
+    case GDK_LEAVE_NOTIFY:
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+    case GDK_SCROLL:
+      return event->any.window && gdk_window_is_viewable (event->any.window);
+      
+    default:
+      /* Remaining events would make sense on an not-viewable window,
+       * or don't have an associated window.
+       */
+      return TRUE;
+    }
+}
+
 static gint
 gtk_widget_event_internal (GtkWidget *widget,
 			   GdkEvent  *event)
 {
   gboolean return_val = FALSE;
 
+  if (!event_window_is_still_viewable (event))
+    return TRUE;
+
   g_object_ref (widget);
 
   g_signal_emit (widget, widget_signals[EVENT], 0, event, &return_val);
-  return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
+  return_val |= !(WIDGET_REALIZED_FOR_EVENT (widget, event) &&
+		  event_window_is_still_viewable (event));
   if (!return_val)
     {
       gint signal_num;
@@ -3144,7 +3181,8 @@ gtk_widget_event_internal (GtkWidget *wi
       if (signal_num != -1)
 	g_signal_emit (widget, widget_signals[signal_num], 0, event, &return_val);
     }
-  if (WIDGET_REALIZED_FOR_EVENT (widget, event))
+  if (WIDGET_REALIZED_FOR_EVENT (widget, event) &&
+      event_window_is_still_viewable (event))
     g_signal_emit (widget, widget_signals[EVENT_AFTER], 0, event);
   else
     return_val = TRUE;


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