From ec32b2aaa3ea1c7162209e56bae950ff16def577 Mon Sep 17 00:00:00 2001 From: Richard Hult Date: Wed, 6 Jun 2007 18:42:07 +0000 Subject: [PATCH] Rework how we get the event window: don't get the window from the NSEvent, 2007-06-06 Richard Hult * gdk/quartz/gdkevents-quartz.c (find_mouse_window_for_ns_event) (find_window_for_ns_event): Rework how we get the event window: don't get the window from the NSEvent, always use the mouse window instead. Fix mouse window tracking by only using MouseExited for non-gdk windows, and to always use the NSEvent window to get the right gdk window in MouseEntered. Add comments to the code. Fixes bug #350460. svn path=/trunk/; revision=18065 --- ChangeLog | 10 ++ gdk/quartz/gdkevents-quartz.c | 170 +++++++++++++++++++++++++--------- 2 files changed, 138 insertions(+), 42 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9b0a0f0ce2..8247b9a361 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-06-06 Richard Hult + + * gdk/quartz/gdkevents-quartz.c (find_mouse_window_for_ns_event) + (find_window_for_ns_event): Rework how we get the event window: + don't get the window from the NSEvent, always use the mouse window + instead. Fix mouse window tracking by only using MouseExited for + non-gdk windows, and to always use the NSEvent window to get the + right gdk window in MouseEntered. Add comments to the code. Fixes bug + #350460. + 2007-06-06 Richard Hult * gdk/quartz/gdkevents-quartz.c (get_converted_window_coordinates): diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c index 8d0e9439ee..536b0c65ec 100644 --- a/gdk/quartz/gdkevents-quartz.c +++ b/gdk/quartz/gdkevents-quartz.c @@ -937,54 +937,67 @@ get_converted_window_coordinates (GdkWindow *in_window, out_x, out_y); } -/* Given a mouse NSEvent, returns the window in which the pointer - * position from the event is. The returned coordinates are relative - * to the found window, and normal GDK coordinates, not Quartz. - */ +/* Given a mouse NSEvent (must be a mouse event for a GDK window), + * finds the subwindow over which the pointer is located. Returns + * coordinates relative to the found window. If no window is found, + * returns NULL. +*/ static GdkWindow * -find_window_for_mouse_ns_event (NSEvent *nsevent, +find_mouse_window_for_ns_event (NSEvent *nsevent, gint *x_ret, gint *y_ret) { - NSWindow *nswindow; - GdkWindow *toplevel; + GdkWindow *event_toplevel; + GdkWindow *mouse_toplevel; + GdkWindow *mouse_window; NSPoint point; gint x_tmp, y_tmp; - GdkWindow *found_window; - - nswindow = [nsevent window]; - toplevel = [(GdkQuartzView *)[nswindow contentView] gdkWindow]; + event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow]; point = [nsevent locationInWindow]; x_tmp = point.x; /* Flip the y coordinate. */ - if (toplevel == _gdk_root) + if (event_toplevel == _gdk_root) y_tmp = _gdk_quartz_window_get_inverted_screen_y (point.y); else { GdkWindowImplQuartz *impl; - impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl); + impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl); y_tmp = impl->height - point.y; } - found_window = _gdk_quartz_window_find_child (toplevel, x_tmp, y_tmp); + if (!current_mouse_window) + return NULL; - /* Translate the coordinates so they are relative to the found - * window. - */ - if (found_window) - get_child_coordinates_from_ancestor (toplevel, + mouse_toplevel = gdk_window_get_toplevel (current_mouse_window); + + get_converted_window_coordinates (event_toplevel, + x_tmp, y_tmp, + mouse_toplevel, + &x_tmp, &y_tmp); + + mouse_window = _gdk_quartz_window_find_child (mouse_toplevel, x_tmp, y_tmp); + if (!mouse_window) + { + /* This happens for events on the window title and window + * buttons (and the desktop). + */ + return NULL; + } + + if (mouse_window != mouse_toplevel) + get_child_coordinates_from_ancestor (mouse_toplevel, x_tmp, y_tmp, - found_window, + mouse_window, &x_tmp, &y_tmp); *x_ret = x_tmp; *y_ret = y_tmp; - return found_window; + return mouse_window; } /* This function finds the correct window to send an event to, taking @@ -1016,24 +1029,22 @@ find_window_for_ns_event (NSEvent *nsevent, { GdkWindow *mouse_window; - mouse_window = find_window_for_mouse_ns_event (nsevent, x, y); + mouse_window = find_mouse_window_for_ns_event (nsevent, x, y); - if (!mouse_window) - mouse_window = _gdk_root; + /* We don't need to handle the case where we don't find a mouse + * window (i.e. after leaving a gdk toplevel and not entering a + * new one) here, it's covered by NSMouseExited events. + */ + if (mouse_window && mouse_window != current_mouse_window) + synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y); - if (_gdk_quartz_pointer_grab_window) - { - if (mouse_window != current_mouse_window) - synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y); - } - else - { - if (current_mouse_window != mouse_window) - { - synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y); - _gdk_quartz_events_update_cursor (mouse_window); - } - } + /* FIXME: Does this check really work as intended? It seems like + * we sometimes get a cursor update when we shouldn't, for + * example during an implicit grab on a GtkButton, and dragging + * the mouse over a link button. + */ + if (mouse_window && !_gdk_quartz_pointer_grab_window) + _gdk_quartz_events_update_cursor (mouse_window); } switch (event_type) @@ -1070,7 +1081,7 @@ find_window_for_ns_event (NSEvent *nsevent, { if (pointer_grab_owner_events) { - mouse_window = find_window_for_mouse_ns_event (nsevent, x, y); + mouse_window = find_mouse_window_for_ns_event (nsevent, x, y); event_mask = get_event_mask_from_ns_event (nsevent); real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE); @@ -1116,7 +1127,7 @@ find_window_for_ns_event (NSEvent *nsevent, else { /* The non-grabbed case. */ - mouse_window = find_window_for_mouse_ns_event (nsevent, x, y); + mouse_window = find_mouse_window_for_ns_event (nsevent, x, y); event_mask = get_event_mask_from_ns_event (nsevent); real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE); @@ -1136,15 +1147,90 @@ find_window_for_ns_event (NSEvent *nsevent, case NSMouseEntered: { - GdkWindow *mouse_window; + GdkWindow *event_toplevel; + GdkWindow *mouse_window; + NSPoint point; + gint x_tmp, y_tmp; + + event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow]; + point = [nsevent locationInWindow]; + + x_tmp = point.x; + + /* Flip the y coordinate. */ + if (event_toplevel == _gdk_root) + y_tmp = _gdk_quartz_window_get_inverted_screen_y (point.y); + else + { + GdkWindowImplQuartz *impl; + + impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl); + y_tmp = impl->height - point.y; + } + + /* This is the only case where we actually use the window from + * the event since we need to know which toplevel we entered + * so it can be tracked properly. + */ + mouse_window = _gdk_quartz_window_find_child (event_toplevel, x_tmp, y_tmp); + + /* Treat unknown windows (including title bar/buttons, + * desktop) as the root. + */ + if (!mouse_window) + mouse_window = _gdk_root; - mouse_window = find_window_for_mouse_ns_event (nsevent, x, y); synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y); } break; case NSMouseExited: - synthesize_crossing_events (_gdk_root, GDK_CROSSING_NORMAL, nsevent, *x, *y); + { + GdkWindow *event_toplevel; + GdkWindow *mouse_window; + NSPoint point; + gint x_tmp, y_tmp; + gint x_orig, y_orig; + + /* We get mouse exited when leaving toplevels. We only use + * this when leaving from a window to the root window. The + * other case is handled above by checking the motion/button + * events, or getting a MouseEntered for another GDK window. + * + * The reason we don't use MouseExited for other windows is + * that quartz first delivers the entered event and then the + * exited which is the opposite from what we need. + */ + + /* Check if the root window has a child at this position, if + * so ignore the event. + */ + event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow]; + point = [nsevent locationInWindow]; + + x_tmp = point.x; + + /* Flip the y coordinate. */ + if (event_toplevel == _gdk_root) + y_tmp = _gdk_quartz_window_get_inverted_screen_y (point.y); + else + { + GdkWindowImplQuartz *impl; + + impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl); + y_tmp = impl->height - point.y; + } + + if (gdk_window_get_origin (event_toplevel, &x_orig, &y_orig)) + { + x_tmp += x_orig; + y_tmp += y_orig; + } + + mouse_window = _gdk_quartz_window_find_child (_gdk_root, x_tmp, y_tmp); + if (mouse_window == _gdk_root) + synthesize_crossing_events (_gdk_root, GDK_CROSSING_NORMAL, nsevent, *x, *y); + } break; case NSKeyDown: