Fixes for grabbing problems
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list gnome org
- Subject: Fixes for grabbing problems
- Date: Fri, 1 Mar 2002 20:31:32 -0500 (EST)
The attached patch addresses the most complex hard-to-puntable bug we
still have open on the 2.0.0 milestone:
69934 Grab group problem with "global grabs"
As discussed earlier on this list, what it does is convert the
"owner" of owner_events = TRUE from the client to the application.
Also included here is a fix for
65006 GtkCombo - dragging scrollbar in popup
Regards,
Owen
Fri Mar 1 18:39:44 2002 Owen Taylor <otaylor redhat com>
* gdk/x11/{gdkevents-x11.c,gdkmain-x11.c,gdkprivate-x11.h,
gdkwindow-x11.c}: Robustify tracking of pointer grab window.
* gdk/x11/gdkmain-x11.c: Keep track of current keyboard
grab window.
* gdk/x11/gdkmain-x11.c (gdk_pointer_grab_info_libgtk_only,
gdk_keyboard_grab_info_libgtk_only): Private libgtk => libgtk
API for finding out current grab information.
* gtk/gtkmain.c (rewrite_event_for_grabs): Rewrite events
so that the effective behavior of owner_events = TRUE is changed
to "deliver events to same window group normally" instead
of "deliver events to same application normally. #69934
* gtk/gtkrange.c: Use an explicit gtk_grab_add() so that
it works within the GtkList combo, where there is a
owner_events = FALSE gdk_pointer_grab() already in effect.
#65006.
? autom4te.cache
? gdk--2.0.pc
? gtk+--2.0.pc
? gdk/x11/foo.c
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.3162
diff -u -p -r1.3162 ChangeLog
--- ChangeLog 2002/03/01 22:01:47 1.3162
+++ ChangeLog 2002/03/02 01:27:36
@@ -1,3 +1,25 @@
+Fri Mar 1 18:39:44 2002 Owen Taylor <otaylor redhat com>
+
+ * gdk/x11/{gdkevents-x11.c,gdkmain-x11.c,gdkprivate-x11.h,
+ gdkwindow-x11.c}: Robustify tracking of pointer grab window.
+
+ * gdk/x11/gdkmain-x11.c: Keep track of current keyboard
+ grab window.
+
+ * gdk/x11/gdkmain-x11.c (gdk_pointer_grab_info_libgtk_only,
+ gdk_keyboard_grab_info_libgtk_only): Private libgtk => libgtk
+ API for finding out current grab information.
+
+ * gtk/gtkmain.c (rewrite_event_for_grabs): Rewrite events
+ so that the effective behavior of owner_events = TRUE is changed
+ to "deliver events to same window group normally" instead
+ of "deliver events to same application normally. #69934
+
+ * gtk/gtkrange.c: Use an explicit gtk_grab_add() so that
+ it works within the GtkList combo, where there is a
+ owner_events = FALSE gdk_pointer_grab() already in effect.
+ #65006.
+
Fri Mar 1 17:00:28 2002 Owen Taylor <otaylor redhat com>
* gdk/gdkpolyreg-generic.c: Fix some reported (but not significant)
Index: gdk/x11/gdkevents-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkevents-x11.c,v
retrieving revision 1.69
diff -u -p -r1.69 gdkevents-x11.c
--- gdk/x11/gdkevents-x11.c 2002/02/24 02:24:50 1.69
+++ gdk/x11/gdkevents-x11.c 2002/03/02 01:27:36
@@ -1262,9 +1262,8 @@ gdk_event_translate (GdkEvent *event,
gdk_synthesize_window_state (window,
0,
GDK_WINDOW_STATE_ICONIFIED);
-
- if (_gdk_xgrab_window == window_private)
- _gdk_xgrab_window = NULL;
+
+ _gdk_xgrab_check_unmap (window, xevent->xany.serial);
break;
Index: gdk/x11/gdkmain-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkmain-x11.c,v
retrieving revision 1.143
diff -u -p -r1.143 gdkmain-x11.c
--- gdk/x11/gdkmain-x11.c 2002/02/08 19:12:28 1.143
+++ gdk/x11/gdkmain-x11.c 2002/03/02 01:27:36
@@ -92,6 +92,18 @@ static gboolean gdk_synchronize = FALSE;
static GSList *gdk_error_traps = NULL; /* List of error traps */
static GSList *gdk_error_trap_free_list = NULL; /* Free list */
+/* Information about current pointer and keyboard grabs held by this
+ * client. If gdk_pointer_xgrab_window or gdk_keyboard_xgrab_window
+ * window is NULL, then the other associated fields are ignored
+ */
+static GdkWindowObject *gdk_pointer_xgrab_window = NULL;
+static gulong gdk_pointer_xgrab_serial;
+static gboolean gdk_pointer_xgrab_owner_events;
+
+static GdkWindowObject *gdk_keyboard_xgrab_window = NULL;
+static gulong gdk_keyboard_xgrab_serial;
+static gboolean gdk_keyboard_xgrab_owner_events;
+
GdkArgDesc _gdk_windowing_args[] = {
{ "display", GDK_ARG_STRING, &_gdk_display_name, (GdkArgFunc)NULL },
{ "sync", GDK_ARG_BOOL, &gdk_synchronize, (GdkArgFunc)NULL },
@@ -251,6 +263,7 @@ gdk_pointer_grab (GdkWindow * window,
Window xwindow;
Window xconfine_to;
Cursor xcursor;
+ unsigned long serial;
int i;
g_return_val_if_fail (window != NULL, 0);
@@ -260,6 +273,7 @@ gdk_pointer_grab (GdkWindow * window,
cursor_private = (GdkCursorPrivate*) cursor;
xwindow = GDK_WINDOW_XID (window);
+ serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
xconfine_to = None;
@@ -308,7 +322,11 @@ gdk_pointer_grab (GdkWindow * window,
}
if (return_val == GrabSuccess)
- _gdk_xgrab_window = (GdkWindowObject *)window;
+ {
+ gdk_pointer_xgrab_window = (GdkWindowObject *)window;
+ gdk_pointer_xgrab_serial = serial;
+ gdk_pointer_xgrab_owner_events = owner_events;
+ }
return gdk_x11_convert_grab_status (return_val);
}
@@ -334,7 +352,7 @@ gdk_pointer_ungrab (guint32 time)
_gdk_input_ungrab_pointer (time);
XUngrabPointer (gdk_display, time);
- _gdk_xgrab_window = NULL;
+ gdk_pointer_xgrab_window = NULL;
}
/*
@@ -355,9 +373,38 @@ gdk_pointer_ungrab (guint32 time)
gboolean
gdk_pointer_is_grabbed (void)
{
- return _gdk_xgrab_window != NULL;
+ return gdk_pointer_xgrab_window != NULL;
}
+/**
+ * gdk_pointer_grab_info_libgtk_only:
+ * @grab_window: location to store current grab window
+ * @owner_events: location to store boolean indicating whether
+ * the @owner_events flag to gdk_pointer_grab() was %TRUE.
+ *
+ * Determines information about the current pointer grab.
+ * This is not public API and must not be used by applications.
+ *
+ * Return value: %TRUE if this application currently has the
+ * pointer grabbed.
+ **/
+gboolean
+gdk_pointer_grab_info_libgtk_only (GdkWindow **grab_window,
+ gboolean *owner_events)
+{
+ if (gdk_pointer_xgrab_window)
+ {
+ if (grab_window)
+ *grab_window = (GdkWindow *)gdk_pointer_xgrab_window;
+ if (owner_events)
+ *owner_events = gdk_pointer_xgrab_owner_events;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
/*
*--------------------------------------------------------------
* gdk_keyboard_grab
@@ -384,10 +431,13 @@ gdk_keyboard_grab (GdkWindow * window
guint32 time)
{
gint return_val;
+ unsigned long serial;
g_return_val_if_fail (window != NULL, 0);
g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
+ serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
+
if (!GDK_WINDOW_DESTROYED (window))
{
#ifdef G_ENABLE_DEBUG
@@ -404,6 +454,13 @@ gdk_keyboard_grab (GdkWindow * window
else
return_val = AlreadyGrabbed;
+ if (return_val == GrabSuccess)
+ {
+ gdk_keyboard_xgrab_window = (GdkWindowObject *)window;
+ gdk_keyboard_xgrab_serial = serial;
+ gdk_keyboard_xgrab_owner_events = owner_events;
+ }
+
return gdk_x11_convert_grab_status (return_val);
}
@@ -426,6 +483,94 @@ void
gdk_keyboard_ungrab (guint32 time)
{
XUngrabKeyboard (gdk_display, time);
+ gdk_keyboard_xgrab_window = NULL;
+}
+
+/**
+ * gdk_keyboard_grab_info_libgtk_only:
+ * @grab_window: location to store current grab window
+ * @owner_events: location to store boolean indicating whether
+ * the @owner_events flag to gdk_keyboard_grab() was %TRUE.
+ *
+ * Determines information about the current keyboard grab.
+ * This is not public API and must not be used by applications.
+ *
+ * Return value: %TRUE if this application currently has the
+ * keyboard grabbed.
+ **/
+gboolean
+gdk_keyboard_grab_info_libgtk_only (GdkWindow **grab_window,
+ gboolean *owner_events)
+{
+ if (gdk_keyboard_xgrab_window)
+ {
+ if (grab_window)
+ *grab_window = (GdkWindow *)gdk_keyboard_xgrab_window;
+ if (owner_events)
+ *owner_events = gdk_keyboard_xgrab_owner_events;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * _gdk_xgrab_check_unmap:
+ * @window: a #GdkWindow
+ * @serial: serial from Unmap event (or from NextRequest(display)
+ * if the unmap is being done by this client.)
+ *
+ * Checks to see if an unmap request or event causes the current
+ * grab window to become not viewable, and if so, clear the
+ * the pointer we keep to it.
+ **/
+void
+_gdk_xgrab_check_unmap (GdkWindow *window,
+ gulong serial)
+{
+ if (gdk_pointer_xgrab_window && serial >= gdk_pointer_xgrab_serial)
+ {
+ GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
+ GdkWindowObject *tmp = gdk_pointer_xgrab_window;
+
+
+ while (tmp && tmp != private)
+ tmp = tmp->parent;
+
+ if (tmp)
+ gdk_pointer_xgrab_window = NULL;
+ }
+
+ if (gdk_keyboard_xgrab_window && serial >= gdk_keyboard_xgrab_serial)
+ {
+ GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
+ GdkWindowObject *tmp = gdk_keyboard_xgrab_window;
+
+
+ while (tmp && tmp != private)
+ tmp = tmp->parent;
+
+ if (tmp)
+ gdk_keyboard_xgrab_window = NULL;
+ }
+}
+
+/**
+ * _gdk_xgrab_check_destroy:
+ * @window: a #GdkWindow
+ *
+ * Checks to see if window is the current grab window, and if
+ * so, clear the current grab window.
+ **/
+void
+_gdk_xgrab_check_destroy (GdkWindow *window)
+{
+ if ((GdkWindowObject *)window == gdk_pointer_xgrab_window)
+ gdk_pointer_xgrab_window = NULL;
+
+ if ((GdkWindowObject *)window == gdk_keyboard_xgrab_window)
+ gdk_keyboard_xgrab_window = NULL;
}
/*
Index: gdk/x11/gdkprivate-x11.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkprivate-x11.h,v
retrieving revision 1.18
diff -u -p -r1.18 gdkprivate-x11.h
--- gdk/x11/gdkprivate-x11.h 2002/01/04 05:58:00 1.18
+++ gdk/x11/gdkprivate-x11.h 2002/03/02 01:27:36
@@ -165,6 +165,10 @@ GC _gdk_x11_gc_flush (GdkGC *gc);
void _gdk_x11_initialize_locale (void);
+void _gdk_xgrab_check_unmap (GdkWindow *window,
+ gulong serial);
+void _gdk_xgrab_check_destroy (GdkWindow *window);
+
extern GdkDrawableClass _gdk_x11_drawable_class;
extern Window _gdk_root_window;
extern gboolean _gdk_use_xshm;
@@ -176,10 +180,6 @@ extern GdkAtom _gdk_selection_property
extern gchar *_gdk_display_name;
extern Window _gdk_leader_window;
-
-extern GdkWindowObject *_gdk_xgrab_window; /* Window that currently holds the
- * x pointer grab
- */
/* Used to detect not-up-to-date keymap */
extern guint _gdk_keymap_serial;
Index: gdk/x11/gdkwindow-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.c,v
retrieving revision 1.142
diff -u -p -r1.142 gdkwindow-x11.c
--- gdk/x11/gdkwindow-x11.c 2002/02/28 21:09:04 1.142
+++ gdk/x11/gdkwindow-x11.c 2002/03/02 01:27:37
@@ -169,6 +169,8 @@ gdk_window_impl_x11_finalize (GObject *o
wrapper = (GdkWindowObject*) draw_impl->wrapper;
+ _gdk_xgrab_check_destroy (GDK_WINDOW (wrapper));
+
if (!GDK_WINDOW_DESTROYED (wrapper))
{
gdk_xid_table_remove (draw_impl->xid);
@@ -794,7 +796,9 @@ _gdk_windowing_window_destroy (GdkWindow
}
}
else if (!recursing && !foreign_destroy)
- XDestroyWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
+ {
+ XDestroyWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
+ }
}
/* This function is called when the XWindow is really gone.
@@ -819,6 +823,8 @@ gdk_window_destroy_notify (GdkWindow *wi
gdk_xid_table_remove (GDK_WINDOW_XID (window));
if (window_impl->focus_window)
gdk_xid_table_remove (window_impl->focus_window);
+
+ _gdk_xgrab_check_destroy (window);
gdk_drawable_unref (window);
}
@@ -990,6 +996,13 @@ gdk_window_hide (GdkWindow *window)
g_return_if_fail (window != NULL);
private = (GdkWindowObject*) window;
+
+ /* We'll get the unmap notify eventually, and handle it then,
+ * but checking here makes things more consistent if we are
+ * just doing stuff ourself.
+ */
+ _gdk_xgrab_check_unmap (window,
+ NextRequest (GDK_WINDOW_XDISPLAY (window)));
/* You can't simply unmap toplevel windows. */
switch (private->window_type)
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.197
diff -u -p -r1.197 gtkmain.c
--- gtk/gtkmain.c 2002/02/21 17:13:38 1.197
+++ gtk/gtkmain.c 2002/03/02 01:27:37
@@ -980,6 +980,121 @@ gtk_main_iteration_do (gboolean blocking
return TRUE;
}
+/* private libgtk to libgdk interfaces
+ */
+gboolean gdk_pointer_grab_info_libgtk_only (GdkWindow **grab_window,
+ gboolean *owner_events);
+gboolean gdk_keyboard_grab_info_libgtk_only (GdkWindow **grab_window,
+ gboolean *owner_events);
+
+static void
+rewrite_events_translate (GdkWindow *old_window,
+ GdkWindow *new_window,
+ gdouble *x,
+ gdouble *y)
+{
+ gint old_origin_x, old_origin_y;
+ gint new_origin_x, new_origin_y;
+
+ gdk_window_get_origin (old_window, &old_origin_x, &old_origin_y);
+ gdk_window_get_origin (new_window, &new_origin_x, &new_origin_y);
+
+ *x += new_origin_x - old_origin_x;
+ *y += new_origin_y - old_origin_y;
+}
+
+GdkEvent *
+rewrite_event_for_window (GdkEvent *event,
+ GdkWindow *new_window)
+{
+ event = gdk_event_copy (event);
+
+ switch (event->type)
+ {
+ case GDK_SCROLL:
+ rewrite_events_translate (event->any.window,
+ new_window,
+ &event->scroll.x, &event->scroll.y);
+ break;
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ rewrite_events_translate (event->any.window,
+ new_window,
+ &event->button.x, &event->button.y);
+ break;
+ case GDK_MOTION_NOTIFY:
+ rewrite_events_translate (event->any.window,
+ new_window,
+ &event->motion.x, &event->motion.y);
+ break;
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ case GDK_PROXIMITY_IN:
+ case GDK_PROXIMITY_OUT:
+ break;
+
+ default:
+ return event;
+ }
+
+ g_object_unref (event->any.window);
+ event->any.window = g_object_ref (new_window);
+
+ return event;
+}
+
+/* If there is a pointer or keyboard grab in effect with owner_events = TRUE,
+ * then what X11 does is deliver the event normally if it was going to this
+ * client, otherwise, delivers it in terms of the grab window. This function
+ * rewrites events to the effect that events going to the same window group
+ * are delivered normally, otherwise, the event is delivered in terms of the
+ * grab window.
+ */
+static GdkEvent *
+rewrite_event_for_grabs (GdkEvent *event)
+{
+ GdkWindow *grab_window;
+ GtkWidget *event_widget, *grab_widget;;
+ gboolean owner_events;
+
+ switch (event->type)
+ {
+ case GDK_SCROLL:
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ case GDK_MOTION_NOTIFY:
+ case GDK_PROXIMITY_IN:
+ case GDK_PROXIMITY_OUT:
+ if (!gdk_pointer_grab_info_libgtk_only (&grab_window, &owner_events) ||
+ !owner_events)
+ return NULL;
+ break;
+
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ if (!gdk_keyboard_grab_info_libgtk_only (&grab_window, &owner_events) ||
+ !owner_events)
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ event_widget = gtk_get_event_widget (event);
+ gdk_window_get_user_data (grab_window, (void**) &grab_widget);
+
+ if (grab_widget &&
+ gtk_main_get_window_group (grab_widget) != gtk_main_get_window_group (event_widget))
+ return rewrite_event_for_window (event, grab_window);
+ else
+ return NULL;
+}
+
void
gtk_main_do_event (GdkEvent *event)
{
@@ -987,6 +1102,7 @@ gtk_main_do_event (GdkEvent *event)
GtkWidget *grab_widget;
GtkWindowGroup *window_group;
GdkEvent *next_event;
+ GdkEvent *rewritten_event = NULL;
GList *tmp_list;
/* If there are any events pending then get the next one.
@@ -1045,14 +1161,24 @@ gtk_main_do_event (GdkEvent *event)
return;
}
+
+ /* If pointer or keyboard grabs are in effect, munge the events
+ * so that each window group looks like a separate app.
+ */
+ rewritten_event = rewrite_event_for_grabs (event);
+ if (rewritten_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);
-
/* If there is a grab in effect...
*/
if (window_group->grabs)
@@ -1198,6 +1324,9 @@ gtk_main_do_event (GdkEvent *event)
tmp_list = current_events;
current_events = g_list_remove_link (current_events, tmp_list);
g_list_free_1 (tmp_list);
+
+ if (rewritten_event)
+ gdk_event_free (rewritten_event);
}
gboolean
Index: gtk/gtkrange.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkrange.c,v
retrieving revision 1.81
diff -u -p -r1.81 gtkrange.c
--- gtk/gtkrange.c 2002/02/27 22:41:33 1.81
+++ gtk/gtkrange.c 2002/03/02 01:27:38
@@ -1048,6 +1048,8 @@ range_grab_add (GtkRange *range,
{
/* we don't actually gtk_grab, since a button is down */
+ gtk_grab_add (GTK_WIDGET (range));
+
range->layout->grab_location = location;
range->layout->grab_button = button;
@@ -1058,6 +1060,8 @@ range_grab_add (GtkRange *range,
static void
range_grab_remove (GtkRange *range)
{
+ gtk_grab_remove (GTK_WIDGET (range));
+
range->layout->grab_location = MOUSE_OUTSIDE;
range->layout->grab_button = 0;
@@ -1305,8 +1309,18 @@ gtk_range_button_release (GtkWidget
{
GtkRange *range = GTK_RANGE (widget);
- range->layout->mouse_x = event->x;
- range->layout->mouse_y = event->y;
+ if (event->window == range->event_window)
+ {
+ range->layout->mouse_x = event->x;
+ range->layout->mouse_y = event->y;
+ }
+ else
+ {
+ gdk_window_get_pointer (range->event_window,
+ &range->layout->mouse_x,
+ &range->layout->mouse_y,
+ NULL);
+ }
if (range->layout->grab_button == event->button)
{
@@ -1318,7 +1332,7 @@ gtk_range_button_release (GtkWidget
gtk_range_remove_step_timer (range);
if (grab_location == MOUSE_SLIDER)
- update_slider_position (range, event->x, event->y);
+ update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
/* Flush any pending discontinuous/delayed updates */
gtk_range_update_value (range);
Index: tests/testgtk.c
===================================================================
RCS file: /cvs/gnome/gtk+/tests/testgtk.c,v
retrieving revision 1.298
diff -u -p -r1.298 testgtk.c
--- tests/testgtk.c 2002/03/01 01:05:11 1.298
+++ tests/testgtk.c 2002/03/02 01:27:38
@@ -11699,6 +11699,8 @@ main (int argc, char *argv[])
GtkBindingSet *binding_set;
int i;
gboolean done_benchmarks = FALSE;
+ GtkWidget *other_window;
+ GtkWindowGroup *other_group;
srand (time (NULL));
@@ -11776,6 +11778,12 @@ main (int argc, char *argv[])
create_main_window ();
+ other_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_add_events (other_window, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
+ other_group = gtk_window_group_new ();
+ gtk_window_group_add_window (other_group, GTK_WINDOW (other_window));
+ gtk_widget_show_all (other_window);
+
gtk_main ();
if (1)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]