[gtk+/xi2] Implement device GTK+ grabs.
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/xi2] Implement device GTK+ grabs.
- Date: Thu, 18 Feb 2010 01:51:36 +0000 (UTC)
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]