[gtk+/xi2] Implement device GTK+ grabs.



commit 701f631aa0c6eee464fbc5485618f6e06991deb2
Author: Carlos Garnacho <carlosg gnome org>
Date:   Thu Feb 18 02:20:47 2010 +0100

    Implement device GTK+ grabs.
    
    gdk_device_grab_add/remove() have been added, these
    can be used to set up a GTK+ grab that only affects
    a device pair, optionally blocking the widget for
    other devices.
    
    gtk_widget_device_is_shadowed() is meant to replace
    the was_grabbed argument in GtkWidget::grab-notify, so
    widgets are able to check whether some device they
    were interacting with has been shadowed by a grab on
    another widget.

 gtk/gtk.symbols |    3 +
 gtk/gtkmain.c   |  110 +++++++++++++++++++++++++++++++++++++++++-----
 gtk/gtkmain.h   |    6 +++
 gtk/gtkwidget.c |   54 +++++++++++++++++++++++
 gtk/gtkwidget.h |    4 ++
 gtk/gtkwindow.c |  130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkwindow.h |   14 ++++++
 7 files changed, 309 insertions(+), 12 deletions(-)
---
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 1c9937d..04aa426 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -2417,6 +2417,8 @@ gtk_get_event_widget
 gtk_grab_add
 gtk_grab_get_current
 gtk_grab_remove
+gtk_device_grab_add
+gtk_device_grab_remove
 gtk_propagate_event
 gtk_quit_add
 gtk_quit_add_destroy
@@ -5257,6 +5259,7 @@ gtk_widget_set_support_multidevice
 gtk_widget_create_device_group
 gtk_widget_remove_device_group
 gtk_widget_get_group_for_device
+gtk_widget_device_is_shadowed
 #endif
 #endif
 
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index bd54d9c..4658749 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -1446,9 +1446,10 @@ void
 gtk_main_do_event (GdkEvent *event)
 {
   GtkWidget *event_widget;
-  GtkWidget *grab_widget;
+  GtkWidget *grab_widget = NULL;
   GtkWindowGroup *window_group;
   GdkEvent *rewritten_event = NULL;
+  GdkDevice *device;
   GList *tmp_list;
 
   if (event->type == GDK_SETTING)
@@ -1495,13 +1496,9 @@ gtk_main_do_event (GdkEvent *event)
       event = rewritten_event;
       event_widget = gtk_get_event_widget (event);
     }
-  
-  window_group = gtk_main_get_window_group (event_widget);
 
-  /* Push the event onto a stack of current events for
-   * gtk_current_event_get().
-   */
-  current_events = g_list_prepend (current_events, event);
+  window_group = gtk_main_get_window_group (event_widget);
+  device = gdk_event_get_device (event);
 
   /* If there is a grab in effect...
    */
@@ -1517,11 +1514,36 @@ gtk_main_do_event (GdkEvent *event)
 	  gtk_widget_is_ancestor (event_widget, grab_widget))
 	grab_widget = event_widget;
     }
-  else
+  else if (device)
+    {
+      grab_widget = _gtk_window_group_get_current_device_grab (window_group, device);
+
+      if (grab_widget)
+        {
+          if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
+              gtk_widget_is_ancestor (event_widget, grab_widget))
+            grab_widget = event_widget;
+        }
+    }
+
+  if (!grab_widget)
+    grab_widget = event_widget;
+
+  /* If the widget receiving events is actually blocked by another device GTK+ grab */
+  if (device &&
+      _gtk_window_group_widget_is_blocked_for_device (window_group, grab_widget, device))
     {
-      grab_widget = event_widget;
+      if (rewritten_event)
+        gdk_event_free (rewritten_event);
+
+      return;
     }
 
+  /* Push the event onto a stack of current events for
+   * gtk_current_event_get().
+   */
+  current_events = g_list_prepend (current_events, event);
+
   /* Not all events get sent to the grabbing widget.
    * The delete, destroy, expose, focus change and resize
    *  events still get sent to the event widget because
@@ -1728,6 +1750,7 @@ typedef struct
   gboolean   is_grabbed;
   gboolean   from_grab;
   GList     *notified_windows;
+  GdkDevice *device;
 } GrabNotifyInfo;
 
 static void
@@ -1808,7 +1831,14 @@ gtk_grab_notify_foreach (GtkWidget *child,
   if ((was_shadowed || is_shadowed) && GTK_IS_CONTAINER (child))
     gtk_container_forall (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
 
-  devices = _gtk_widget_list_devices (child);
+  if (info->device &&
+      _gtk_widget_get_device_window (child, info->device))
+    {
+      /* Device specified and is on widget */
+      devices = g_list_prepend (NULL, info->device);
+    }
+  else
+    devices = _gtk_widget_list_devices (child);
 
   if (is_shadowed)
     {
@@ -1842,6 +1872,7 @@ gtk_grab_notify_foreach (GtkWidget *child,
 
 static void
 gtk_grab_notify (GtkWindowGroup *group,
+                 GdkDevice      *device,
 		 GtkWidget      *old_grab_widget,
 		 GtkWidget      *new_grab_widget,
 		 gboolean        from_grab)
@@ -1855,6 +1886,7 @@ gtk_grab_notify (GtkWindowGroup *group,
   info.old_grab_widget = old_grab_widget;
   info.new_grab_widget = new_grab_widget;
   info.from_grab = from_grab;
+  info.device = device;
 
   g_object_ref (group);
 
@@ -1900,7 +1932,7 @@ gtk_grab_add (GtkWidget *widget)
       g_object_ref (widget);
       group->grabs = g_slist_prepend (group->grabs, widget);
 
-      gtk_grab_notify (group, old_grab_widget, widget, TRUE);
+      gtk_grab_notify (group, NULL, old_grab_widget, widget, TRUE);
     }
 }
 
@@ -1936,12 +1968,66 @@ gtk_grab_remove (GtkWidget *widget)
       else
 	new_grab_widget = NULL;
 
-      gtk_grab_notify (group, widget, new_grab_widget, FALSE);
+      gtk_grab_notify (group, NULL, widget, new_grab_widget, FALSE);
       
       g_object_unref (widget);
     }
 }
 
+/**
+ * gtk_device_grab_add:
+ * @widget: a #GtkWidget
+ * @device: a #GtkDevice to grab on.
+ * @block_others: %TRUE to prevent other devices to interact with @widget.
+ *
+ * Adds a GTK+ grab on @device, so all the events on @device and its
+ * associated pointer or keyboard (if any) are delivered to @widget.
+ * If the @block_others parameter is %TRUE, any other devices will be
+ * unable to interact with @widget during the grab.
+ **/
+void
+gtk_device_grab_add (GtkWidget        *widget,
+                     GdkDevice        *device,
+                     gboolean          block_others)
+{
+  GtkWindowGroup *group;
+  GtkWidget *old_grab_widget;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  group = gtk_main_get_window_group (widget);
+  old_grab_widget = _gtk_window_group_get_current_device_grab (group, device);
+  _gtk_window_group_add_device_grab (group, widget, device, block_others);
+
+  gtk_grab_notify (group, device, old_grab_widget, widget, TRUE);
+}
+
+/**
+ * gtk_device_grab_remove:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Removes a device grab from the given widget. You have to pair calls
+ * to gtk_device_grab_add() and gtk_device_grab_remove().
+ **/
+void
+gtk_device_grab_remove (GtkWidget *widget,
+                        GdkDevice *device)
+{
+  GtkWindowGroup *group;
+  GtkWidget *new_grab_widget;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  group = gtk_main_get_window_group (widget);
+  _gtk_window_group_remove_device_grab (group, widget, device);
+  new_grab_widget = _gtk_window_group_get_current_device_grab (group, device);
+
+  gtk_grab_notify (group, device, widget, new_grab_widget, FALSE);
+}
+
 void
 gtk_init_add (GtkFunction function,
 	      gpointer	  data)
diff --git a/gtk/gtkmain.h b/gtk/gtkmain.h
index 087b000..334b410 100644
--- a/gtk/gtkmain.h
+++ b/gtk/gtkmain.h
@@ -156,6 +156,12 @@ void	   gtk_grab_add		   (GtkWidget	       *widget);
 GtkWidget* gtk_grab_get_current	   (void);
 void	   gtk_grab_remove	   (GtkWidget	       *widget);
 
+void       gtk_device_grab_add     (GtkWidget          *widget,
+                                    GdkDevice          *device,
+                                    gboolean            block_others);
+void       gtk_device_grab_remove  (GtkWidget          *widget,
+                                    GdkDevice          *device);
+
 void	   gtk_init_add		   (GtkFunction	       function,
 				    gpointer	       data);
 void	   gtk_quit_add_destroy	   (guint	       main_level,
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index a987ebd..32442c1 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -5826,6 +5826,60 @@ gtk_widget_has_grab (GtkWidget *widget)
 }
 
 /**
+ * gtk_widget_device_is_shadowed:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Returns %TRUE if @device has been shadowed by a GTK+
+ * device grab on another widget, so it would stop sending
+ * events to @widget. This may be used in the
+ * #GtkWidget::grab-notify signal to check for specific
+ * devices. See gtk_device_grab_add().
+ *
+ * Returns: %TRUE if there is an ongoing grab on @device
+ *          by another #GtkWidget than @widget.
+ **/
+gboolean
+gtk_widget_device_is_shadowed (GtkWidget *widget,
+                               GdkDevice *device)
+{
+  GtkWindowGroup *group;
+  GtkWidget *grab_widget, *toplevel;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+  if (!GTK_WIDGET_REALIZED (widget))
+    return TRUE;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  if (GTK_IS_WINDOW (toplevel))
+    group = gtk_window_get_group (GTK_WINDOW (toplevel));
+  else
+    group = gtk_window_get_group (NULL);
+
+  grab_widget = _gtk_window_group_get_current_device_grab (group, device);
+
+  /* Widget not inside the hierarchy of grab_widget */
+  if (grab_widget &&
+      widget != grab_widget &&
+      !gtk_widget_is_ancestor (widget, grab_widget))
+    return TRUE;
+
+  if (group->grabs)
+    {
+      grab_widget = group->grabs->data;
+
+      if (widget != grab_widget &&
+          !gtk_widget_is_ancestor (widget, grab_widget))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
  * gtk_widget_set_name:
  * @widget: a #GtkWidget
  * @name: name for the widget
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index b989045..94be39a 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -933,6 +933,10 @@ gboolean  gtk_widget_get_receives_default (GtkWidget           *widget);
 
 gboolean   gtk_widget_has_grab            (GtkWidget           *widget);
 
+gboolean   gtk_widget_device_is_shadowed  (GtkWidget           *widget,
+                                           GdkDevice           *device);
+
+
 void                  gtk_widget_set_name               (GtkWidget    *widget,
 							 const gchar  *name);
 G_CONST_RETURN gchar* gtk_widget_get_name               (GtkWidget    *widget);
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index f1c5273..43d841c 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -166,6 +166,7 @@ struct _GtkWindowGeometryInfo
 };
 
 #define GTK_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_WINDOW, GtkWindowPrivate))
+#define GTK_WINDOW_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_WINDOW_GROUP, GtkWindowGroupPrivate))
 
 typedef struct _GtkWindowPrivate GtkWindowPrivate;
 
@@ -198,6 +199,21 @@ struct _GtkWindowPrivate
   gchar *startup_id;
 };
 
+typedef struct _GtkDeviceGrabInfo GtkDeviceGrabInfo;
+typedef struct _GtkWindowGroupPrivate GtkWindowGroupPrivate;
+
+struct _GtkDeviceGrabInfo
+{
+  GtkWidget *widget;
+  GdkDevice *device;
+  guint block_others : 1;
+};
+
+struct _GtkWindowGroupPrivate
+{
+  GList *device_grabs;
+};
+
 static void gtk_window_dispose            (GObject           *object);
 static void gtk_window_destroy            (GtkObject         *object);
 static void gtk_window_finalize           (GObject           *object);
@@ -7604,6 +7620,7 @@ gtk_window_has_toplevel_focus (GtkWindow *window)
 static void
 gtk_window_group_class_init (GtkWindowGroupClass *klass)
 {
+  g_type_class_add_private (klass, sizeof (GtkWindowGroupPrivate));
 }
 
 GType
@@ -7794,6 +7811,119 @@ _gtk_window_group_get_current_grab (GtkWindowGroup *window_group)
   return NULL;
 }
 
+void
+_gtk_window_group_add_device_grab (GtkWindowGroup *window_group,
+                                   GtkWidget      *widget,
+                                   GdkDevice      *device,
+                                   gboolean        block_others)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+
+  info = g_slice_new0 (GtkDeviceGrabInfo);
+  info->widget = widget;
+  info->device = device;
+  info->block_others = block_others;
+
+  priv->device_grabs = g_list_prepend (priv->device_grabs, info);
+}
+
+void
+_gtk_window_group_remove_device_grab (GtkWindowGroup *window_group,
+                                      GtkWidget      *widget,
+                                      GdkDevice      *device)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+  GList *list, *node = NULL;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+  list = priv->device_grabs;
+
+  while (list)
+    {
+      info = list->data;
+
+      if (info->widget == widget &&
+          info->device == device)
+        {
+          node = list;
+          break;
+        }
+
+      list = list->next;
+    }
+
+  if (node)
+    {
+      info = node->data;
+
+      g_slice_free (GtkDeviceGrabInfo, info);
+      priv->device_grabs = g_list_delete_link (priv->device_grabs, node);
+    }
+}
+
+GtkWidget *
+_gtk_window_group_get_current_device_grab (GtkWindowGroup *window_group,
+                                           GdkDevice      *device)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+  GdkDevice *other_device;
+  GList *list;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+  list = priv->device_grabs;
+  other_device = gdk_device_get_associated_device (device);
+
+  while (list)
+    {
+      info = list->data;
+      list = list->next;
+
+      if (info->device == device ||
+          info->device == other_device)
+        return info->widget;
+    }
+
+  return NULL;
+}
+
+gboolean
+_gtk_window_group_widget_is_blocked_for_device (GtkWindowGroup *window_group,
+                                                GtkWidget      *widget,
+                                                GdkDevice      *device)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+  GdkDevice *other_device;
+  GList *list;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+  other_device = gdk_device_get_associated_device (device);
+  list = priv->device_grabs;
+
+  while (list)
+    {
+      info = list->data;
+      list = list->next;
+
+      /* Look for blocking grabs on other device pairs
+       * that have the passed widget within the GTK+ grab.
+       */
+      if (info->block_others &&
+          info->device != device &&
+          info->device != other_device &&
+          (info->widget == widget ||
+           gtk_widget_is_ancestor (widget, info->widget)))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
 /*
   Derived from XParseGeometry() in XFree86  
 
diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h
index 1b4362f..4175069 100644
--- a/gtk/gtkwindow.h
+++ b/gtk/gtkwindow.h
@@ -418,6 +418,20 @@ void            _gtk_window_constrain_size     (GtkWindow *window,
 						gint      *new_width,
 						gint      *new_height);
 GtkWidget      *_gtk_window_group_get_current_grab (GtkWindowGroup *window_group);
+void            _gtk_window_group_add_device_grab    (GtkWindowGroup   *window_group,
+                                                      GtkWidget        *widget,
+                                                      GdkDevice        *device,
+                                                      gboolean          block_others);
+void            _gtk_window_group_remove_device_grab (GtkWindowGroup   *window_group,
+                                                      GtkWidget        *widget,
+                                                      GdkDevice        *device);
+
+GtkWidget *     _gtk_window_group_get_current_device_grab (GtkWindowGroup *window_group,
+                                                           GdkDevice      *device);
+
+gboolean        _gtk_window_group_widget_is_blocked_for_device (GtkWindowGroup *window_group,
+                                                                GtkWidget      *widget,
+                                                                GdkDevice      *device);
 
 void            _gtk_window_set_has_toplevel_focus (GtkWindow *window,
 						    gboolean   has_toplevel_focus);



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