[goocanvas] Fix stuck grab bug #711709



commit cd1ad0f66ded84901f77519434191ae108dadace
Author: Damon Chaplin <Damon A Chaplin gmail com>
Date:   Thu Aug 31 08:18:14 2017 +0100

    Fix stuck grab bug #711709
    
    2017-08-30  Damon Chaplin  <damon gnome org>
    
        * src/goocanvas.c: If we receive a LEAVE_NOTIFY event with a mode of
          GDK_CROSSING_GRAB or GDK_CROSSING_GTK_GRAB we finish any passive grab
          we have underway. Hopefully fixes #711709
    
        * demo/mv-simple-demo.c (on_rect_button_press):
        * demo/simple-demo.c (on_rect_button_press): show a dialog to test
          the grab bug fix above.

 .gitignore            |    1 +
 ChangeLog             |   10 +++++++
 demo/mv-simple-demo.c |   17 +++++++++--
 demo/simple-demo.c    |   17 +++++++++--
 src/Makefile.am       |    7 +++++
 src/goocanvas.c       |   73 +++++++++++++++++++++++++++++-------------------
 6 files changed, 90 insertions(+), 35 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index b5916a8..fd25ad2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,6 +45,7 @@ Makefile.in
 /install-sh
 /libtool
 /ltmain.sh
+/m4
 /missing
 /mkinstalldirs
 /py-compile
diff --git a/ChangeLog b/ChangeLog
index 9a8d576..0a022af 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2017-08-30  Damon Chaplin  <damon gnome org>
+
+       * src/goocanvas.c: If we receive a LEAVE_NOTIFY event with a mode of
+       GDK_CROSSING_GRAB or GDK_CROSSING_GTK_GRAB we finish any passive grab
+       we have underway. Hopefully fixes #711709
+
+       * demo/mv-simple-demo.c (on_rect_button_press): 
+       * demo/simple-demo.c (on_rect_button_press): show a dialog to test
+       the grab bug fix above.
+
 2017-08-29  Damon Chaplin  <damon gnome org>
 
        * src/goocanvas.c:
diff --git a/demo/mv-simple-demo.c b/demo/mv-simple-demo.c
index 9ec41eb..1026518 100644
--- a/demo/mv-simple-demo.c
+++ b/demo/mv-simple-demo.c
@@ -72,15 +72,26 @@ main ()
 }
 
 
-/* This handles button presses in item views. We simply output a message to
-   the console. */
+/* The signal handler for the rectangle item. We show a dialog here. */
 static gboolean
 on_rect_button_press (GooCanvasItem  *item,
                      GooCanvasItem  *target,
                      GdkEventButton *event,
                      gpointer        data)
 {
-  g_print ("rect item received button press event\n");
+  GooCanvas *canvas;
+  GtkWidget *window, *dialog;
+
+  canvas = goo_canvas_item_get_canvas (item);
+  window = gtk_widget_get_toplevel (GTK_WIDGET (canvas));
+  dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_INFO,
+                                  GTK_BUTTONS_CLOSE,
+                                  "rect item received button press event");
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+
   return TRUE;
 }
 
diff --git a/demo/simple-demo.c b/demo/simple-demo.c
index 9a3358f..7e7bdcd 100644
--- a/demo/simple-demo.c
+++ b/demo/simple-demo.c
@@ -68,15 +68,26 @@ main ()
 }
 
 
-/* This handles button presses in item views. We simply output a message to
-   the console. */
+/* The signal handler for the rectangle item. We show a dialog here. */
 static gboolean
 on_rect_button_press (GooCanvasItem  *item,
                      GooCanvasItem  *target,
                      GdkEventButton *event,
                      gpointer        data)
 {
-  g_print ("rect item received button press event\n");
+  GooCanvas *canvas;
+  GtkWidget *window, *dialog;
+
+  canvas = goo_canvas_item_get_canvas (item);
+  window = gtk_widget_get_toplevel (GTK_WIDGET (canvas));
+  dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+                                  GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_INFO,
+                                  GTK_BUTTONS_CLOSE,
+                                  "rect item received button press event");
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+
   return TRUE;
 }
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 0f31008..2f7d4f3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -146,6 +146,13 @@ BUILT_SOURCES = $(libgoocanvas_built_headers) $(libgoocanvas_built_sources)
 MAINTAINERCLEANFILES = $(BUILT_SOURCES) $(stamp_files)
 EXTRA_DIST = $(BUILT_SOURCES) $(libgoocanvas_extra_sources)
 
+# if srcdir!=builddir, clean out maintainer-clean files from builddir
+# this allows dist to pass.
+distclean-local:
+       if test $(srcdir) != .; then \
+         rm -f $(MAINTAINERCLEANFILES); \
+       fi
+
 -include $(INTROSPECTION_MAKEFILE)
 INTROSPECTION_GIRS =
 INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir)
diff --git a/src/goocanvas.c b/src/goocanvas.c
index 77d20e6..5641f45 100644
--- a/src/goocanvas.c
+++ b/src/goocanvas.c
@@ -122,6 +122,7 @@ struct _GooCanvasPrivate {
   gint static_window_x, static_window_y;
   GdkRGBA background_color;
   guint background_color_set : 1;
+  guint pointer_grab_is_implicit : 1;
 };
 
 
@@ -3159,17 +3160,51 @@ update_pointer_item (GooCanvas *canvas,
 }
 
 
+static void
+goo_canvas_finish_pointer_grab (GooCanvas *canvas,
+                               GdkEvent  *event)
+{
+  /* We set the pointer item back to the item it was in before the
+     grab, so we'll synthesize enter/leave notify events as appropriate. */
+  if (canvas->pointer_grab_initial_item
+      && ITEM_IS_VALID (canvas->pointer_grab_initial_item))
+    set_item_pointer (&canvas->pointer_item,
+                     canvas->pointer_grab_initial_item);
+  else
+    set_item_pointer (&canvas->pointer_item, NULL);
+
+  set_item_pointer (&canvas->pointer_grab_item, NULL);
+  set_item_pointer (&canvas->pointer_grab_initial_item, NULL);
+
+  update_pointer_item (canvas, event);
+}
+
+
 static gboolean
 goo_canvas_crossing        (GtkWidget        *widget,
                            GdkEventCrossing *event)
 {
   GooCanvas *canvas = GOO_CANVAS (widget);
+  GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas);
 
   if (event->window != canvas->canvas_window)
     return FALSE;
 
-  /* This will result in synthesizing focus_in/out events as appropriate. */
-  update_pointer_item (canvas, (GdkEvent*) event);
+    /* If the pointer has left the canvas window due to a grab, then finish any
+       implicit pointer grab we have underway. */
+    if (event->type == GDK_LEAVE_NOTIFY
+      && (event->mode == GDK_CROSSING_GRAB
+         || event->mode == GDK_CROSSING_GTK_GRAB)
+      && canvas->pointer_grab_item
+      && priv->pointer_grab_is_implicit)
+    {
+      goo_canvas_finish_pointer_grab (canvas, (GdkEvent*) event);
+    }
+  else
+    {
+      /* This will result in synthesizing focus_in/out events as appropriate. */
+      update_pointer_item (canvas, (GdkEvent*) event);
+    }
 
   return FALSE;
 }
@@ -3201,6 +3236,7 @@ goo_canvas_button_press (GtkWidget      *widget,
                         GdkEventButton *event)
 {
   GooCanvas *canvas = GOO_CANVAS (widget);
+  GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas);
   GdkDevice *device = gdk_event_get_device ((GdkEvent*) event);
   GdkDisplay *display;
 
@@ -3220,6 +3256,7 @@ goo_canvas_button_press (GtkWidget      *widget,
       set_item_pointer (&canvas->pointer_grab_item,
                        canvas->pointer_item);
       canvas->pointer_grab_button = event->button;
+      priv->pointer_grab_is_implicit = TRUE;
     }
 
   return emit_pointer_event (canvas, "button_press_event", (GdkEvent*) event);
@@ -3249,19 +3286,7 @@ goo_canvas_button_release (GtkWidget      *widget,
       && event->button == canvas->pointer_grab_button
       && !gdk_display_device_is_grabbed (display, device))
     {
-      /* We set the pointer item back to the item it was in before the
-        grab, so we'll synthesize enter/leave notify events as appropriate. */
-      if (canvas->pointer_grab_initial_item
-         && ITEM_IS_VALID (canvas->pointer_grab_initial_item))
-       set_item_pointer (&canvas->pointer_item,
-                         canvas->pointer_grab_initial_item);
-      else
-       set_item_pointer (&canvas->pointer_item, NULL);
-
-      set_item_pointer (&canvas->pointer_grab_item, NULL);
-      set_item_pointer (&canvas->pointer_grab_initial_item, NULL);
-
-      update_pointer_item (canvas, (GdkEvent*) event);
+      goo_canvas_finish_pointer_grab (canvas, (GdkEvent*) event);
     }
 
   return retval;
@@ -3484,6 +3509,7 @@ goo_canvas_pointer_grab (GooCanvas     *canvas,
                         GdkCursor     *cursor,
                         guint32        time)
 {
+  GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas);
   GdkGrabStatus status = GDK_GRAB_SUCCESS;
   GdkDisplay *display;
 
@@ -3520,6 +3546,7 @@ goo_canvas_pointer_grab (GooCanvas     *canvas,
                             canvas->pointer_item);
       set_item_pointer (&canvas->pointer_grab_item,
                             item);
+      priv->pointer_grab_is_implicit = FALSE;
     }
 
   return status;
@@ -3560,20 +3587,8 @@ goo_canvas_pointer_ungrab (GooCanvas     *canvas,
     gdk_display_pointer_ungrab (display, time);
 #endif
 
-  /* We set the pointer item back to the item it was in before the
-     grab, so we'll synthesize enter/leave notify events as appropriate. */
-  if (canvas->pointer_grab_initial_item
-      && ITEM_IS_VALID (canvas->pointer_grab_initial_item))
-    set_item_pointer (&canvas->pointer_item,
-                     canvas->pointer_grab_initial_item);
-  else
-    set_item_pointer (&canvas->pointer_item, NULL);
-
-  set_item_pointer (&canvas->pointer_grab_item, NULL);
-  set_item_pointer (&canvas->pointer_grab_initial_item, NULL);
-
-  update_pointer_item (canvas, NULL);
- }
+  goo_canvas_finish_pointer_grab (canvas, NULL);
+}
 
 
 /**


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