From 26cbf87d7d70e9eae4dea4667aee40c9d154184f Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Sat, 31 Jan 2009 19:42:44 +0100 Subject: [PATCH] New approach for grab tracking code We try to track the exact grab state, i.e. whats valid on the client now and whats comming soon via the xserver roundtrip (and when). --- gdk/gdkdisplay.c | 585 ++++++++++++++++++++++++++------------- gdk/gdkdisplay.h | 21 +- gdk/gdkinternals.h | 49 +++- gdk/gdkoffscreenwindow.c | 5 +- gdk/gdkwindow.c | 195 +++++++------ gdk/x11/gdkasync.c | 5 +- gdk/x11/gdkasync.h | 5 +- gdk/x11/gdkdisplay-x11.c | 37 ++- gdk/x11/gdkevents-x11.c | 2 +- gdk/x11/gdkmain-x11.c | 111 ++++---- 10 files changed, 630 insertions(+), 385 deletions(-) diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index a8f027e73..48d79e854 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -750,228 +750,434 @@ generate_grab_broken_event (GdkWindow *window, } } -void -_gdk_display_set_has_pointer_grab (GdkDisplay *display, - GdkWindow *window, - GdkWindow *native_window, - gboolean owner_events, - GdkEventMask event_mask, - unsigned long serial, - guint32 time, - gboolean implicit) +/* Get the pointer grab in effects for events we just sent */ +GdkPointerGrabInfo * +_gdk_display_get_active_pointer_grab (GdkDisplay *display) { - GdkWindow *src_toplevel, *dest_toplevel, *src_window; + GdkPointerGrabInfo *info; + + if (display->pointer_grabs == NULL) + return NULL; + + info = display->pointer_grabs->data; + + if (info->activated) + return info; - if (display->pointer_grab.window != NULL && - display->pointer_grab.window != window) + return NULL; +} + + +GdkPointerGrabInfo * +_gdk_display_get_last_pointer_grab (GdkDisplay *display) +{ + GList *l; + + l = display->pointer_grabs; + + if (l == NULL) + return NULL; + + while (l->next != NULL) + l = l->next; + + return (GdkPointerGrabInfo *)l->data; +} + + +GdkPointerGrabInfo * +_gdk_display_add_pointer_grab (GdkDisplay *display, + GdkWindow *window, + GdkWindow *native_window, + gboolean owner_events, + GdkEventMask event_mask, + unsigned long serial_start, + guint32 time, + gboolean implicit) +{ + GdkPointerGrabInfo *info, *other_info; + GList *l; + + info = g_new0 (GdkPointerGrabInfo, 1); + + info->window = g_object_ref (window); + info->native_window = g_object_ref (native_window); + info->serial_start = serial_start; + info->serial_end = G_MAXULONG; + info->owner_events = owner_events; + info->event_mask = event_mask; + info->time = time; + info->implicit = implicit; + info->converted_implicit = FALSE; + + /* Find the first grab that has a larger start time (if any) and insert + * before that. I.E we insert after already existing grabs with same + * start time */ + for (l = display->pointer_grabs; l != NULL; l = l->next) { - generate_grab_broken_event (GDK_WINDOW (display->pointer_grab.window), - FALSE, display->pointer_grab.implicit, - window); + other_info = l->data; + + if (info->serial_start < other_info->serial_start) + break; + } + display->pointer_grabs = + g_list_insert_before (display->pointer_grabs, l, info); + + /* Make sure the new grab end before next grab */ + if (l) + { + other_info = l->data; + info->serial_end = other_info->serial_start; } - /* We need to generate crossing events for the grab. - * However, there are never any crossing events for implicit grabs - * TODO: ... Actually, this could happen if the pointer window doesn't have button mask so a parent gets the event... - */ - if (!implicit) + /* Find any previous grab and update its end time */ + l = g_list_find (display->pointer_grabs, info); + l = l->prev; + if (l) { - int x, y; - GdkModifierType state; + other_info = l->data; + other_info->serial_end = serial_start; + } + + return info; +} + +static void +free_pointer_grab (GdkPointerGrabInfo *info) +{ + g_object_unref (info->window); + g_object_unref (info->native_window); + g_free (info); +} + +/* _gdk_syntesize_crossing_events only works inside one toplevel. + This function splits things into two calls if needed, converting the + coordinates to the right toplevel */ +static void +synthesize_crossing_events (GdkDisplay *display, + GdkWindow *src_window, + GdkWindow *dest_window, + GdkCrossingMode crossing_mode, + guint32 time, + gulong serial) +{ + GdkWindow *src_toplevel, *dest_toplevel; + GdkModifierType state; + int x, y; + + if (src_window) + src_toplevel = gdk_window_get_toplevel (src_window); + else + src_toplevel = NULL; + if (dest_window) + dest_toplevel = gdk_window_get_toplevel (dest_window); + else + dest_toplevel = NULL; + + if (src_toplevel == NULL && dest_toplevel == NULL) + return; + + if (src_toplevel == NULL || + src_toplevel == dest_toplevel) + { + /* Same toplevels */ + _gdk_windowing_window_get_pointer (display, + dest_toplevel, + &x, &y, &state); + _gdk_syntesize_crossing_events (display, + src_window, + dest_window, + crossing_mode, + x, y, state, + time, + NULL, + serial); + } + else if (dest_toplevel == NULL) + { + _gdk_windowing_window_get_pointer (display, + src_toplevel, + &x, &y, &state); + _gdk_syntesize_crossing_events (display, + src_window, + NULL, + crossing_mode, + x, y, state, + time, + NULL, + serial); + } + else + { + /* Different toplevels */ + _gdk_windowing_window_get_pointer (display, + src_toplevel, + &x, &y, &state); + _gdk_syntesize_crossing_events (display, + src_window, + NULL, + crossing_mode, + x, y, state, + time, + NULL, + serial); + _gdk_windowing_window_get_pointer (display, + dest_toplevel, + &x, &y, &state); + _gdk_syntesize_crossing_events (display, + NULL, + dest_window, + crossing_mode, + x, y, state, + time, + NULL, + serial); + } +} - /* We send GRAB crossing events from the window under the pointer to the - grab window. Except if there is an old grab then we start from that */ - if (display->pointer_grab.window) - src_window = display->pointer_grab.window; - else - src_window = display->pointer_info.window_under_pointer; - /* Unset any current grab to make sure we send the events */ - display->pointer_grab.window = NULL; +static void +switch_to_pointer_grab (GdkDisplay *display, + GdkPointerGrabInfo *grab, + GdkPointerGrabInfo *last_grab, + guint32 time, + gulong serial) +{ + GdkWindow *src_window, *pointer_window; + GdkWindowObject *w; + GList *old_grabs; + GdkModifierType state; + int x, y; + + /* Temporarily unset pointer to make sure we send the crossing events below */ + old_grabs = display->pointer_grabs; + display->pointer_grabs = NULL; + + if (grab) + { + /* New grab is in effect */ - if (src_window != window) + /* We need to generate crossing events for the grab. + * However, there are never any crossing events for implicit grabs + * TODO: ... Actually, this could happen if the pointer window + * doesn't have button mask so a parent gets the event... + */ + if (!grab->implicit) { - /* _gdk_syntesize_crossing_events only works inside one toplevel, split into two calls if needed */ - if (src_window) - src_toplevel = gdk_window_get_toplevel (src_window); + /* We send GRAB crossing events from the window under the pointer to the + grab window. Except if there is an old grab then we start from that */ + if (last_grab) + src_window = last_grab->window; else - src_toplevel = NULL; - dest_toplevel = gdk_window_get_toplevel (window); + src_window = display->pointer_info.window_under_pointer; - if (src_toplevel == NULL || - src_toplevel == dest_toplevel) + if (src_window != grab->window) { - _gdk_windowing_window_get_pointer (display, - dest_toplevel, - &x, &y, &state); - _gdk_syntesize_crossing_events (display, - src_window, - window, - GDK_CROSSING_GRAB, - x, y, state, - time, - NULL); + synthesize_crossing_events (display, + src_window, grab->window, + GDK_CROSSING_GRAB, time, serial); } - else + + /* !owner_event Grabbing a window that we're not inside, current status is + now NULL (i.e. outside grabbed window) */ + if (!grab->owner_events && display->pointer_info.window_under_pointer != grab->window) + _gdk_display_set_window_under_pointer (display, NULL); + } + + grab->activated = TRUE; + } + else if (last_grab) + { + pointer_window = _gdk_windowing_window_at_pointer (display, &x, &y, &state); + if (pointer_window != NULL && + (GDK_WINDOW_TYPE (pointer_window) == GDK_WINDOW_ROOT || + GDK_WINDOW_TYPE (pointer_window) == GDK_WINDOW_FOREIGN)) + pointer_window = NULL; + + /* We force checked what window we're in, so we need to + * update the toplevel_under_pointer info, as that won't get told of + * this change. + */ + if (display->pointer_info.toplevel_under_pointer) + g_object_unref (display->pointer_info.toplevel_under_pointer); + display->pointer_info.toplevel_under_pointer = NULL; + + if (pointer_window) + { + /* Convert to toplevel */ + w = (GdkWindowObject *)pointer_window; + while (w->parent->window_type != GDK_WINDOW_ROOT) { - _gdk_windowing_window_get_pointer (display, - src_toplevel, - &x, &y, &state); - _gdk_syntesize_crossing_events (display, - src_window, - NULL, - GDK_CROSSING_GRAB, - x, y, state, - time, - NULL); - _gdk_windowing_window_get_pointer (display, - dest_toplevel, - &x, &y, &state); - _gdk_syntesize_crossing_events (display, - NULL, - window, - GDK_CROSSING_GRAB, - x, y, state, - time, - NULL); + x += w->x; + y += w->y; + w = w->parent; } + + /* w is now toplevel and x,y in toplevel coords */ + display->pointer_info.toplevel_under_pointer = g_object_ref (w); + + /* Find (possibly virtual) child window */ + pointer_window = + _gdk_window_find_descendant_at ((GdkWindow *)w, + x, y, + NULL, NULL); } - /* !owner_event Grabbing a window that we're not inside, current status is - now NULL (i.e. outside grabbed window) */ - if (!owner_events && display->pointer_info.window_under_pointer != window) - _gdk_display_set_window_under_pointer (display, NULL); + if (pointer_window != last_grab->window) + synthesize_crossing_events (display, + last_grab->window, pointer_window, + GDK_CROSSING_UNGRAB, time, serial); + + /* We're now ungrabbed, update the window_under_pointer */ + _gdk_display_set_window_under_pointer (display, pointer_window); + + if (last_grab->implicit_ungrab) + generate_grab_broken_event (last_grab->window, + FALSE, TRUE, + NULL); } + + display->pointer_grabs = old_grabs; - display->pointer_grab.window = window; - display->pointer_grab.native_window = native_window; - display->pointer_grab.serial = serial; - display->pointer_grab.owner_events = owner_events; - display->pointer_grab.event_mask = event_mask; - display->pointer_grab.time = time; - display->pointer_grab.implicit = implicit; - display->pointer_grab.converted_implicit = FALSE; } void -_gdk_display_unset_has_pointer_grab (GdkDisplay *display, - gboolean implicit, - gboolean do_grab_one_pointer_release_event, - guint32 time) +_gdk_display_pointer_grab_update (GdkDisplay *display, + gulong current_serial) { - GdkWindow *pointer_window, *src_toplevel, *dest_toplevel; - GdkWindow *old_grab_window; - GdkWindow *old_native_grab_window; - int x, y; - GdkModifierType state; - GdkWindowObject *w; - - old_grab_window = display->pointer_grab.window; - old_native_grab_window = display->pointer_grab.native_window; + GdkPointerGrabInfo *current_grab, *next_grab; + guint32 time; + + time = display->last_event_time; - if (old_grab_window == NULL) - return; /* This happens in the gdk_window_hide case */ + while (display->pointer_grabs != NULL) + { + current_grab = display->pointer_grabs->data; - if (do_grab_one_pointer_release_event) - display->pointer_grab.grab_one_pointer_release_event = display->pointer_grab.window; + if (current_grab->serial_start > current_serial) + return; /* Hasn't started yet */ + + if (current_grab->serial_end > current_serial || + (current_grab->serial_end == current_serial && + current_grab->grab_one_pointer_release_event)) + { + /* This one hasn't ended yet. + its the currently active one or scheduled to be active */ - /* Set first so crossing events get sent */ - display->pointer_grab.window = NULL; - - pointer_window = _gdk_windowing_window_at_pointer (display, &x, &y, &state); + if (!current_grab->activated) + switch_to_pointer_grab (display, current_grab, NULL, time, current_serial); - if (pointer_window != NULL && - (GDK_WINDOW_TYPE (pointer_window) == GDK_WINDOW_ROOT || - GDK_WINDOW_TYPE (pointer_window) == GDK_WINDOW_FOREIGN)) - pointer_window = NULL; - - /* We force checked what window we're in, so we need to - * update the toplevel_under_pointer info, as that won't get told of - * this change. - */ - if (display->pointer_info.toplevel_under_pointer) - g_object_unref (display->pointer_info.toplevel_under_pointer); - display->pointer_info.toplevel_under_pointer = NULL; - - if (pointer_window) - { - /* Convert to toplevel */ - w = (GdkWindowObject *)pointer_window; - while (w->parent->window_type != GDK_WINDOW_ROOT) + break; + } + + + next_grab = NULL; + if (display->pointer_grabs->next) { - x += w->x; - y += w->y; - w = w->parent; + /* This is the next active grab */ + next_grab = display->pointer_grabs->next->data; + + if (next_grab->serial_start > current_serial) + next_grab = NULL; /* Actually its not yet active */ } - /* w is now toplevel and x,y in toplevel coords */ + if (next_grab == NULL || + current_grab->window != next_grab->window) + generate_grab_broken_event (GDK_WINDOW (current_grab->window), + FALSE, current_grab->implicit, + next_grab? next_grab->window : NULL); - display->pointer_info.toplevel_under_pointer = g_object_ref (w); + + /* Remove old grab */ + display->pointer_grabs = + g_list_delete_link (display->pointer_grabs, + display->pointer_grabs); + + switch_to_pointer_grab (display, + next_grab, current_grab, + time, current_serial); - /* Find child window */ - pointer_window = - _gdk_window_find_descendant_at ((GdkWindow *)w, - x, y, - NULL, NULL); + free_pointer_grab (current_grab); } - - - if (pointer_window == NULL) +} + +static gboolean +is_parent_of (GdkWindow *parent, + GdkWindow *child) +{ + GdkWindow *w; + + w = child; + while (w != NULL) { - _gdk_syntesize_crossing_events (display, - old_grab_window, - NULL, - GDK_CROSSING_UNGRAB, - x, y, state, - time, - NULL); + if (w == parent) + return TRUE; + + w = gdk_window_get_parent (w); } - else + + return FALSE; +} + +static GList * +find_pointer_grab (GdkDisplay *display, + gulong serial) +{ + GdkPointerGrabInfo *grab; + GList *l; + + for (l = display->pointer_grabs; l != NULL; l = l->next) { - if (pointer_window != old_grab_window) - { - /* _gdk_syntesize_crossing_events only works inside one toplevel, split into two calls if needed */ - src_toplevel = gdk_window_get_toplevel (old_grab_window); - dest_toplevel = gdk_window_get_toplevel (pointer_window); + grab = l->data; - if (src_toplevel == dest_toplevel) - { - _gdk_syntesize_crossing_events (display, - display->pointer_info.window_under_pointer, - pointer_window, - GDK_CROSSING_UNGRAB, - x, y, state, - time, - NULL); - } - else - { - /* TODO: We're reporting the wrong coords here. They are in pointer_window toplevel coords */ - _gdk_syntesize_crossing_events (display, - display->pointer_info.window_under_pointer, - NULL, - GDK_CROSSING_UNGRAB, - x, y, state, - time, - NULL); - _gdk_syntesize_crossing_events (display, - NULL, - pointer_window, - GDK_CROSSING_UNGRAB, - x, y, state, - time, - NULL); - } - } + if (serial >= grab->serial_start && serial < grab->serial_end) + return l; } + + return NULL; +} + - /* We're now ungrabbed, update the window_under_pointer */ - _gdk_display_set_window_under_pointer (display, pointer_window); + +GdkPointerGrabInfo * +_gdk_display_has_pointer_grab (GdkDisplay *display, + gulong serial) +{ + GList *l; + + l = find_pointer_grab (display, serial); + if (l) + return l->data; - if (implicit) - generate_grab_broken_event (old_grab_window, - FALSE, implicit, - NULL); + return NULL; +} + +/* Returns true if last grab was ended */ +gboolean +_gdk_display_end_pointer_grab (GdkDisplay *display, + gulong serial, + GdkWindow *if_child, + gboolean implicit) +{ + GdkPointerGrabInfo *grab; + GList *l; + + l = find_pointer_grab (display, serial); + + if (l == NULL) + return FALSE; + + grab = l->data; + if (grab && + (if_child == NULL || + is_parent_of (grab->window, if_child))) + { + grab->serial_end = serial; + grab->implicit_ungrab = implicit; + return l->next == NULL; + } + + return FALSE; } void @@ -1055,14 +1261,18 @@ gdk_pointer_grab_info_libgtk_only (GdkDisplay *display, GdkWindow **grab_window, gboolean *owner_events) { + GdkPointerGrabInfo *info; + g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE); - if (display->pointer_grab.window) + info = _gdk_display_get_active_pointer_grab (display); + + if (info) { if (grab_window) - *grab_window = (GdkWindow *)display->pointer_grab.window; + *grab_window = info->window; if (owner_events) - *owner_events = display->pointer_grab.owner_events; + *owner_events = info->owner_events; return TRUE; } @@ -1084,10 +1294,13 @@ gdk_pointer_grab_info_libgtk_only (GdkDisplay *display, gboolean gdk_display_pointer_is_grabbed (GdkDisplay *display) { + GdkPointerGrabInfo *info; + g_return_val_if_fail (GDK_IS_DISPLAY (display), TRUE); + + info = _gdk_display_get_active_pointer_grab (display); - return (display->pointer_grab.window != NULL && - !display->pointer_grab.implicit); + return (info && !info->implicit); } #define __GDK_DISPLAY_C__ diff --git a/gdk/gdkdisplay.h b/gdk/gdkdisplay.h index c3a7d62a6..3c7bde322 100644 --- a/gdk/gdkdisplay.h +++ b/gdk/gdkdisplay.h @@ -43,21 +43,6 @@ typedef struct _GdkDisplayPointerHooks GdkDisplayPointerHooks; #define GDK_IS_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DISPLAY)) #define GDK_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DISPLAY, GdkDisplayClass)) -/* Tracks information about the pointer grab on this display */ -typedef struct -{ - GdkWindow *window; - GdkWindow *native_window; - gulong serial; - gboolean owner_events; - guint event_mask; - gboolean implicit; - gboolean converted_implicit; - guint32 time; - - GdkWindow *grab_one_pointer_release_event; -} GdkPointerGrabInfo; - /* Tracks information about the keyboard grab on this display */ typedef struct { @@ -68,7 +53,6 @@ typedef struct guint32 time; } GdkKeyboardGrabInfo; - /* Tracks information about which window and position the pointer last was in. * This is useful when we need to synthesize events later. * Note that we track toplevel_under_pointer using enter/leave events, @@ -112,9 +96,12 @@ struct _GdkDisplay gint button_x[2]; /* The last 2 button click positions. */ gint button_y[2]; - GdkPointerGrabInfo pointer_grab; + GList *pointer_grabs; GdkKeyboardGrabInfo keyboard_grab; GdkPointerWindowInfo pointer_info; + + /* Last reported event time from server */ + guint32 last_event_time; }; struct _GdkDisplayClass diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index dc7fec8c9..33822365a 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -170,6 +170,24 @@ struct _GdkEventPrivate gpointer windowing_data; }; +/* Tracks information about the pointer grab on this display */ +typedef struct +{ + GdkWindow *window; + GdkWindow *native_window; + gulong serial_start; + gulong serial_end; /* exclusive, i.e. not active on serial_end */ + gboolean owner_events; + guint event_mask; + gboolean implicit; + gboolean converted_implicit; + guint32 time; + + gboolean activated; + gboolean implicit_ungrab; + gboolean grab_one_pointer_release_event; +} GdkPointerGrabInfo; + extern GdkEventFunc _gdk_event_func; /* Callback for events */ extern gpointer _gdk_event_data; extern GDestroyNotify _gdk_event_notify; @@ -464,18 +482,24 @@ char *_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context, void _gdk_windowing_launch_failed (GAppLaunchContext *context, const char *startup_notify_id); -void _gdk_display_set_has_pointer_grab (GdkDisplay *display, - GdkWindow *window, - GdkWindow *native_window, - gboolean owner_events, - GdkEventMask event_mask, - unsigned long serial, - guint32 time, +GdkPointerGrabInfo *_gdk_display_get_active_pointer_grab (GdkDisplay *display); +void _gdk_display_pointer_grab_update (GdkDisplay *display, + gulong current_serial); +GdkPointerGrabInfo *_gdk_display_get_last_pointer_grab (GdkDisplay *display); +GdkPointerGrabInfo *_gdk_display_add_pointer_grab (GdkDisplay *display, + GdkWindow *window, + GdkWindow *native_window, + gboolean owner_events, + GdkEventMask event_mask, + unsigned long serial_start, + guint32 time, + gboolean implicit); +GdkPointerGrabInfo * _gdk_display_has_pointer_grab (GdkDisplay *display, + gulong serial); +gboolean _gdk_display_end_pointer_grab (GdkDisplay *display, + gulong serial, + GdkWindow *if_child, gboolean implicit); -void _gdk_display_unset_has_pointer_grab (GdkDisplay *display, - gboolean implicit, - gboolean do_grab_one_pointer_release_event, - guint32 time); void _gdk_display_set_has_keyboard_grab (GdkDisplay *display, GdkWindow *window, GdkWindow *native_window, @@ -518,7 +542,8 @@ void _gdk_syntesize_crossing_events (GdkDisplay *display, gint toplevel_y, GdkModifierType mask, guint32 time_, - GdkEvent *event_in_queue); + GdkEvent *event_in_queue, + gulong serial); void _gdk_display_set_window_under_pointer (GdkDisplay *display, GdkWindow *window); diff --git a/gdk/gdkoffscreenwindow.c b/gdk/gdkoffscreenwindow.c index 703dcfa7f..69126537d 100644 --- a/gdk/gdkoffscreenwindow.c +++ b/gdk/gdkoffscreenwindow.c @@ -851,7 +851,9 @@ gdk_offscreen_window_hide (GdkWindow *window) /* May need to break grabs on children */ display = gdk_drawable_get_display (window); - + + /* TODO: This needs updating to the new grab world */ +#if 0 if (display->pointer_grab.window != NULL) { if (is_parent_of (window, display->pointer_grab.window)) @@ -866,6 +868,7 @@ gdk_offscreen_window_hide (GdkWindow *window) gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); } } +#endif } static void diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 102fc903e..604febac6 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -5725,20 +5725,11 @@ gdk_window_hide (GdkWindow *window) /* May need to break grabs on children */ display = gdk_drawable_get_display (window); - if (display->pointer_grab.window != NULL) - { - if (is_parent_of (window, display->pointer_grab.window)) - { - /* Call this ourselves, even though gdk_display_pointer_ungrab - does so too, since we want to pass implicit == TRUE so the - broken grab event is generated */ - _gdk_display_unset_has_pointer_grab (display, - TRUE, - FALSE, - GDK_CURRENT_TIME); - gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); - } - } + if (_gdk_display_end_pointer_grab (display, + _gdk_windowing_window_get_next_serial (display), + window, + TRUE)) + gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); if (display->keyboard_grab.window != NULL) { @@ -6561,6 +6552,7 @@ static void update_cursor (GdkDisplay *display) { GdkWindowObject *pointer_window, *cursor_window; + GdkPointerGrabInfo *grab; pointer_window = (GdkWindowObject *)display->pointer_info.window_under_pointer; @@ -6570,14 +6562,18 @@ update_cursor (GdkDisplay *display) cursor_window->parent->window_type != GDK_WINDOW_ROOT) cursor_window = cursor_window->parent; - if (display->pointer_grab.window != NULL && - !is_parent_of (display->pointer_grab.window, (GdkWindow *)cursor_window)) - cursor_window = (GdkWindowObject *)display->pointer_grab.window; + /* We ignore the serials here and just pick the last grab + we've sent, as that would shortly be used anyway. */ + grab = _gdk_display_get_last_pointer_grab (display); + if (grab != NULL && + !is_parent_of (grab->window, (GdkWindow *)cursor_window)) + cursor_window = (GdkWindowObject *)grab->window; /* Set all cursors on toplevel, otherwise its tricky to keep track of * which native window has what cursor set. */ - GDK_WINDOW_IMPL_GET_IFACE (pointer_window->impl)->set_cursor (gdk_window_get_toplevel ((GdkWindow *)pointer_window), - cursor_window->cursor); + GDK_WINDOW_IMPL_GET_IFACE (pointer_window->impl)->set_cursor + (gdk_window_get_toplevel ((GdkWindow *)pointer_window), + cursor_window->cursor); } /** @@ -7869,14 +7865,18 @@ send_crossing_event (GdkDisplay *display, gint toplevel_y, GdkModifierType mask, guint32 time_, - GdkEvent *event_in_queue) + GdkEvent *event_in_queue, + gulong serial) { GdkEvent *event; guint32 event_mask; - - if (display->pointer_grab.window != NULL && - !display->pointer_grab.owner_events && - (GdkWindow *)window != display->pointer_grab.window) + GdkPointerGrabInfo *grab; + + grab = _gdk_display_has_pointer_grab (display, serial); + + if (grab != NULL && + !grab->owner_events && + (GdkWindow *)window != grab->window) return; if (type == GDK_LEAVE_NOTIFY) @@ -7918,7 +7918,8 @@ _gdk_syntesize_crossing_events (GdkDisplay *display, gint toplevel_y, GdkModifierType mask, guint32 time_, - GdkEvent *event_in_queue) + GdkEvent *event_in_queue, + gulong serial) { GdkWindowObject *c; GdkWindowObject *win, *last, *next; @@ -7958,7 +7959,8 @@ _gdk_syntesize_crossing_events (GdkDisplay *display, NULL, toplevel_x, toplevel_y, mask, time_, - event_in_queue); + event_in_queue, + serial); if (c != a) { @@ -7978,7 +7980,8 @@ _gdk_syntesize_crossing_events (GdkDisplay *display, (GdkWindow *)last, toplevel_x, toplevel_y, mask, time_, - event_in_queue); + event_in_queue, + serial); last = win; win = win->parent; @@ -8023,7 +8026,8 @@ _gdk_syntesize_crossing_events (GdkDisplay *display, (GdkWindow *)next, toplevel_x, toplevel_y, mask, time_, - event_in_queue); + event_in_queue, + serial); } g_list_free (path); } @@ -8043,7 +8047,8 @@ _gdk_syntesize_crossing_events (GdkDisplay *display, NULL, toplevel_x, toplevel_y, mask, time_, - event_in_queue); + event_in_queue, + serial); } } @@ -8067,9 +8072,11 @@ static GdkWindow * get_pointer_window (GdkDisplay *display, GdkWindow *event_window, gdouble toplevel_x, - gdouble toplevel_y) + gdouble toplevel_y, + gulong serial) { GdkWindow *pointer_window; + GdkPointerGrabInfo *grab; if (event_window == display->pointer_info.toplevel_under_pointer) pointer_window = @@ -8079,9 +8086,10 @@ get_pointer_window (GdkDisplay *display, else pointer_window = NULL; - if (display->pointer_grab.window != NULL && - !display->pointer_grab.owner_events && - pointer_window != display->pointer_grab.window) + grab = _gdk_display_has_pointer_grab (display, serial); + if (grab != NULL && + !grab->owner_events && + pointer_window != grab->window) pointer_window = NULL; return pointer_window; @@ -8113,16 +8121,20 @@ _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window) GdkDisplay *display; GdkWindow *changed_toplevel; GdkWindow *new_window_under_pointer; + gulong serial; + display = gdk_drawable_get_display (changed_window); + + serial = _gdk_windowing_window_get_next_serial (display); changed_toplevel = get_toplevel (changed_window); - display = gdk_drawable_get_display (changed_window); if (changed_toplevel == display->pointer_info.toplevel_under_pointer) { new_window_under_pointer = get_pointer_window (display, changed_toplevel, display->pointer_info.toplevel_x, - display->pointer_info.toplevel_y); + display->pointer_info.toplevel_y, + serial); if (new_window_under_pointer != display->pointer_info.window_under_pointer) { @@ -8134,7 +8146,8 @@ _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window) display->pointer_info.toplevel_y, display->pointer_info.state, GDK_CURRENT_TIME, - NULL); + NULL, + serial); _gdk_display_set_window_under_pointer (display, new_window_under_pointer); } } @@ -8146,27 +8159,22 @@ get_event_window (GdkDisplay *display, GdkWindow *pointer_window, GdkEventType type, GdkModifierType mask, - guint *evmask_out) + guint *evmask_out, + gulong serial) { guint evmask; GdkWindow *grab_window; GdkWindowObject *w; + GdkPointerGrabInfo *grab; + + grab = _gdk_display_has_pointer_grab (display, serial); - if ((display->pointer_grab.window != NULL && !display->pointer_grab.owner_events) || - (type == GDK_BUTTON_RELEASE && display->pointer_grab.grab_one_pointer_release_event)) + if (grab != NULL && !grab->owner_events) { - evmask = display->pointer_grab.event_mask; + evmask = grab->event_mask; evmask = update_evmask_for_button_motion (evmask, mask); - if (type == GDK_BUTTON_RELEASE && - display->pointer_grab.grab_one_pointer_release_event) - { - grab_window = display->pointer_grab.grab_one_pointer_release_event; - display->pointer_grab.grab_one_pointer_release_event = NULL; - } - else - grab_window = display->pointer_grab.window; - + grab_window = grab->window; if (evmask & type_masks[type]) { @@ -8194,17 +8202,17 @@ get_event_window (GdkDisplay *display, w = w->parent; } - if (display->pointer_grab.window != NULL && - display->pointer_grab.owner_events) + if (grab != NULL && + grab->owner_events) { - evmask = display->pointer_grab.event_mask; + evmask = grab->event_mask; evmask = update_evmask_for_button_motion (evmask, mask); if (evmask & type_masks[type]) { if (evmask_out) *evmask_out = evmask; - return display->pointer_grab.window; + return grab->window; } else return NULL; @@ -8220,7 +8228,6 @@ proxy_pointer_event (GdkDisplay *display, { GdkWindow *toplevel_window; GdkWindow *pointer_window; - GdkWindow *cursor_window; GdkEvent *event; guint state; gdouble toplevel_x, toplevel_y; @@ -8231,7 +8238,7 @@ proxy_pointer_event (GdkDisplay *display, gdk_event_get_state (source_event, &state); time_ = gdk_event_get_time (source_event); - pointer_window = get_pointer_window (display, toplevel_window, toplevel_x, toplevel_y); + pointer_window = get_pointer_window (display, toplevel_window, toplevel_x, toplevel_y, serial); if (display->pointer_info.window_under_pointer != pointer_window) { /* Either a toplevel crossing notify that ended up inside a child window, @@ -8244,7 +8251,8 @@ proxy_pointer_event (GdkDisplay *display, GDK_CROSSING_NORMAL, toplevel_x, toplevel_y, state, time_, - source_event); + source_event, + serial); _gdk_display_set_window_under_pointer (display, pointer_window); } @@ -8258,7 +8266,8 @@ proxy_pointer_event (GdkDisplay *display, pointer_window, source_event->type, state, - &evmask); + &evmask, + serial); is_hint = FALSE; @@ -8291,22 +8300,14 @@ proxy_pointer_event (GdkDisplay *display, } } - /* TODO: set cursor from cursor_window, or grab cursor */ - cursor_window = pointer_window; - if (display->pointer_grab.window && - (pointer_window == NULL || - !is_parent_of (display->pointer_grab.window, pointer_window))) - cursor_window = display->pointer_grab.window; - /* Actually, this should probably happen in synthesize crossing so it works with geometry changes */ - - /* unlink all move events from queue. We handle our own, including our emulated masks. */ return TRUE; } static gboolean -proxy_button_event (GdkEvent *source_event) +proxy_button_event (GdkEvent *source_event, + gulong serial) { GdkWindow *toplevel_window; GdkWindow *event_win; @@ -8318,6 +8319,7 @@ proxy_button_event (GdkEvent *source_event) gdouble toplevel_x, toplevel_y; GdkDisplay *display; GdkWindowObject *w; + GdkPointerGrabInfo *grab; type = source_event->any.type; toplevel_window = source_event->any.window; @@ -8326,10 +8328,11 @@ proxy_button_event (GdkEvent *source_event) time_ = gdk_event_get_time (source_event); display = gdk_drawable_get_display (source_event->any.window); + grab = _gdk_display_get_active_pointer_grab (display); + if ((type == GDK_BUTTON_PRESS || type == GDK_SCROLL) && - display->pointer_grab.window == source_event->any.window && - display->pointer_grab.implicit && - !display->pointer_grab.converted_implicit) + grab && grab->window == toplevel_window && + grab->implicit && !grab->converted_implicit) { pointer_window = _gdk_window_find_descendant_at (toplevel_window, @@ -8347,25 +8350,25 @@ proxy_button_event (GdkEvent *source_event) pointer_window = (GdkWindow *)w; if (pointer_window != NULL && - pointer_window != source_event->any.window) - _gdk_display_set_has_pointer_grab (display, - pointer_window, - display->pointer_grab.native_window, - display->pointer_grab.owner_events, - gdk_window_get_events (pointer_window), - display->pointer_grab.serial, - display->pointer_grab.time, - display->pointer_grab.implicit); - display->pointer_grab.converted_implicit = TRUE; + pointer_window != toplevel_window) + { + g_object_ref (pointer_window); + g_object_unref (grab->window); + grab->window = pointer_window; + grab->event_mask = gdk_window_get_events (pointer_window); + } + + grab->converted_implicit = TRUE; } - pointer_window = get_pointer_window (display, toplevel_window, toplevel_x, toplevel_y); + pointer_window = get_pointer_window (display, toplevel_window, + toplevel_x, toplevel_y, + serial); event_win = get_event_window (display, pointer_window, - type, - state, - NULL); + type, state, + NULL, serial); if (event_win == NULL) return TRUE; @@ -8478,7 +8481,14 @@ _gdk_windowing_got_event (GdkDisplay *display, gdouble x, y; gboolean unlink_event; guint old_state, old_button; + GdkPointerGrabInfo *button_release_grab; + if (gdk_event_get_time (event) != GDK_CURRENT_TIME) + display->last_event_time = gdk_event_get_time (event); + + _gdk_display_pointer_grab_update (display, + serial); + event_window = event->any.window; if (!event_window) return; @@ -8559,7 +8569,22 @@ _gdk_windowing_got_event (GdkDisplay *display, event, serial); else if (is_button_type (event->type)) - unlink_event = proxy_button_event (event); + unlink_event = proxy_button_event (event, + serial); + + if (event->type == GDK_BUTTON_RELEASE) + { + button_release_grab = + _gdk_display_has_pointer_grab (display, serial); + if (button_release_grab && + button_release_grab->grab_one_pointer_release_event) + { + button_release_grab->grab_one_pointer_release_event = FALSE; + button_release_grab->serial_end = serial; + button_release_grab->implicit_ungrab = TRUE; + _gdk_display_pointer_grab_update (display, serial); + } + } if (unlink_event) { diff --git a/gdk/x11/gdkasync.c b/gdk/x11/gdkasync.c index b230c1fab..e412ed2f7 100644 --- a/gdk/x11/gdkasync.c +++ b/gdk/x11/gdkasync.c @@ -118,6 +118,7 @@ struct _RoundtripState Display *dpy; _XAsyncHandler async; gulong get_input_focus_req; + GdkDisplay *display; GdkRoundTripCallback callback; gpointer data; }; @@ -758,7 +759,7 @@ roundtrip_callback_idle (gpointer data) { RoundtripState *state = (RoundtripState *)data; - state->callback (state->data); + state->callback (state->display, state->data, state->get_input_focus_req); g_free (state); @@ -790,6 +791,7 @@ roundtrip_handler (Display *dpy, True); } + if (state->callback) gdk_threads_add_idle (roundtrip_callback_idle, state); @@ -813,6 +815,7 @@ _gdk_x11_roundtrip_async (GdkDisplay *display, state = g_new (RoundtripState, 1); + state->display = display; state->dpy = dpy; state->callback = callback; state->data = data; diff --git a/gdk/x11/gdkasync.h b/gdk/x11/gdkasync.h index 407e3c847..44aa18c47 100644 --- a/gdk/x11/gdkasync.h +++ b/gdk/x11/gdkasync.h @@ -31,8 +31,9 @@ typedef struct _GdkChildInfoX11 GdkChildInfoX11; typedef void (*GdkSendXEventCallback) (Window window, gboolean success, gpointer data); -typedef void (*GdkRoundTripCallback) (gpointer data); - +typedef void (*GdkRoundTripCallback) (GdkDisplay *display, + gpointer data, + gulong serial); struct _GdkChildInfoX11 { diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c index a05bc511c..a517404e1 100644 --- a/gdk/x11/gdkdisplay-x11.c +++ b/gdk/x11/gdkdisplay-x11.c @@ -598,16 +598,11 @@ struct XPointerUngrabInfo { }; static void -pointer_ungrab_callback (gpointer _data) +pointer_ungrab_callback (GdkDisplay *display, + gpointer data, + gulong serial) { - struct XPointerUngrabInfo *data = _data; - - _gdk_display_unset_has_pointer_grab (data->display, - FALSE, - FALSE, - data->time); - - g_free (data); + _gdk_display_pointer_grab_update (display, serial); } @@ -631,30 +626,30 @@ gdk_display_pointer_ungrab (GdkDisplay *display, { Display *xdisplay; GdkDisplayX11 *display_x11; + GdkPointerGrabInfo *grab; + unsigned long serial; g_return_if_fail (GDK_IS_DISPLAY (display)); display_x11 = GDK_DISPLAY_X11 (display); xdisplay = GDK_DISPLAY_XDISPLAY (display); + + serial = NextRequest (xdisplay); _gdk_input_ungrab_pointer (display, time_); XUngrabPointer (xdisplay, time_); XFlush (xdisplay); - if (time_ == GDK_CURRENT_TIME || - display->pointer_grab.time == GDK_CURRENT_TIME || - !XSERVER_TIME_IS_LATER (display->pointer_grab.time, time_)) + grab = _gdk_display_get_last_pointer_grab (display); + if (grab && + (time_ == GDK_CURRENT_TIME || + grab->time == GDK_CURRENT_TIME || + !XSERVER_TIME_IS_LATER (grab->time, time_))) { - struct XPointerUngrabInfo *data; - - data = g_new (struct XPointerUngrabInfo, 1); - - data->display = GDK_DISPLAY_OBJECT (display_x11); - data->time = time_; - - _gdk_x11_roundtrip_async (data->display, + grab->serial_end = serial; + _gdk_x11_roundtrip_async (display, pointer_ungrab_callback, - data); + NULL); } } diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index 617e87dc1..01ed9b65c 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -2207,7 +2207,7 @@ gdk_event_translate (GdkDisplay *display, if (window) g_object_unref (window); - + return return_val; } diff --git a/gdk/x11/gdkmain-x11.c b/gdk/x11/gdkmain-x11.c index aa11db38d..b06b15e69 100644 --- a/gdk/x11/gdkmain-x11.c +++ b/gdk/x11/gdkmain-x11.c @@ -149,22 +149,11 @@ struct XPointerGrabInfo { }; static void -has_pointer_grab_callback (gpointer _data) +has_pointer_grab_callback (GdkDisplay *display, + gpointer data, + gulong serial) { - struct XPointerGrabInfo *data = _data; - - _gdk_display_set_has_pointer_grab (data->display, - data->window, - data->native_window, - data->owner_events, - data->event_mask, - data->serial, - data->time, - FALSE); - - g_object_unref (data->window); - g_object_unref (data->native_window); - g_free (data); + _gdk_display_pointer_grab_update (display, serial); } /* @@ -215,9 +204,17 @@ gdk_pointer_grab (GdkWindow * window, native = gdk_window_get_toplevel (window); + /* We need a native window for confine to to work, ensure we have one */ + if (confine_to) + gdk_window_set_has_native (confine_to, TRUE); + /* TODO: What do we do for offscreens and their children? We need to proxy the grab somehow */ if (!GDK_IS_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (native)->impl)) return GDK_GRAB_SUCCESS; + + if (!_gdk_window_has_impl (window) && + !gdk_window_is_viewable (window)) + return GDK_GRAB_NOT_VIEWABLE; if (confine_to) confine_to = _gdk_window_get_impl_window (confine_to); @@ -285,21 +282,18 @@ gdk_pointer_grab (GdkWindow * window, if (return_val == GrabSuccess) { - struct XPointerGrabInfo *data; - - data = g_new (struct XPointerGrabInfo, 1); - - data->display = GDK_DISPLAY_OBJECT (display_x11); - data->window = g_object_ref (window); - data->native_window = g_object_ref (native); - data->owner_events = owner_events; - data->event_mask = event_mask; - data->serial = serial; - data->time = time; + _gdk_display_add_pointer_grab (GDK_DISPLAY_OBJECT (display_x11), + window, + native, + owner_events, + event_mask, + serial, + time, + FALSE); - _gdk_x11_roundtrip_async (data->display, + _gdk_x11_roundtrip_async (GDK_DISPLAY_OBJECT (display_x11), has_pointer_grab_callback, - data); + NULL); } return gdk_x11_convert_grab_status (return_val); @@ -394,19 +388,8 @@ _gdk_xgrab_check_unmap (GdkWindow *window, gulong serial) { GdkDisplay *display = gdk_drawable_get_display (window); - - if (display->pointer_grab.window && - serial >= display->pointer_grab.serial) - { - GdkWindowObject *private = GDK_WINDOW_OBJECT (window); - GdkWindowObject *tmp = GDK_WINDOW_OBJECT (display->pointer_grab.native_window); - while (tmp && tmp != private) - tmp = tmp->parent; - - if (tmp) - _gdk_display_unset_has_pointer_grab (display, TRUE, FALSE, GDK_CURRENT_TIME); - } + _gdk_display_end_pointer_grab (display, serial, window, TRUE); if (display->keyboard_grab.window && serial >= display->keyboard_grab.serial) @@ -433,11 +416,21 @@ void _gdk_xgrab_check_destroy (GdkWindow *window) { GdkDisplay *display = gdk_drawable_get_display (window); - - if (window == display->pointer_grab.native_window && - display->pointer_grab.window != NULL) - _gdk_display_unset_has_pointer_grab (display, TRUE, FALSE, GDK_CURRENT_TIME); + GdkPointerGrabInfo *grab; + /* Make sure there is no lasting grab in this native + window */ + grab = _gdk_display_get_last_pointer_grab (display); + if (grab && grab->native_window == window) + { + /* We don't know the actual serial to end, but it + doesn't really matter as this only happens + after we get told of the destroy from the + server so we know its ended in the server, + just make sure its ended. */ + grab->serial_end = grab->serial_start; + } + if (window == display->keyboard_grab.native_window && display->keyboard_grab.window != NULL) _gdk_display_unset_has_keyboard_grab (display, TRUE); @@ -461,31 +454,31 @@ _gdk_xgrab_check_button_event (GdkWindow *window, XEvent *xevent) { GdkDisplay *display = gdk_drawable_get_display (window); + gulong serial = xevent->xany.serial; + GdkPointerGrabInfo *grab; /* track implicit grabs for button presses */ switch (xevent->type) { case ButtonPress: - if (!display->pointer_grab.window) + if (!_gdk_display_has_pointer_grab (display, serial)) { - _gdk_display_set_has_pointer_grab (display, - window, - window, - FALSE, - gdk_window_get_events (window), - xevent->xany.serial, - xevent->xbutton.time, - TRUE); + _gdk_display_add_pointer_grab (display, + window, + window, + FALSE, + gdk_window_get_events (window), + serial, + xevent->xbutton.time, + TRUE); } break; case ButtonRelease: - if (display->pointer_grab.window && - display->pointer_grab.implicit && + serial = serial; + grab = _gdk_display_has_pointer_grab (display, serial); + if (grab && grab->implicit && (xevent->xbutton.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (xevent->xbutton.button - 1))) == 0) - { - _gdk_display_unset_has_pointer_grab (display, TRUE, TRUE, - xevent->xbutton.time); - } + grab->grab_one_pointer_release_event = TRUE; break; default: g_assert_not_reached (); -- 2.43.2