[gtk+/gtk-2-24-win32: 5/5] Better crossing events and grab destination reporting



commit 039f5fdfb55cf3bd5093ba2c8cf565810bd92161
Author: Alexander Larsson <alexl redhat com>
Date:   Wed Oct 19 21:44:38 2011 +0200

    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.

 gdk/win32/gdkevents-win32.c |  399 ++++++++++++++++++++++++++++++++++---------
 1 files changed, 314 insertions(+), 85 deletions(-)
---
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index b3b095a..d2b2fdd 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -112,7 +112,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;
@@ -131,13 +131,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)
 {
@@ -531,45 +524,44 @@ static GdkWindow *
 find_window_for_mouse_event (GdkWindow* reported_window,
 			     MSG*       msg)
 {
-  HWND hwnd;
-  POINTS points;
   POINT pt;
-  GdkWindow* other_window = NULL;
+  GdkWindow *event_window;
+  HWND hwnd;
+  RECT rect;
+  GdkPointerGrabInfo *grab;
 
-  if (!_gdk_display_get_last_pointer_grab (_gdk_display))
+  grab = _gdk_display_get_last_pointer_grab (_gdk_display);
+  if (grab == NULL)
     return reported_window;
 
-  points = MAKEPOINTS (msg->lParam);
-  pt.x = points.x;
-  pt.y = points.y;
-  ClientToScreen (msg->hwnd, &pt);
-
-  hwnd = WindowFromPoint (pt);
+  pt = msg->pt;
 
-  if (hwnd != NULL)
+  if (!grab->owner_events)
+    event_window = grab->native_window;
+  else
     {
-      RECT rect;
-
-      GetClientRect (hwnd, &rect);
-      ScreenToClient (hwnd, &pt);
-      if (!PtInRect (&rect, pt))
-	return _gdk_root;
+      event_window = NULL;
+      hwnd = WindowFromPoint (pt);
+      if (hwnd != NULL)
+	{
+	  POINT client_pt = pt;
 
-      other_window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwnd);
+	  ScreenToClient (hwnd, &client_pt);
+	  GetClientRect (hwnd, &rect);
+	  if (PtInRect (&rect, client_pt))
+	    event_window = gdk_win32_handle_table_lookup ((GdkNativeWindow) 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;
 }
 
 GdkGrabStatus
@@ -1205,36 +1197,225 @@ 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,
+		     GdkWindowObject            *window,
+		     GdkEventType                type,
+		     GdkCrossingMode             mode,
+		     GdkNotifyType               notify_type,
+		     GdkWindow                  *subwindow,
+		     POINT                      *screen_pt,
+		     GdkModifierType             mask,
+		     guint32                     time_)
 {
   GdkEvent *event;
+  GdkPointerGrabInfo *grab;
   POINT pt;
 
-  pt = msg->pt;
+  grab = _gdk_display_has_pointer_grab (display, 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.window = (GdkWindow *)window;
+  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 = notify_type;
   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.focus = FALSE;
+  event->crossing.state = mask;
 
   _gdk_win32_append_event (event);
-  
+
   if (type == GDK_ENTER_NOTIFY &&
       ((GdkWindowObject *) window)->extension_events != 0)
-    _gdk_input_enter_event (window);
+    _gdk_input_enter_event ((GdkWindow *)window);
+}
+
+static GdkWindowObject *
+get_native_parent (GdkWindowObject *window)
+{
+  if (window->parent != NULL)
+    return window->parent->impl_window;
+  return NULL;
+}
+
+
+static GdkWindowObject *
+find_common_ancestor (GdkWindowObject *win1,
+		      GdkWindowObject *win2)
+{
+  GdkWindowObject *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 = (GdkWindowObject *)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)
+{
+  GdkWindowObject *c;
+  GdkWindowObject *win, *last, *next;
+  GList *path, *list;
+  GdkWindowObject *a;
+  GdkWindowObject *b;
+  GdkNotifyType notify_type;
+
+  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) */
+    {
+      /* 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 = (GdkWindowObject *)list->data;
+	      list = g_list_next (list);
+	      if (list)
+		next = (GdkWindowObject *)list->data;
+	      else
+		next = b;
+
+	      send_crossing_event (display,
+				   win, GDK_ENTER_NOTIFY,
+				   mode,
+				   notify_type,
+				   (GdkWindow *)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_);
+    }
 }
 			 
 static void
@@ -2319,7 +2500,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 ((GdkNativeWindow) hwnd);
+		}
+	      synthesize_crossing_events (_gdk_display,
+					  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,
@@ -2334,22 +2537,51 @@ 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;
+
+      grab = _gdk_display_get_last_pointer_grab (_gdk_display);
+      if (grab != NULL)
+	{
+	  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 ((GdkNativeWindow) hwnd);
+	    }
+
+	  if (!grab->owner_events &&
+	      new_window != NULL &&
+	      new_window != grab->native_window)
+	    new_window = NULL;
+	}
+
+      if (mouse_window != new_window)
 	{
-	  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));
+	  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.
@@ -2388,22 +2620,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 ((GdkNativeWindow) 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 ((GdkNativeWindow) 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;
 
@@ -2593,14 +2830,6 @@ gdk_event_translate (MSG  *msg,
 	{
 	  grab_window = grab->window;
 	}
-      else if (window != gdk_window_get_toplevel (window)) /* xxx: only for native child windows? */
-	{
-	  /* when entering a new window we are Windows is always asking for a new cursor
-	   * so this might be a good spot to emmulate  enter-notify events */
-	  synthesize_enter_or_leave_event (window, msg,
-					   GDK_ENTER_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
-	  track_mouse_event (TME_LEAVE, GDK_WINDOW_HWND (window));
-	}
 
       if (grab_window == NULL && LOWORD (msg->lParam) != HTCLIENT)
 	break;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]