win32: Better crossing events and grab destination reporting

We new report to the right window during !owner_event grabs, and
we send proper enter and leave events.
This commit is contained in:
Alexander Larsson 2011-10-19 21:44:38 +02:00
parent 73c527aef0
commit d66ad8c39d

View File

@ -119,7 +119,7 @@ static GSourceFuncs event_funcs = {
GPollFD event_poll_fd;
static GdkWindow *current_toplevel = NULL;
static GdkWindow *mouse_window = NULL;
static gint current_x, current_y;
static gint current_root_x, current_root_y;
static UINT client_message;
@ -137,13 +137,6 @@ static UINT sync_timer = 0;
static int debug_indent = 0;
static void
synthesize_enter_or_leave_event (GdkWindow *window,
MSG *msg,
GdkEventType type,
GdkCrossingMode mode,
GdkNotifyType detail);
static void
assign_object (gpointer lhsp,
gpointer rhs)
@ -506,48 +499,47 @@ static GdkWindow *
find_window_for_mouse_event (GdkWindow* reported_window,
MSG* msg)
{
HWND hwnd;
POINTS points;
POINT pt;
GdkWindow* other_window = NULL;
GdkDeviceManagerWin32 *device_manager;
GdkWindow *event_window;
HWND hwnd;
RECT rect;
GdkDeviceGrabInfo *grab;
device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (_gdk_display));
if (!_gdk_display_get_last_device_grab (_gdk_display, device_manager->core_pointer))
grab = _gdk_display_get_last_device_grab (_gdk_display, device_manager->core_pointer);
if (grab == NULL)
return reported_window;
points = MAKEPOINTS (msg->lParam);
pt.x = points.x;
pt.y = points.y;
ClientToScreen (msg->hwnd, &pt);
pt = msg->pt;
hwnd = WindowFromPoint (pt);
if (hwnd != NULL)
if (!grab->owner_events)
event_window = grab->native_window;
else
{
RECT rect;
event_window = NULL;
hwnd = WindowFromPoint (pt);
if (hwnd != NULL)
{
POINT client_pt = pt;
GetClientRect (hwnd, &rect);
ScreenToClient (hwnd, &pt);
if (!PtInRect (&rect, pt))
return _gdk_root;
other_window = gdk_win32_handle_table_lookup (hwnd);
ScreenToClient (hwnd, &client_pt);
GetClientRect (hwnd, &rect);
if (PtInRect (&rect, client_pt))
event_window = gdk_win32_handle_table_lookup (hwnd);
}
if (event_window == NULL)
event_window = grab->native_window;
}
if (other_window == NULL)
return _gdk_root;
/* need to also adjust the coordinates to the new window */
pt.x = points.x;
pt.y = points.y;
ClientToScreen (msg->hwnd, &pt);
ScreenToClient (GDK_WINDOW_HWND (other_window), &pt);
ScreenToClient (GDK_WINDOW_HWND (event_window), &pt);
/* ATTENTION: need to update client coords */
msg->lParam = MAKELPARAM (pt.x, pt.y);
return other_window;
return event_window;
}
static void
@ -1137,39 +1129,230 @@ do_show_window (GdkWindow *window, gboolean hide_window)
}
static void
synthesize_enter_or_leave_event (GdkWindow *window,
MSG *msg,
GdkEventType type,
GdkCrossingMode mode,
GdkNotifyType detail)
send_crossing_event (GdkDisplay *display,
GdkWindow *window,
GdkEventType type,
GdkCrossingMode mode,
GdkNotifyType notify_type,
GdkWindow *subwindow,
POINT *screen_pt,
GdkModifierType mask,
guint32 time_)
{
GdkEvent *event;
GdkDeviceGrabInfo *grab;
GdkDeviceManagerWin32 *device_manager;
POINT pt;
pt = msg->pt;
device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (display));
grab = _gdk_display_has_device_grab (display, device_manager->core_pointer, 0);
if (grab != NULL &&
!grab->owner_events &&
mode != GDK_CROSSING_UNGRAB)
{
/* !owner_event => only report events wrt grab window, ignore rest */
if ((GdkWindow *)window != grab->native_window)
return;
}
pt = *screen_pt;
ScreenToClient (GDK_WINDOW_HWND (window), &pt);
event = gdk_event_new (type);
event->crossing.window = window;
event->crossing.subwindow = NULL;
event->crossing.time = _gdk_win32_get_next_tick (msg->time);
event->crossing.subwindow = subwindow;
event->crossing.time = _gdk_win32_get_next_tick (time_);
event->crossing.x = pt.x;
event->crossing.y = pt.y;
event->crossing.x_root = msg->pt.x + _gdk_offset_x;
event->crossing.y_root = msg->pt.y + _gdk_offset_y;
event->crossing.x_root = screen_pt->x + _gdk_offset_x;
event->crossing.y_root = screen_pt->y + _gdk_offset_y;
event->crossing.mode = mode;
event->crossing.detail = detail;
event->crossing.focus = TRUE; /* FIXME: Set correctly */
event->crossing.state = 0; /* FIXME: Set correctly */
event->crossing.detail = notify_type;
event->crossing.mode = mode;
event->crossing.detail = notify_type;
event->crossing.focus = FALSE;
event->crossing.state = mask;
gdk_event_set_device (event, _gdk_display->core_pointer);
_gdk_win32_append_event (event);
if (type == GDK_ENTER_NOTIFY &&
window->extension_events != 0)
_gdk_device_wintab_update_window_coords (window);
}
static GdkWindow *
get_native_parent (GdkWindow *window)
{
if (window->parent != NULL)
return window->parent->impl_window;
return NULL;
}
static GdkWindow *
find_common_ancestor (GdkWindow *win1,
GdkWindow *win2)
{
GdkWindow *tmp;
GList *path1 = NULL, *path2 = NULL;
GList *list1, *list2;
tmp = win1;
while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT)
{
path1 = g_list_prepend (path1, tmp);
tmp = get_native_parent (tmp);
}
tmp = win2;
while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT)
{
path2 = g_list_prepend (path2, tmp);
tmp = get_native_parent (tmp);
}
list1 = path1;
list2 = path2;
tmp = NULL;
while (list1 && list2 && (list1->data == list2->data))
{
tmp = (GdkWindow *)list1->data;
list1 = g_list_next (list1);
list2 = g_list_next (list2);
}
g_list_free (path1);
g_list_free (path2);
return tmp;
}
void
synthesize_crossing_events (GdkDisplay *display,
GdkWindow *src,
GdkWindow *dest,
GdkCrossingMode mode,
POINT *screen_pt,
GdkModifierType mask,
guint32 time_,
gboolean non_linear)
{
GdkWindow *c;
GdkWindow *win, *last, *next;
GList *path, *list;
GdkWindow *a;
GdkWindow *b;
GdkNotifyType notify_type;
a = src;
b = 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) */
{
/* Traverse up from a to (excluding) c sending leave events */
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,
a, GDK_LEAVE_NOTIFY,
mode,
notify_type,
NULL,
screen_pt,
mask, time_);
if (c != a)
{
if (non_linear)
notify_type = GDK_NOTIFY_NONLINEAR_VIRTUAL;
else
notify_type = GDK_NOTIFY_VIRTUAL;
last = a;
win = get_native_parent (a);
while (win != c && win->window_type != GDK_WINDOW_ROOT)
{
send_crossing_event (display,
win, GDK_LEAVE_NOTIFY,
mode,
notify_type,
(GdkWindow *)last,
screen_pt,
mask, time_);
last = win;
win = get_native_parent (win);
}
}
}
if (b) /* Might not be a dest, e.g. if we're moving out of the window */
{
/* Traverse down from c to b */
if (c != b)
{
path = NULL;
win = get_native_parent (b);
while (win != c && win->window_type != GDK_WINDOW_ROOT)
{
path = g_list_prepend (path, win);
win = get_native_parent (win);
}
if (non_linear)
notify_type = GDK_NOTIFY_NONLINEAR_VIRTUAL;
else
notify_type = GDK_NOTIFY_VIRTUAL;
list = path;
while (list)
{
win = (GdkWindow *)list->data;
list = g_list_next (list);
if (list)
next = (GdkWindow *)list->data;
else
next = b;
send_crossing_event (display,
win, GDK_ENTER_NOTIFY,
mode,
notify_type,
next,
screen_pt,
mask, time_);
}
g_list_free (path);
}
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,
b, GDK_ENTER_NOTIFY,
mode,
notify_type,
NULL,
screen_pt,
mask, time_);
}
}
/* The check_extended flag controls whether to check if the windows want
* events from extended input devices and if the message should be skipped
* because an extended input device is active
@ -1716,7 +1899,7 @@ gdk_event_translate (MSG *msg,
GdkWindow *window = NULL;
GdkWindowImplWin32 *impl;
GdkWindow *orig_window, *new_window, *toplevel;
GdkWindow *orig_window, *new_window;
GdkDeviceManager *device_manager;
@ -2157,7 +2340,29 @@ gdk_event_translate (MSG *msg,
/* We keep the implicit grab until no buttons at all are held down */
if ((state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (button - 1))) == 0)
ReleaseCapture ();
{
ReleaseCapture ();
new_window = NULL;
hwnd = WindowFromPoint (msg->pt);
if (hwnd != NULL)
{
POINT client_pt = msg->pt;
ScreenToClient (hwnd, &client_pt);
GetClientRect (hwnd, &rect);
if (PtInRect (&rect, client_pt))
new_window = gdk_win32_handle_table_lookup (hwnd);
}
synthesize_crossing_events (_gdk_display,
pointer_grab->native_window, new_window,
GDK_CROSSING_UNGRAB,
&msg->pt,
0, /* TODO: Set right mask */
msg->time,
FALSE);
assign_object (&mouse_window, new_window);
}
}
generate_button_event (GDK_BUTTON_RELEASE, button,
@ -2172,22 +2377,50 @@ gdk_event_translate (MSG *msg,
(gpointer) msg->wParam,
GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
assign_object (&window, find_window_for_mouse_event (window, msg));
toplevel = gdk_window_get_toplevel (window);
if (current_toplevel != toplevel)
new_window = window;
if (pointer_grab != NULL)
{
GDK_NOTE (EVENTS, g_print (" toplevel %p -> %p",
current_toplevel ? GDK_WINDOW_HWND (current_toplevel) : NULL,
toplevel ? GDK_WINDOW_HWND (toplevel) : NULL));
if (current_toplevel)
synthesize_enter_or_leave_event (current_toplevel, msg,
GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
synthesize_enter_or_leave_event (toplevel, msg,
GDK_ENTER_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
assign_object (&current_toplevel, toplevel);
track_mouse_event (TME_LEAVE, GDK_WINDOW_HWND (toplevel));
POINT pt;
pt = msg->pt;
new_window = NULL;
hwnd = WindowFromPoint (pt);
if (hwnd != NULL)
{
POINT client_pt = pt;
ScreenToClient (hwnd, &client_pt);
GetClientRect (hwnd, &rect);
if (PtInRect (&rect, client_pt))
new_window = gdk_win32_handle_table_lookup (hwnd);
}
if (!pointer_grab->owner_events &&
new_window != NULL &&
new_window != pointer_grab->native_window)
new_window = NULL;
}
if (mouse_window != new_window)
{
GDK_NOTE (EVENTS, g_print (" mouse_sinwod %p -> %p",
mouse_window ? GDK_WINDOW_HWND (mouse_window) : NULL,
new_window ? GDK_WINDOW_HWND (new_window) : NULL));
synthesize_crossing_events (_gdk_display,
mouse_window, new_window,
GDK_CROSSING_NORMAL,
&msg->pt,
0, /* TODO: Set right mask */
msg->time,
FALSE);
assign_object (&mouse_window, new_window);
if (new_window != NULL)
track_mouse_event (TME_LEAVE, GDK_WINDOW_HWND (new_window));
}
assign_object (&window, find_window_for_mouse_event (window, msg));
/* If we haven't moved, don't create any GDK event. Windows
* sends WM_MOUSEMOVE messages after a new window is shows under
* the mouse, even if the mouse hasn't moved. This disturbs gtk.
@ -2226,22 +2459,27 @@ gdk_event_translate (MSG *msg,
GDK_NOTE (EVENTS, g_print (" %d (%ld,%ld)",
HIWORD (msg->wParam), msg->pt.x, msg->pt.y));
if (!gdk_win32_handle_table_lookup (WindowFromPoint (msg->pt)))
new_window = NULL;
hwnd = WindowFromPoint (msg->pt);
if (hwnd != NULL)
{
/* we are only interested if we don't know the new window */
if (current_toplevel)
synthesize_enter_or_leave_event (current_toplevel, msg,
GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
assign_object (&current_toplevel, NULL);
}
else if (window != gdk_window_get_toplevel (window)) /* xxx: only for native child windows? */
{
/* XXX: this used to be ignored pre-csw, but I think we need at least some
* of the leave events */
synthesize_enter_or_leave_event (window, msg,
GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
POINT client_pt = msg->pt;
ScreenToClient (hwnd, &client_pt);
GetClientRect (hwnd, &rect);
if (PtInRect (&rect, client_pt))
new_window = gdk_win32_handle_table_lookup (hwnd);
}
synthesize_crossing_events (_gdk_display,
mouse_window, new_window,
GDK_CROSSING_NORMAL,
&msg->pt,
0, /* TODO: Set right mask */
msg->time,
FALSE);
assign_object (&mouse_window, new_window);
return_val = TRUE;
break;