New approach to motion event handling

This commit is contained in:
Alexander Larsson
2008-12-15 10:24:54 +01:00
committed by Alexander Larsson
parent afc81c9e64
commit b771c9924d
6 changed files with 611 additions and 380 deletions

View File

@ -491,7 +491,7 @@ gdk_display_real_get_window_at_pointer (GdkDisplay *display,
GdkWindow *window; GdkWindow *window;
gint x, y; 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 /* This might need corrections, as the native window returned
may contain client side children */ 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 void
_gdk_display_set_has_pointer_grab (GdkDisplay *display, _gdk_display_set_has_pointer_grab (GdkDisplay *display,
GdkWindow *window, GdkWindow *window,
@ -747,51 +758,89 @@ _gdk_display_set_has_pointer_grab (GdkDisplay *display,
guint32 time, guint32 time,
gboolean implicit) 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 && if (display->pointer_grab.window != NULL &&
display->pointer_grab.window != window) display->pointer_grab.window != window)
{ {
generate_grab_broken_event (GDK_WINDOW (display->pointer_grab.window), generate_grab_broken_event (GDK_WINDOW (display->pointer_grab.window),
FALSE, display->pointer_grab.implicit, FALSE, display->pointer_grab.implicit,
window); 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)
{
_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);
/* 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)
{
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; display->pointer_grab.window = window;
@ -810,10 +859,12 @@ _gdk_display_unset_has_pointer_grab (GdkDisplay *display,
gboolean do_grab_one_pointer_release_event, gboolean do_grab_one_pointer_release_event,
guint32 time) guint32 time)
{ {
int wx, wy; GdkWindow *pointer_window, *src_toplevel, *dest_toplevel;
GdkWindow *old_grab_window; GdkWindow *old_grab_window;
GdkWindow *old_native_grab_window; GdkWindow *old_native_grab_window;
int x, y;
GdkModifierType state;
GdkWindowObject *w;
old_grab_window = display->pointer_grab.window; old_grab_window = display->pointer_grab.window;
old_native_grab_window = display->pointer_grab.native_window; old_native_grab_window = display->pointer_grab.native_window;
@ -821,34 +872,98 @@ _gdk_display_unset_has_pointer_grab (GdkDisplay *display,
if (do_grab_one_pointer_release_event) if (do_grab_one_pointer_release_event)
display->pointer_grab.grab_one_pointer_release_event = display->pointer_grab.window; 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 /* Set first so crossing events get sent */
delivered to anything but the grab window */
display->pointer_grab.window = NULL; display->pointer_grab.window = NULL;
/* Normal UNGRAB events are sent by listening for enter and leave pointer_window = _gdk_windowing_window_at_pointer (display, &x, &y, &state);
* 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.
*/
if (_gdk_windowing_window_at_pointer (display, &wx, &wy) == old_native_grab_window) 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)
{
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, _gdk_syntesize_crossing_events (display,
old_grab_window, old_grab_window,
display->pointer_info.window_under_pointer, NULL,
GDK_CROSSING_UNGRAB, GDK_CROSSING_UNGRAB,
/* These may be stale... */ x, y, state,
display->pointer_info.toplevel_x, time,
display->pointer_info.toplevel_y, NULL, FALSE);
display->pointer_info.state,
time, TRUE, TRUE);
} }
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) if (implicit)
generate_grab_broken_event (old_grab_window, generate_grab_broken_event (old_grab_window,

View File

@ -58,13 +58,17 @@ typedef struct
GdkWindow *grab_one_pointer_release_event; GdkWindow *grab_one_pointer_release_event;
} GdkPointerGrabInfo; } GdkPointerGrabInfo;
/* Tracks information about which window the pointer is in and /* Tracks information about which window and position the pointer last was in.
* at what position the mouse is. This is useful when we need * This is useful when we need to synthesize events later.
* 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 typedef struct
{ {
GdkWindow *window_under_pointer; 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; gdouble toplevel_x, toplevel_y;
guint32 state; guint32 state;
} GdkPointerWindowInfo; } GdkPointerWindowInfo;

View File

@ -345,7 +345,12 @@ GdkWindow* _gdk_windowing_window_get_pointer (GdkDisplay *display,
GdkModifierType *mask); GdkModifierType *mask);
GdkWindow* _gdk_windowing_window_at_pointer (GdkDisplay *display, GdkWindow* _gdk_windowing_window_at_pointer (GdkDisplay *display,
gint *win_x, 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. */ /* Return the number of bits-per-pixel for images of the specified depth. */
gint _gdk_windowing_get_bits_for_depth (GdkDisplay *display, gint _gdk_windowing_get_bits_for_depth (GdkDisplay *display,
@ -521,8 +526,8 @@ void _gdk_syntesize_crossing_events (GdkDisplay *display,
gint toplevel_y, gint toplevel_y,
GdkModifierType mask, GdkModifierType mask,
guint32 time_, guint32 time_,
gboolean do_first, GdkEvent *event_in_queue,
gboolean do_last); gboolean before_event);
void _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window); void _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window);

View File

@ -1159,6 +1159,7 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
GdkWindowObject *temp_private; GdkWindowObject *temp_private;
GdkWindow *temp_window; GdkWindow *temp_window;
GdkScreen *screen; GdkScreen *screen;
GdkDisplay *display;
GList *children; GList *children;
GList *tmp; GList *tmp;
@ -1169,11 +1170,13 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
if (GDK_WINDOW_DESTROYED (window)) if (GDK_WINDOW_DESTROYED (window))
return; return;
display = gdk_drawable_get_display (GDK_DRAWABLE (window));
screen = gdk_drawable_get_screen (GDK_DRAWABLE (window)); screen = gdk_drawable_get_screen (GDK_DRAWABLE (window));
temp_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window); temp_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
if (temp_window == window) if (temp_window == window)
g_object_set_qdata (G_OBJECT (screen), quark_pointer_window, NULL); g_object_set_qdata (G_OBJECT (screen), quark_pointer_window, NULL);
switch (GDK_WINDOW_TYPE (window)) switch (GDK_WINDOW_TYPE (window))
{ {
case GDK_WINDOW_ROOT: case GDK_WINDOW_ROOT:
@ -1284,6 +1287,12 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
gdk_window_redirect_free (private->redirect); gdk_window_redirect_free (private->redirect);
private->redirect = NULL; 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; break;
} }
@ -6615,13 +6624,9 @@ static gboolean
point_in_window (GdkWindowObject *window, point_in_window (GdkWindowObject *window,
double x, double y) double x, double y)
{ {
int w, h;
gdk_drawable_get_size (GDK_DRAWABLE (window), &w, &h);
return return
x >= 0 && x < w && x >= 0 && x < window->width &&
y >= 0 && y < h; y >= 0 && y < window->height;
} }
static void static void
@ -6856,22 +6861,22 @@ is_motion_type (GdkEventType type)
type == GDK_LEAVE_NOTIFY; type == GDK_LEAVE_NOTIFY;
} }
static GdkWindow * static GdkWindowObject *
find_common_ancestor (GdkWindow *win1, find_common_ancestor (GdkWindowObject *win1,
GdkWindow *win2) GdkWindowObject *win2)
{ {
GdkWindowObject *tmp; GdkWindowObject *tmp;
GList *path1 = NULL, *path2 = NULL; GList *path1 = NULL, *path2 = NULL;
GList *list1, *list2; GList *list1, *list2;
tmp = GDK_WINDOW_OBJECT (win1); tmp = win1;
while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT) while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT)
{ {
path1 = g_list_prepend (path1, tmp); path1 = g_list_prepend (path1, tmp);
tmp = tmp->parent; tmp = tmp->parent;
} }
tmp = GDK_WINDOW_OBJECT (win2); tmp = win2;
while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT) while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT)
{ {
path2 = g_list_prepend (path2, tmp); path2 = g_list_prepend (path2, tmp);
@ -6890,7 +6895,7 @@ find_common_ancestor (GdkWindow *win1,
g_list_free (path1); g_list_free (path1);
g_list_free (path2); g_list_free (path2);
return GDK_WINDOW (tmp); return tmp;
} }
GdkEvent * GdkEvent *
@ -6995,6 +7000,54 @@ _gdk_make_event (GdkWindow *window,
return event; 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. /* The coordinates are in the toplevel window that src/dest are in.
* src and dest are always (if != NULL) in the same toplevel, as * 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 * 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, gint toplevel_y,
GdkModifierType mask, GdkModifierType mask,
guint32 time_, guint32 time_,
gboolean do_first, GdkEvent *event_in_queue,
gboolean do_last) gboolean before_event)
{ {
GdkWindow *c; GdkWindowObject *c;
GdkWindow *win, *last, *next; GdkWindowObject *win, *last, *next;
GdkEvent *event;
GList *path, *list; GList *path, *list;
gboolean non_linear; gboolean non_linear;
GdkWindow *a; GdkWindowObject *a;
GdkWindow *b; GdkWindowObject *b;
GdkWindow *event_win;
GdkWindowObject *toplevel; GdkWindowObject *toplevel;
GdkNotifyType notify_type;
/* TODO: Don't send events to toplevel, as we get those from the windowing system */ /* TODO: Don't send events to toplevel, as we get those from the windowing system */
a = src; a = (GdkWindowObject *)src;
b = dest; b = (GdkWindowObject *)dest;
if (a == b) if (a == b)
return; /* No crossings generated between src and dest */ return; /* No crossings generated between src and dest */
c = find_common_ancestor (a, b); c = find_common_ancestor (a, b);
non_linear = (c != a) && (c != b); non_linear = (c != a) && (c != b);
if (a) /* There might not be a source (i.e. if no previous pointer_in_window) */ 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 */ /* Traverse up from a to (excluding) c sending leave events */
if (non_linear)
event_win = get_target_window_for_pointer_event (display, a, GDK_LEAVE_NOTIFY, mask); notify_type = GDK_NOTIFY_NONLINEAR;
if (do_first && event_win) else if (c == a)
{ notify_type = GDK_NOTIFY_INFERIOR;
event = _gdk_make_event (event_win, GDK_LEAVE_NOTIFY, NULL, FALSE); else
event->crossing.time = time_; notify_type = GDK_NOTIFY_ANCESTOR;
event->crossing.subwindow = NULL; send_crossing_event (display, toplevel,
convert_toplevel_coords_to_window (event_win, a, GDK_LEAVE_NOTIFY,
toplevel_x, toplevel_y, mode,
&event->crossing.x, &event->crossing.y); notify_type,
event->crossing.x_root = toplevel_x + toplevel->x; NULL,
event->crossing.y_root = toplevel_y + toplevel->y; toplevel_x, toplevel_y,
event->crossing.mode = mode; mask, time_,
if (non_linear) event_in_queue, before_event);
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 (c != a) if (c != a)
{ {
if (non_linear)
notify_type = GDK_NOTIFY_NONLINEAR_VIRTUAL;
else
notify_type = GDK_NOTIFY_VIRTUAL;
last = a; last = a;
win = GDK_WINDOW (GDK_WINDOW_OBJECT (a)->parent); win = a->parent;
while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT) while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT)
{ {
event_win = get_target_window_for_pointer_event (display, win, GDK_LEAVE_NOTIFY, mask); send_crossing_event (display, toplevel,
if (event_win) win, GDK_LEAVE_NOTIFY,
{ mode,
event = _gdk_make_event (event_win, GDK_LEAVE_NOTIFY, NULL, FALSE); notify_type,
event->crossing.time = time_; (GdkWindow *)last,
event->crossing.subwindow = g_object_ref (last); toplevel_x, toplevel_y,
convert_toplevel_coords_to_window (event_win, mask, time_,
toplevel_x, toplevel_y, event_in_queue, before_event);
&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;
}
last = win; 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 */ 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 */ /* Traverse down from c to b */
if (c != b) if (c != b)
{ {
path = NULL; path = NULL;
win = GDK_WINDOW (GDK_WINDOW_OBJECT (b)->parent); win = b->parent;
while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT) while (win != c && GDK_WINDOW_TYPE (win) != GDK_WINDOW_ROOT)
{ {
path = g_list_prepend (path, win); 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; list = path;
while (list) while (list)
{ {
win = (GdkWindow *)list->data; win = (GdkWindowObject *)list->data;
list = g_list_next (list); list = g_list_next (list);
if (list) if (list)
next = (GdkWindow *)list->data; next = (GdkWindowObject *)list->data;
else else
next = b; next = b;
event_win = get_target_window_for_pointer_event (display, win, GDK_ENTER_NOTIFY, mask); send_crossing_event (display, toplevel,
if (event_win) win, GDK_ENTER_NOTIFY,
{ mode,
event = _gdk_make_event (event_win, GDK_ENTER_NOTIFY, NULL, FALSE); notify_type,
event->crossing.time = time_; (GdkWindow *)next,
event->crossing.subwindow = g_object_ref (next); toplevel_x, toplevel_y,
convert_toplevel_coords_to_window (event_win, mask, time_,
toplevel_x, toplevel_y, event_in_queue, before_event);
&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;
}
} }
g_list_free (path); 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 * static GdkWindow *
@ -7176,146 +7204,182 @@ get_toplevel (GdkWindow *w)
return GDK_WINDOW (private); 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 void
_gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window) _gdk_syntesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
{ {
GdkDisplay *display; GdkDisplay *display;
GdkWindow *changed_toplevel; GdkWindow *changed_toplevel;
GdkWindow *pointer_toplevel; GdkWindow *pointer_toplevel, *grab_toplevel;
GdkWindow *new_window_under_pointer; GdkWindow *new_window_under_pointer;
changed_toplevel = get_toplevel (changed_window); changed_toplevel = get_toplevel (changed_window);
display = gdk_drawable_get_display (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); new_window_under_pointer =
get_pointer_window (display, changed_toplevel,
if (pointer_toplevel == 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_syntesize_crossing_events (display,
_gdk_window_find_descendant_at (pointer_toplevel, display->pointer_info.window_under_pointer,
display->pointer_info.toplevel_x, new_window_under_pointer,
display->pointer_info.toplevel_y, GDK_CROSSING_NORMAL,
NULL, NULL); display->pointer_info.toplevel_x,
if (new_window_under_pointer != display->pointer_info.toplevel_y,
display->pointer_info.window_under_pointer) display->pointer_info.state,
{ GDK_CURRENT_TIME,
_gdk_syntesize_crossing_events (display, NULL, FALSE);
display->pointer_info.window_under_pointer, set_window_under_pointer (display, new_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);
}
} }
} }
} }
/* 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 static gboolean
proxy_pointer_event (GdkDisplay *display, proxy_pointer_event (GdkDisplay *display,
GdkWindow *pointer_window,
gdouble toplevel_x,
gdouble toplevel_y,
GdkEvent *source_event) GdkEvent *source_event)
{ {
GdkWindow *event_window; GdkWindow *toplevel_window;
GdkWindow *event_win, *cursor_window; GdkWindow *pointer_window;
gboolean crossing_event; GdkWindow *cursor_window;
gboolean sent_motion; gboolean sent_motion;
GdkEvent *event; GdkEvent *event;
guint state; guint state;
gdouble toplevel_x, toplevel_y;
guint32 time_; 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); gdk_event_get_state (source_event, &state);
time_ = gdk_event_get_time (source_event); time_ = gdk_event_get_time (source_event);
crossing_event = pointer_window = get_pointer_window (display, toplevel_window, toplevel_x, toplevel_y);
source_event->type == GDK_ENTER_NOTIFY || if (display->pointer_info.window_under_pointer != pointer_window)
source_event->type == GDK_LEAVE_NOTIFY;
if (crossing_event)
{ {
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) _gdk_syntesize_crossing_events (display,
{ display->pointer_info.window_under_pointer,
if (crossing->type == GDK_LEAVE_NOTIFY && pointer_window,
display->pointer_info.window_under_pointer != NULL) GDK_CROSSING_NORMAL,
{ toplevel_x, toplevel_y,
_gdk_syntesize_crossing_events (display, state, time_,
display->pointer_info.window_under_pointer, source_event, source_event->type == GDK_LEAVE_NOTIFY);
event_window,
GDK_CROSSING_GRAB,
toplevel_x, toplevel_y, state, time_,
TRUE, FALSE);
}
if (crossing->type == GDK_ENTER_NOTIFY && set_window_under_pointer (display, pointer_window);
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);
}
}
} }
else if (source_event->type == GDK_MOTION_NOTIFY)
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)))
{ {
/* send motion events */ GdkWindow *event_win;
event_win = get_target_window_for_pointer_event (display, pointer_window, GDK_MOTION_NOTIFY, state);
event_win = get_event_window (display,
pointer_window,
source_event->type,
state);
if (event_win) if (event_win)
{ {
sent_motion = TRUE; sent_motion = TRUE;
@ -7334,43 +7398,52 @@ proxy_pointer_event (GdkDisplay *display,
} }
} }
_gdk_syntesize_crossing_events (display, /* TODO: set cursor from cursor_window, or grab cursor */
display->pointer_info.window_under_pointer, cursor_window = pointer_window;
pointer_window, if (display->pointer_grab.window &&
GDK_CROSSING_NORMAL, (pointer_window == NULL ||
toplevel_x, toplevel_y, !is_parent_of (display->pointer_grab.window, pointer_window)))
state, time_, cursor_window = display->pointer_grab.window;
TRUE, TRUE); /* 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 static gboolean
proxy_button_event (GdkWindow *pointer_window, proxy_button_event (GdkEvent *source_event)
gdouble toplevel_x,
gdouble toplevel_y,
GdkEvent *source_event)
{ {
GdkWindow *toplevel_window;
GdkWindow *event_win; GdkWindow *event_win;
GdkWindow *pointer_window;
GdkEvent *event; GdkEvent *event;
guint state; guint state;
guint32 time_; guint32 time_;
GdkEventType type; GdkEventType type;
gdouble toplevel_x, toplevel_y;
GdkDisplay *display; GdkDisplay *display;
type = source_event->any.type; 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); gdk_event_get_state (source_event, &state);
time_ = gdk_event_get_time (source_event); time_ = gdk_event_get_time (source_event);
display = gdk_drawable_get_display (source_event->any.window); display = gdk_drawable_get_display (source_event->any.window);
if ((type == GDK_BUTTON_PRESS || type == GDK_SCROLL) && if ((type == GDK_BUTTON_PRESS || type == GDK_SCROLL) &&
pointer_window != NULL &&
display->pointer_grab.window == source_event->any.window && display->pointer_grab.window == source_event->any.window &&
display->pointer_grab.implicit && display->pointer_grab.implicit &&
!display->pointer_grab.converted_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, _gdk_display_set_has_pointer_grab (display,
pointer_window, pointer_window,
display->pointer_grab.native_window, display->pointer_grab.native_window,
@ -7382,57 +7455,51 @@ proxy_button_event (GdkWindow *pointer_window,
display->pointer_grab.converted_implicit = TRUE; display->pointer_grab.converted_implicit = TRUE;
} }
event_win = get_target_window_for_pointer_event (display, pointer_window = get_pointer_window (display, toplevel_window, toplevel_x, toplevel_y);
pointer_window,
type, event_win = get_event_window (display,
state); pointer_window,
type,
state);
if (event_win == NULL) if (event_win == NULL)
return TRUE; 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); 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;
switch (type) if (type == GDK_BUTTON_PRESS)
{ _gdk_event_button_generate (display, event);
case GDK_BUTTON_PRESS: return TRUE;
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) case GDK_SCROLL:
_gdk_event_button_generate (display, event); event->scroll.direction = source_event->scroll.direction;
return TRUE; 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;
case GDK_SCROLL: default:
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);
return FALSE; return FALSE;
} }
return TRUE; /* Always unlink original, we want to obey the emulated event mask */
} }
void void
@ -7441,9 +7508,8 @@ _gdk_windowing_got_event (GdkDisplay *display,
GdkEvent *event) GdkEvent *event)
{ {
GdkWindow *event_window; GdkWindow *event_window;
GdkWindow *pointer_window;
GdkWindowObject *event_private; GdkWindowObject *event_private;
gdouble x, y, child_x, child_y; gdouble x, y;
gboolean unlink_event; gboolean unlink_event;
event_window = event->any.window; event_window = event->any.window;
@ -7466,40 +7532,78 @@ _gdk_windowing_got_event (GdkDisplay *display,
event_private->window_type); event_private->window_type);
/* We should only get these events on toplevel windows */ /* 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, event_type_value->value_name,
window_type_value->value_name); window_type_value->value_name,
GDK_WINDOW_TYPE (event_window));
return; return;
} }
gdk_event_get_coords (event, &x, &y); if ((event->type == GDK_ENTER_NOTIFY ||
pointer_window = _gdk_window_find_descendant_at (event_window, x, y, event->type == GDK_LEAVE_NOTIFY) &&
&child_x, (event->crossing.mode == GDK_CROSSING_GRAB ||
&child_y); event->crossing.mode == GDK_CROSSING_UNGRAB))
unlink_event = FALSE; {
if (is_motion_type (event->type)) /* We synthesize all crossing events due to grabs are synthesized,
unlink_event = proxy_pointer_event (display, * so we ignore the native ones. This is partly to get easier non-X
pointer_window, * portability, and because of problems with race conditions due to
x, y, * the cached state in the client and the real state in the xserver
event); * when grabbing.
else if (is_button_type (event->type)) */
unlink_event = proxy_button_event (pointer_window, x, y,
event); /* 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;
}
/* Store last pointer window and position/state */ /* 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_x = x;
display->pointer_info.toplevel_y = y; display->pointer_info.toplevel_y = y;
gdk_event_get_state (event, &display->pointer_info.state); gdk_event_get_state (event, &display->pointer_info.state);
if (pointer_window != display->pointer_info.window_under_pointer)
{ unlink_event = FALSE;
if (display->pointer_info.window_under_pointer) if (is_motion_type (event->type))
g_object_unref (display->pointer_info.window_under_pointer); unlink_event = proxy_pointer_event (display,
display->pointer_info.window_under_pointer = NULL; event);
if (pointer_window) else if (is_button_type (event->type))
display->pointer_info.window_under_pointer = g_object_ref (pointer_window); unlink_event = proxy_button_event (event);
}
if (unlink_event) if (unlink_event)
{ {

View File

@ -987,7 +987,7 @@ gdk_event_translate (GdkDisplay *display,
display_x11->keyboard_xgrab_window != NULL && display_x11->keyboard_xgrab_window != NULL &&
( (
/* The window is not a descendant of the grabbed window */ /* 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 */ /* Or owner event is false */
!display_x11->keyboard_xgrab_owner_events !display_x11->keyboard_xgrab_owner_events
) )

View File

@ -3185,7 +3185,8 @@ gdk_display_warp_pointer (GdkDisplay *display,
GdkWindow* GdkWindow*
_gdk_windowing_window_at_pointer (GdkDisplay *display, _gdk_windowing_window_at_pointer (GdkDisplay *display,
gint *win_x, gint *win_x,
gint *win_y) gint *win_y,
GdkModifierType *mask)
{ {
GdkWindow *window; GdkWindow *window;
GdkScreen *screen; GdkScreen *screen;
@ -3294,6 +3295,8 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display,
window = gdk_window_lookup_for_display (display, xwindow_last); window = gdk_window_lookup_for_display (display, xwindow_last);
*win_x = window ? winx : -1; *win_x = window ? winx : -1;
*win_y = window ? winy : -1; *win_y = window ? winy : -1;
if (mask)
*mask = xmask;
return window; return window;
} }