diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index bc515b540b..d07b6a5acc 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -491,7 +491,7 @@ gdk_display_real_get_window_at_pointer (GdkDisplay *display, GdkWindow *window; gint x, y; - window = _gdk_windowing_window_at_pointer (display, &x, &y); + window = _gdk_windowing_window_at_pointer (display, &x, &y, NULL); /* This might need corrections, as the native window returned may contain client side children */ @@ -737,6 +737,17 @@ generate_grab_broken_event (GdkWindow *window, } } +static void +set_window_under_pointer (GdkDisplay *display, + GdkWindow *window) +{ + if (display->pointer_info.window_under_pointer) + g_object_unref (display->pointer_info.window_under_pointer); + display->pointer_info.window_under_pointer = window; + if (window) + g_object_ref (window); +} + void _gdk_display_set_has_pointer_grab (GdkDisplay *display, GdkWindow *window, @@ -747,51 +758,89 @@ _gdk_display_set_has_pointer_grab (GdkDisplay *display, guint32 time, gboolean implicit) { - int wx, wy; + GdkWindow *pointer_window, *src_toplevel, *dest_toplevel, *src_window; - /* Normal GRAB events are sent by listening for enter and leave - * events on the native event window, which is then proxied - * into the virtual windows when the events are seen. - * However, there are two cases where X will not send these events: - * * When there is already a grab on the native parent of the - * virtual grab window - * * When there is no grab, but the pointer is already in the - * native parent of the virtual grab window - * In the first case we send the right GRAB events from the grab, but - * in the second case we need to generate our own UNGRAB crossing events. - */ if (display->pointer_grab.window != NULL && display->pointer_grab.window != window) { generate_grab_broken_event (GDK_WINDOW (display->pointer_grab.window), FALSE, display->pointer_grab.implicit, window); - - /* Re-grabbing. Pretend we have no grab for now so that - the GRAB events get delivered */ - display->pointer_grab.window = NULL; - _gdk_syntesize_crossing_events (display, - display->pointer_grab.window, - window, - GDK_CROSSING_GRAB, - /* These may be stale... */ - display->pointer_info.toplevel_x, - display->pointer_info.toplevel_y, - display->pointer_info.state, - time, TRUE, TRUE); } - else if (_gdk_windowing_window_at_pointer (display, &wx, &wy) == native_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 (!implicit) { - _gdk_syntesize_crossing_events (display, - display->pointer_info.window_under_pointer, - window, - GDK_CROSSING_GRAB, - /* These may be stale... */ - display->pointer_info.toplevel_x, - display->pointer_info.toplevel_y, - display->pointer_info.state, - time, TRUE, TRUE); + GdkScreen *screen; + GdkWindowObject *w; + int x, y; + GdkModifierType state; + + /* 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; + if (src_window != window) + { + /* _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); + else + src_toplevel = NULL; + dest_toplevel = gdk_window_get_toplevel (window); + + if (src_toplevel == NULL || + src_toplevel == dest_toplevel) + { + _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, FALSE); + } + else + { + _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, FALSE); + _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, FALSE); + } + } + + /* !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) + set_window_under_pointer (display, NULL); } display->pointer_grab.window = window; @@ -810,10 +859,12 @@ _gdk_display_unset_has_pointer_grab (GdkDisplay *display, gboolean do_grab_one_pointer_release_event, guint32 time) { - int wx, wy; + 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; @@ -821,35 +872,99 @@ _gdk_display_unset_has_pointer_grab (GdkDisplay *display, if (do_grab_one_pointer_release_event) display->pointer_grab.grab_one_pointer_release_event = display->pointer_grab.window; - /* We need to set this to null befor syntesizing events to make sure they get - delivered to anything but the grab window */ + /* Set first so crossing events get sent */ display->pointer_grab.window = NULL; - /* Normal UNGRAB events are sent by listening for enter and leave - * events on the native event window, which is then proxied - * into the virtual windows when the events are seen. - * However, there are two cases where X will not send these events: - * * When this ungrab is due to a new grab on the native window that - * is a parent of the currently grabbed virtual window - * * When there is no new grab, and the pointer is already in the - * grabbed virtual windows parent native window - * In the first case we send the right GRAB events from the grab, but - * in the second case we need to generate our own UNGRAB crossing events. - */ + 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; - if (_gdk_windowing_window_at_pointer (display, &wx, &wy) == old_native_grab_window) + /* 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) { - _gdk_syntesize_crossing_events (display, - old_grab_window, - display->pointer_info.window_under_pointer, - GDK_CROSSING_UNGRAB, - /* These may be stale... */ - display->pointer_info.toplevel_x, - display->pointer_info.toplevel_y, - display->pointer_info.state, - time, TRUE, TRUE); + /* Convert to toplevel */ + w = (GdkWindowObject *)pointer_window; + while (w->parent->window_type != GDK_WINDOW_ROOT) + { + 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 child window */ + pointer_window = + _gdk_window_find_descendant_at ((GdkWindow *)w, + x, y, + NULL, NULL); } + + if (pointer_window == NULL) + { + _gdk_syntesize_crossing_events (display, + old_grab_window, + NULL, + GDK_CROSSING_UNGRAB, + x, y, state, + time, + NULL, FALSE); + } + else + { + 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); + + 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, FALSE); + } + 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, FALSE); + _gdk_syntesize_crossing_events (display, + NULL, + pointer_window, + GDK_CROSSING_UNGRAB, + x, y, state, + time, + NULL, FALSE); + } + } + } + + /* We're now ungrabbed, update the window_under_pointer */ + set_window_under_pointer (display, pointer_window); + if (implicit) generate_grab_broken_event (old_grab_window, FALSE, implicit, diff --git a/gdk/gdkdisplay.h b/gdk/gdkdisplay.h index 790bc0b7f3..e4ca845ebc 100644 --- a/gdk/gdkdisplay.h +++ b/gdk/gdkdisplay.h @@ -58,14 +58,18 @@ typedef struct GdkWindow *grab_one_pointer_release_event; } GdkPointerGrabInfo; -/* Tracks information about which window the pointer is in and - * at what position the mouse is. This is useful when we need - * to synthesize events later. +/* 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, + * so in the case of a grab, either with owner_events==FALSE or with the + * pointer in no clients window the x/y coordinates may actually be outside + * the window. */ typedef struct { - GdkWindow *window_under_pointer; - gdouble toplevel_x, toplevel_y; + GdkWindow *toplevel_under_pointer; /* The toplevel window with mouse inside, tracked via native events */ + GdkWindow *window_under_pointer; /* The window that last got sent a normal enter event */ + gdouble toplevel_x, toplevel_y; guint32 state; } GdkPointerWindowInfo; diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 0acd617069..51d3f7de95 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -345,7 +345,12 @@ GdkWindow* _gdk_windowing_window_get_pointer (GdkDisplay *display, GdkModifierType *mask); GdkWindow* _gdk_windowing_window_at_pointer (GdkDisplay *display, gint *win_x, - gint *win_y); + gint *win_y, + GdkModifierType *mask); +void _gdk_windowing_got_event (GdkDisplay *display, + GList *event_link, + GdkEvent *event); + /* Return the number of bits-per-pixel for images of the specified depth. */ gint _gdk_windowing_get_bits_for_depth (GdkDisplay *display, @@ -521,8 +526,8 @@ void _gdk_syntesize_crossing_events (GdkDisplay *display, gint toplevel_y, GdkModifierType mask, guint32 time_, - gboolean do_first, - gboolean do_last); + GdkEvent *event_in_queue, + gboolean before_event); void _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window); diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index de0f463f11..49a58d39f1 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -1159,6 +1159,7 @@ _gdk_window_destroy_hierarchy (GdkWindow *window, GdkWindowObject *temp_private; GdkWindow *temp_window; GdkScreen *screen; + GdkDisplay *display; GList *children; GList *tmp; @@ -1169,11 +1170,13 @@ _gdk_window_destroy_hierarchy (GdkWindow *window, if (GDK_WINDOW_DESTROYED (window)) return; + display = gdk_drawable_get_display (GDK_DRAWABLE (window)); screen = gdk_drawable_get_screen (GDK_DRAWABLE (window)); temp_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window); if (temp_window == window) g_object_set_qdata (G_OBJECT (screen), quark_pointer_window, NULL); + switch (GDK_WINDOW_TYPE (window)) { case GDK_WINDOW_ROOT: @@ -1284,6 +1287,12 @@ _gdk_window_destroy_hierarchy (GdkWindow *window, gdk_window_redirect_free (private->redirect); private->redirect = NULL; + + if (display->pointer_info.toplevel_under_pointer == window) + { + g_object_unref (display->pointer_info.toplevel_under_pointer); + display->pointer_info.toplevel_under_pointer = NULL; + } } break; } @@ -6615,13 +6624,9 @@ static gboolean point_in_window (GdkWindowObject *window, double x, double y) { - int w, h; - - gdk_drawable_get_size (GDK_DRAWABLE (window), &w, &h); - return - x >= 0 && x < w && - y >= 0 && y < h; + x >= 0 && x < window->width && + y >= 0 && y < window->height; } static void @@ -6856,22 +6861,22 @@ is_motion_type (GdkEventType type) type == GDK_LEAVE_NOTIFY; } -static GdkWindow * -find_common_ancestor (GdkWindow *win1, - GdkWindow *win2) +static GdkWindowObject * +find_common_ancestor (GdkWindowObject *win1, + GdkWindowObject *win2) { GdkWindowObject *tmp; GList *path1 = NULL, *path2 = NULL; GList *list1, *list2; - tmp = GDK_WINDOW_OBJECT (win1); + tmp = win1; while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT) { path1 = g_list_prepend (path1, tmp); tmp = tmp->parent; } - tmp = GDK_WINDOW_OBJECT (win2); + tmp = win2; while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT) { path2 = g_list_prepend (path2, tmp); @@ -6890,7 +6895,7 @@ find_common_ancestor (GdkWindow *win1, g_list_free (path1); g_list_free (path2); - return GDK_WINDOW (tmp); + return tmp; } GdkEvent * @@ -6995,6 +7000,54 @@ _gdk_make_event (GdkWindow *window, return event; } +static void +send_crossing_event (GdkDisplay *display, + GdkWindowObject *toplevel, + GdkWindowObject *window, + GdkEventType type, + GdkCrossingMode mode, + GdkNotifyType notify_type, + GdkWindow *subwindow, + gint toplevel_x, + gint toplevel_y, + GdkModifierType mask, + guint32 time_, + GdkEvent *event_in_queue, + gboolean before_event) +{ + GdkEvent *event; + guint32 event_mask; + + if (display->pointer_grab.window != NULL && + !display->pointer_grab.owner_events && + (GdkWindow *)window != display->pointer_grab.window) + return; + + if (type == GDK_LEAVE_NOTIFY) + event_mask = GDK_LEAVE_NOTIFY_MASK; + else + event_mask = GDK_ENTER_NOTIFY_MASK; + + if (window->event_mask & event_mask) + { + event = _gdk_make_event ((GdkWindow *)window, type, event_in_queue, before_event); + event->crossing.time = time_; + event->crossing.subwindow = subwindow; + if (subwindow) + g_object_ref (subwindow); + convert_toplevel_coords_to_window ((GdkWindow *)window, + toplevel_x, toplevel_y, + &event->crossing.x, &event->crossing.y); + event->crossing.x_root = toplevel_x + toplevel->x; + event->crossing.y_root = toplevel_y + toplevel->y; + event->crossing.mode = mode; + event->crossing.detail = notify_type; + event->crossing.focus = FALSE; + event->crossing.state = mask; + } +} + + /* The coordinates are in the toplevel window that src/dest are in. * src and dest are always (if != NULL) in the same toplevel, as * we get a leave-notify and set the window_under_pointer to null @@ -7009,159 +7062,134 @@ _gdk_syntesize_crossing_events (GdkDisplay *display, gint toplevel_y, GdkModifierType mask, guint32 time_, - gboolean do_first, - gboolean do_last) + GdkEvent *event_in_queue, + gboolean before_event) { - GdkWindow *c; - GdkWindow *win, *last, *next; - GdkEvent *event; + GdkWindowObject *c; + GdkWindowObject *win, *last, *next; GList *path, *list; gboolean non_linear; - GdkWindow *a; - GdkWindow *b; - GdkWindow *event_win; + GdkWindowObject *a; + GdkWindowObject *b; GdkWindowObject *toplevel; + GdkNotifyType notify_type; /* TODO: Don't send events to toplevel, as we get those from the windowing system */ - a = src; - b = dest; + a = (GdkWindowObject *)src; + b = (GdkWindowObject *)dest; if (a == b) return; /* No crossings generated between src and dest */ c = find_common_ancestor (a, b); + non_linear = (c != a) && (c != b); if (a) /* There might not be a source (i.e. if no previous pointer_in_window) */ { - toplevel = (GdkWindowObject *)gdk_window_get_toplevel (a); + toplevel = (GdkWindowObject *)gdk_window_get_toplevel ((GdkWindow *)a); /* Traverse up from a to (excluding) c sending leave events */ - - event_win = get_target_window_for_pointer_event (display, a, GDK_LEAVE_NOTIFY, mask); - if (do_first && event_win) - { - event = _gdk_make_event (event_win, GDK_LEAVE_NOTIFY, NULL, FALSE); - event->crossing.time = time_; - event->crossing.subwindow = NULL; - convert_toplevel_coords_to_window (event_win, - toplevel_x, toplevel_y, - &event->crossing.x, &event->crossing.y); - event->crossing.x_root = toplevel_x + toplevel->x; - event->crossing.y_root = toplevel_y + toplevel->y; - event->crossing.mode = mode; - if (non_linear) - event->crossing.detail = GDK_NOTIFY_NONLINEAR; - else if (c == a) - event->crossing.detail = GDK_NOTIFY_INFERIOR; - else - event->crossing.detail = GDK_NOTIFY_ANCESTOR; - event->crossing.focus = FALSE; - event->crossing.state = mask; - } - + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR; + else if (c == a) + notify_type = GDK_NOTIFY_INFERIOR; + else + notify_type = GDK_NOTIFY_ANCESTOR; + send_crossing_event (display, toplevel, + a, GDK_LEAVE_NOTIFY, + mode, + notify_type, + NULL, + toplevel_x, toplevel_y, + mask, time_, + event_in_queue, before_event); + if (c != a) { + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR_VIRTUAL; + else + notify_type = GDK_NOTIFY_VIRTUAL; + last = a; - win = GDK_WINDOW (GDK_WINDOW_OBJECT (a)->parent); + win = a->parent; while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT) { - event_win = get_target_window_for_pointer_event (display, win, GDK_LEAVE_NOTIFY, mask); - if (event_win) - { - event = _gdk_make_event (event_win, GDK_LEAVE_NOTIFY, NULL, FALSE); - event->crossing.time = time_; - event->crossing.subwindow = g_object_ref (last); - convert_toplevel_coords_to_window (event_win, - toplevel_x, toplevel_y, - &event->crossing.x, &event->crossing.y); - event->crossing.x_root = toplevel_x + toplevel->x; - event->crossing.y_root = toplevel_y + toplevel->y; - event->crossing.mode = mode; - if (non_linear) - event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL; - else - event->crossing.detail = GDK_NOTIFY_VIRTUAL; - event->crossing.focus = FALSE; - event->crossing.state = mask; - } + send_crossing_event (display, toplevel, + win, GDK_LEAVE_NOTIFY, + mode, + notify_type, + (GdkWindow *)last, + toplevel_x, toplevel_y, + mask, time_, + event_in_queue, before_event); + last = win; - win = GDK_WINDOW (GDK_WINDOW_OBJECT (win)->parent); + win = win->parent; } } } if (b) /* Might not be a dest, e.g. if we're moving out of the window */ { - toplevel = (GdkWindowObject *)gdk_window_get_toplevel (b); + toplevel = (GdkWindowObject *)gdk_window_get_toplevel ((GdkWindow *)b); /* Traverse down from c to b */ if (c != b) { path = NULL; - win = GDK_WINDOW (GDK_WINDOW_OBJECT (b)->parent); + win = b->parent; while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT) { path = g_list_prepend (path, win); - win = GDK_WINDOW( GDK_WINDOW_OBJECT (win)->parent); + win = win->parent; } + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR_VIRTUAL; + else + notify_type = GDK_NOTIFY_VIRTUAL; + list = path; while (list) { - win = (GdkWindow *)list->data; + win = (GdkWindowObject *)list->data; list = g_list_next (list); if (list) - next = (GdkWindow *)list->data; + next = (GdkWindowObject *)list->data; else next = b; - - event_win = get_target_window_for_pointer_event (display, win, GDK_ENTER_NOTIFY, mask); - if (event_win) - { - event = _gdk_make_event (event_win, GDK_ENTER_NOTIFY, NULL, FALSE); - event->crossing.time = time_; - event->crossing.subwindow = g_object_ref (next); - convert_toplevel_coords_to_window (event_win, - toplevel_x, toplevel_y, - &event->crossing.x, &event->crossing.y); - event->crossing.x_root = toplevel_x + toplevel->x; - event->crossing.y_root = toplevel_y + toplevel->y; - event->crossing.mode = mode; - if (non_linear) - event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL; - else - event->crossing.detail = GDK_NOTIFY_VIRTUAL; - event->crossing.focus = FALSE; - event->crossing.state = mask; - } + + send_crossing_event (display, toplevel, + win, GDK_ENTER_NOTIFY, + mode, + notify_type, + (GdkWindow *)next, + toplevel_x, toplevel_y, + mask, time_, + event_in_queue, before_event); } g_list_free (path); } - - event_win = get_target_window_for_pointer_event (display, b, GDK_ENTER_NOTIFY, mask); - if (do_last && event_win) - { - event = _gdk_make_event (event_win, GDK_ENTER_NOTIFY, NULL, FALSE); - event->crossing.time = time_; - event->crossing.subwindow = NULL; - convert_toplevel_coords_to_window (event_win, - toplevel_x, toplevel_y, - &event->crossing.x, &event->crossing.y); - event->crossing.x_root = toplevel_x + toplevel->x; - event->crossing.y_root = toplevel_y + toplevel->y; - event->crossing.mode = mode; - if (non_linear) - event->crossing.detail = GDK_NOTIFY_NONLINEAR; - else if (c == a) - event->crossing.detail = GDK_NOTIFY_ANCESTOR; - else - event->crossing.detail = GDK_NOTIFY_INFERIOR; - event->crossing.focus = FALSE; - event->crossing.state = mask; - } - } + + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR; + else if (c == a) + notify_type = GDK_NOTIFY_ANCESTOR; + else + notify_type = GDK_NOTIFY_INFERIOR; + + send_crossing_event (display, toplevel, + b, GDK_ENTER_NOTIFY, + mode, + notify_type, + NULL, + toplevel_x, toplevel_y, + mask, time_, + event_in_queue, before_event); + } } static GdkWindow * @@ -7176,146 +7204,182 @@ get_toplevel (GdkWindow *w) return GDK_WINDOW (private); } +/* Returns the window inside the event window with the pointer in it + * at the specified coordinates, or NULL if its not in any child of + * the toplevel. It also takes into account !owner_events grabs. + */ +static GdkWindow * +get_pointer_window (GdkDisplay *display, + GdkWindow *event_window, + gdouble toplevel_x, + gdouble toplevel_y) +{ + GdkWindow *pointer_window; + + if (event_window == display->pointer_info.toplevel_under_pointer) + pointer_window = + _gdk_window_find_descendant_at (event_window, + toplevel_x, toplevel_y, + NULL, NULL); + else + pointer_window = NULL; + + if (display->pointer_grab.window != NULL && + !display->pointer_grab.owner_events && + pointer_window != display->pointer_grab.window) + pointer_window = NULL; + + return pointer_window; +} + +static void +set_window_under_pointer (GdkDisplay *display, + GdkWindow *window) +{ + if (display->pointer_info.window_under_pointer) + g_object_unref (display->pointer_info.window_under_pointer); + display->pointer_info.window_under_pointer = window; + if (window) + g_object_ref (window); +} + void _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window) { GdkDisplay *display; GdkWindow *changed_toplevel; - GdkWindow *pointer_toplevel; + GdkWindow *pointer_toplevel, *grab_toplevel; GdkWindow *new_window_under_pointer; changed_toplevel = get_toplevel (changed_window); display = gdk_drawable_get_display (changed_window); - if (display->pointer_info.window_under_pointer) + if (changed_toplevel == display->pointer_info.toplevel_under_pointer) { - pointer_toplevel = get_toplevel (display->pointer_info.window_under_pointer); - - if (pointer_toplevel == changed_toplevel) + new_window_under_pointer = + get_pointer_window (display, changed_toplevel, + display->pointer_info.toplevel_x, + display->pointer_info.toplevel_y); + if (new_window_under_pointer != + display->pointer_info.window_under_pointer) { - new_window_under_pointer = - _gdk_window_find_descendant_at (pointer_toplevel, - display->pointer_info.toplevel_x, - display->pointer_info.toplevel_y, - NULL, NULL); - if (new_window_under_pointer != - display->pointer_info.window_under_pointer) - { - _gdk_syntesize_crossing_events (display, - display->pointer_info.window_under_pointer, - new_window_under_pointer, - GDK_CROSSING_NORMAL, - display->pointer_info.toplevel_x, - display->pointer_info.toplevel_y, - display->pointer_info.state, - GDK_CURRENT_TIME, - TRUE, TRUE); - - if (display->pointer_info.window_under_pointer) - g_object_unref (display->pointer_info.window_under_pointer); - display->pointer_info.window_under_pointer = NULL; - if (new_window_under_pointer) - display->pointer_info.window_under_pointer = g_object_ref (new_window_under_pointer); - } + _gdk_syntesize_crossing_events (display, + display->pointer_info.window_under_pointer, + new_window_under_pointer, + GDK_CROSSING_NORMAL, + display->pointer_info.toplevel_x, + display->pointer_info.toplevel_y, + display->pointer_info.state, + GDK_CURRENT_TIME, + NULL, FALSE); + set_window_under_pointer (display, new_window_under_pointer); } } } +/* Don't use for crossing events */ +static GdkWindow * +get_event_window (GdkDisplay *display, + GdkWindow *pointer_window, + GdkEventType type, + GdkModifierType mask) +{ + guint evmask; + GdkWindow *grab_window; + GdkWindowObject *w; + + if ((display->pointer_grab.window != NULL && !display->pointer_grab.owner_events) || + (type == GDK_BUTTON_RELEASE && display->pointer_grab.grab_one_pointer_release_event)) + { + evmask = display->pointer_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; + + if (evmask & type_masks[type]) + return grab_window; + else + return NULL; + } + + w = (GdkWindowObject *)pointer_window; + while (w != NULL) + { + evmask = w->event_mask; + evmask = update_evmask_for_button_motion (evmask, mask); + + if (evmask & type_masks[type]) + return (GdkWindow *)w; + + w = w->parent; + } + + if (display->pointer_grab.window != NULL && + display->pointer_grab.owner_events) + { + evmask = display->pointer_grab.event_mask; + evmask = update_evmask_for_button_motion (evmask, mask); + + if (evmask & type_masks[type]) + return display->pointer_grab.window; + else + return NULL; + } + + return NULL; +} + static gboolean proxy_pointer_event (GdkDisplay *display, - GdkWindow *pointer_window, - gdouble toplevel_x, - gdouble toplevel_y, GdkEvent *source_event) { - GdkWindow *event_window; - GdkWindow *event_win, *cursor_window; - gboolean crossing_event; + GdkWindow *toplevel_window; + GdkWindow *pointer_window; + GdkWindow *cursor_window; gboolean sent_motion; GdkEvent *event; guint state; + gdouble toplevel_x, toplevel_y; guint32 time_; - event_window = source_event->any.window; - + toplevel_window = source_event->any.window; + gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y); gdk_event_get_state (source_event, &state); time_ = gdk_event_get_time (source_event); - crossing_event = - source_event->type == GDK_ENTER_NOTIFY || - source_event->type == GDK_LEAVE_NOTIFY; - - if (crossing_event) + pointer_window = get_pointer_window (display, toplevel_window, toplevel_x, toplevel_y); + if (display->pointer_info.window_under_pointer != pointer_window) { - GdkEventCrossing *crossing = &source_event->crossing; + /* Either a toplevel crossing notify that ended up inside a child window, + or a motion notify that got into another child window */ + /* Different than last time, send crossing events */ - if (crossing->mode == GDK_CROSSING_GRAB) - { - if (crossing->type == GDK_LEAVE_NOTIFY && - display->pointer_info.window_under_pointer != NULL) - { - _gdk_syntesize_crossing_events (display, - display->pointer_info.window_under_pointer, - event_window, - GDK_CROSSING_GRAB, - toplevel_x, toplevel_y, state, time_, - TRUE, FALSE); - } + _gdk_syntesize_crossing_events (display, + display->pointer_info.window_under_pointer, + pointer_window, + GDK_CROSSING_NORMAL, + toplevel_x, toplevel_y, + state, time_, + source_event, source_event->type == GDK_LEAVE_NOTIFY); - if (crossing->type == GDK_ENTER_NOTIFY && - display->pointer_grab.window != NULL) - { - _gdk_syntesize_crossing_events (display, - event_window, - display->pointer_grab.window, - GDK_CROSSING_GRAB, - toplevel_x, toplevel_y, state, time_, - FALSE, TRUE); - } - } - - if (crossing->mode == GDK_CROSSING_UNGRAB) - { - if (crossing->type == GDK_LEAVE_NOTIFY && - display->pointer_grab.window != NULL) - { - _gdk_syntesize_crossing_events (display, - display->pointer_grab.window, - event_window, - GDK_CROSSING_UNGRAB, - toplevel_x, toplevel_y, state, time_, - TRUE, FALSE); - } - - if (crossing->type == GDK_ENTER_NOTIFY && - display->pointer_info.window_under_pointer != NULL) - { - _gdk_syntesize_crossing_events (display, - event_window, - display->pointer_info.window_under_pointer, - GDK_CROSSING_UNGRAB, - toplevel_x, toplevel_y, state, time_, - FALSE, TRUE); - } - } + set_window_under_pointer (display, pointer_window); } - - 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; - - /* TODO: set cursor from cursor_window, or grab cursor */ - - sent_motion = FALSE; - if (!crossing_event && - (display->pointer_info.window_under_pointer == pointer_window || - (display->pointer_grab.window != NULL && - !display->pointer_grab.owner_events))) + else if (source_event->type == GDK_MOTION_NOTIFY) { - /* send motion events */ - event_win = get_target_window_for_pointer_event (display, pointer_window, GDK_MOTION_NOTIFY, state); + GdkWindow *event_win; + + event_win = get_event_window (display, + pointer_window, + source_event->type, + state); + if (event_win) { sent_motion = TRUE; @@ -7334,43 +7398,52 @@ proxy_pointer_event (GdkDisplay *display, } } - _gdk_syntesize_crossing_events (display, - display->pointer_info.window_under_pointer, - pointer_window, - GDK_CROSSING_NORMAL, - toplevel_x, toplevel_y, - state, time_, - TRUE, TRUE); + /* 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 move event from parent if we sent a motion event */ - return source_event->type == GDK_MOTION_NOTIFY && sent_motion; + + /* unlink all move events from queue. + We handle our own, including our emulated masks. */ + return TRUE; } static gboolean -proxy_button_event (GdkWindow *pointer_window, - gdouble toplevel_x, - gdouble toplevel_y, - GdkEvent *source_event) +proxy_button_event (GdkEvent *source_event) { + GdkWindow *toplevel_window; GdkWindow *event_win; + GdkWindow *pointer_window; GdkEvent *event; guint state; guint32 time_; GdkEventType type; + gdouble toplevel_x, toplevel_y; GdkDisplay *display; type = source_event->any.type; + toplevel_window = source_event->any.window; + gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y); gdk_event_get_state (source_event, &state); time_ = gdk_event_get_time (source_event); display = gdk_drawable_get_display (source_event->any.window); - + if ((type == GDK_BUTTON_PRESS || type == GDK_SCROLL) && - pointer_window != NULL && display->pointer_grab.window == source_event->any.window && display->pointer_grab.implicit && !display->pointer_grab.converted_implicit) { - if (pointer_window != source_event->any.window) + pointer_window = + _gdk_window_find_descendant_at (toplevel_window, + toplevel_x, toplevel_y, + NULL, NULL); + + if (pointer_window != NULL && + pointer_window != source_event->any.window) _gdk_display_set_has_pointer_grab (display, pointer_window, display->pointer_grab.native_window, @@ -7382,57 +7455,51 @@ proxy_button_event (GdkWindow *pointer_window, display->pointer_grab.converted_implicit = TRUE; } - event_win = get_target_window_for_pointer_event (display, - pointer_window, - type, - state); + pointer_window = get_pointer_window (display, toplevel_window, toplevel_x, toplevel_y); + + event_win = get_event_window (display, + pointer_window, + type, + state); if (event_win == NULL) return TRUE; - if (event_win != source_event->any.window) + event = _gdk_make_event (event_win, type, source_event, FALSE); + + switch (type) { - event = _gdk_make_event (event_win, type, source_event, FALSE); - - switch (type) - { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - event->button.button = source_event->button.button; - convert_toplevel_coords_to_window (event_win, - toplevel_x, toplevel_y, - &event->button.x, &event->button.y); - event->button.x_root = source_event->button.x_root; - event->button.y_root = source_event->button.y_root; - event->button.state = state; - event->button.device = source_event->button.device; - - if (type == GDK_BUTTON_PRESS) - _gdk_event_button_generate (display, event); - return TRUE; - - case GDK_SCROLL: - event->scroll.direction = source_event->scroll.direction; - convert_toplevel_coords_to_window (event_win, - toplevel_x, toplevel_y, - &event->scroll.x, &event->scroll.y); - event->scroll.x_root = source_event->scroll.x_root; - event->scroll.y_root = source_event->scroll.y_root; - event->scroll.state = state; - event->scroll.device = source_event->scroll.device; - return TRUE; - - default: - return FALSE; - } - } - else - { - /* Same window as original window, keep the event */ - if (source_event->type == GDK_BUTTON_PRESS) - _gdk_event_button_generate (display, source_event); + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + event->button.button = source_event->button.button; + convert_toplevel_coords_to_window (event_win, + toplevel_x, toplevel_y, + &event->button.x, &event->button.y); + event->button.x_root = source_event->button.x_root; + event->button.y_root = source_event->button.y_root; + event->button.state = state; + event->button.device = source_event->button.device; + + if (type == GDK_BUTTON_PRESS) + _gdk_event_button_generate (display, event); + return TRUE; + + case GDK_SCROLL: + event->scroll.direction = source_event->scroll.direction; + convert_toplevel_coords_to_window (event_win, + toplevel_x, toplevel_y, + &event->scroll.x, &event->scroll.y); + event->scroll.x_root = source_event->scroll.x_root; + event->scroll.y_root = source_event->scroll.y_root; + event->scroll.state = state; + event->scroll.device = source_event->scroll.device; + return TRUE; + + default: return FALSE; } + + return TRUE; /* Always unlink original, we want to obey the emulated event mask */ } void @@ -7441,9 +7508,8 @@ _gdk_windowing_got_event (GdkDisplay *display, GdkEvent *event) { GdkWindow *event_window; - GdkWindow *pointer_window; GdkWindowObject *event_private; - gdouble x, y, child_x, child_y; + gdouble x, y; gboolean unlink_event; event_window = event->any.window; @@ -7466,40 +7532,78 @@ _gdk_windowing_got_event (GdkDisplay *display, event_private->window_type); /* We should only get these events on toplevel windows */ - g_warning ("got unexpected event of type %s on non-toplevel window (type %s)", + g_warning ("got unexpected event of type %s on non-toplevel window (gtype %s, type %d)", event_type_value->value_name, - window_type_value->value_name); + window_type_value->value_name, + GDK_WINDOW_TYPE (event_window)); return; } + + if ((event->type == GDK_ENTER_NOTIFY || + event->type == GDK_LEAVE_NOTIFY) && + (event->crossing.mode == GDK_CROSSING_GRAB || + event->crossing.mode == GDK_CROSSING_UNGRAB)) + { + /* We synthesize all crossing events due to grabs are synthesized, + * so we ignore the native ones. This is partly to get easier non-X + * portability, and because of problems with race conditions due to + * the cached state in the client and the real state in the xserver + * when grabbing. + */ + + /* Some grab in another window (by perhaps another client) did a grab. + * The pointer is still in this window, but we won't get told if it + * moves out, so NULL this now and set it back to the right value at + * ungrab time. + */ + if (event->type == GDK_LEAVE_NOTIFY && + event->crossing.mode == GDK_CROSSING_GRAB) + { + g_assert (display->pointer_info.toplevel_under_pointer == event_window); + g_object_unref (display->pointer_info.toplevel_under_pointer); + display->pointer_info.toplevel_under_pointer = NULL; + } + + /* We ended up in this window after some (perhaps other clients) + grab, so update the toplevel_under_window state */ + if (event->type == GDK_ENTER_NOTIFY && + event->crossing.mode == GDK_CROSSING_UNGRAB) + { + if (display->pointer_info.toplevel_under_pointer) + g_object_unref (display->pointer_info.toplevel_under_pointer); + display->pointer_info.toplevel_under_pointer = g_object_ref (event_window); + } + + return TRUE; + } - gdk_event_get_coords (event, &x, &y); - pointer_window = _gdk_window_find_descendant_at (event_window, x, y, - &child_x, - &child_y); - unlink_event = FALSE; - if (is_motion_type (event->type)) - unlink_event = proxy_pointer_event (display, - pointer_window, - x, y, - event); - else if (is_button_type (event->type)) - unlink_event = proxy_button_event (pointer_window, x, y, - event); - /* Store last pointer window and position/state */ + if (event->type == GDK_ENTER_NOTIFY && + event->crossing.detail != GDK_NOTIFY_INFERIOR) + { + g_assert (display->pointer_info.toplevel_under_pointer == NULL); + display->pointer_info.toplevel_under_pointer = g_object_ref (event_window); + } + else if (event->type == GDK_LEAVE_NOTIFY && + event->crossing.detail != GDK_NOTIFY_INFERIOR) + { + g_assert (display->pointer_info.toplevel_under_pointer == event_window); + g_object_unref (display->pointer_info.toplevel_under_pointer); + display->pointer_info.toplevel_under_pointer = NULL; + } + gdk_event_get_coords (event, &x, &y); display->pointer_info.toplevel_x = x; display->pointer_info.toplevel_y = y; gdk_event_get_state (event, &display->pointer_info.state); + - if (pointer_window != display->pointer_info.window_under_pointer) - { - if (display->pointer_info.window_under_pointer) - g_object_unref (display->pointer_info.window_under_pointer); - display->pointer_info.window_under_pointer = NULL; - if (pointer_window) - display->pointer_info.window_under_pointer = g_object_ref (pointer_window); - } + unlink_event = FALSE; + if (is_motion_type (event->type)) + unlink_event = proxy_pointer_event (display, + event); + else if (is_button_type (event->type)) + unlink_event = proxy_button_event (event); if (unlink_event) { diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index 2a8c32c779..4f02dc2cbd 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -987,7 +987,7 @@ gdk_event_translate (GdkDisplay *display, display_x11->keyboard_xgrab_window != NULL && ( /* The window is not a descendant of the grabbed window */ - !is_parent_of (display_x11->keyboard_xgrab_window, window) || + !is_parent_of ((GdkWindow *)display_x11->keyboard_xgrab_window, window) || /* Or owner event is false */ !display_x11->keyboard_xgrab_owner_events ) diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index 21fe7d6890..27ab04535c 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -3185,7 +3185,8 @@ gdk_display_warp_pointer (GdkDisplay *display, GdkWindow* _gdk_windowing_window_at_pointer (GdkDisplay *display, gint *win_x, - gint *win_y) + gint *win_y, + GdkModifierType *mask) { GdkWindow *window; GdkScreen *screen; @@ -3294,7 +3295,9 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display, window = gdk_window_lookup_for_display (display, xwindow_last); *win_x = window ? winx : -1; *win_y = window ? winy : -1; - + if (mask) + *mask = xmask; + return window; }