GDK W32: Force correct mouse cursor for custom resize/move
* Explicitly grab the device, setting appropriate cursor on it. * Fix gdk_device_virtual_set_window_cursor() to just set the cursor, without trying to check that mouse is over the given window. Also prevent it from immediately resetting cursor. * Alse take into account things that happen in other parts of GDK - don't look for replacement cursor, GDK already did that, and don't create a default arrow cursor instead of NULL, GDK-W32 already did that up the stack as well. Warn about inappropriate cursor == NULL argument instead. https://bugzilla.gnome.org/show_bug.cgi?id=762711
This commit is contained in:
		@ -94,7 +94,6 @@ gdk_device_virtual_set_window_cursor (GdkDevice *device,
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  GdkWindow *parent_window;
 | 
					  GdkWindow *parent_window;
 | 
				
			||||||
  GdkWindowImplWin32 *impl;
 | 
					  GdkWindowImplWin32 *impl;
 | 
				
			||||||
  GdkCursor *replacement_cursor;
 | 
					 | 
				
			||||||
  GdkCursor *previous_cursor;
 | 
					  GdkCursor *previous_cursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
 | 
					  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
 | 
				
			||||||
@ -103,55 +102,27 @@ gdk_device_virtual_set_window_cursor (GdkDevice *device,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (cursor != NULL && GDK_WIN32_CURSOR (cursor)->hcursor != NULL)
 | 
					  if (cursor != NULL && GDK_WIN32_CURSOR (cursor)->hcursor != NULL)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      /* If the pointer is over our window, set new cursor */
 | 
					      SetCursor (GDK_WIN32_CURSOR (cursor)->hcursor);
 | 
				
			||||||
      GdkWindow *curr_window = gdk_window_get_device_position (window, device, NULL, NULL, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (curr_window == window ||
 | 
					 | 
				
			||||||
          (curr_window && window == gdk_window_get_toplevel (curr_window)))
 | 
					 | 
				
			||||||
        SetCursor (GDK_WIN32_CURSOR (cursor)->hcursor);
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          /* Climb up the tree and find whether our window is the
 | 
					 | 
				
			||||||
           * first ancestor that has cursor defined, and if so, set
 | 
					 | 
				
			||||||
           * new cursor.
 | 
					 | 
				
			||||||
           */
 | 
					 | 
				
			||||||
          while (curr_window && curr_window->impl &&
 | 
					 | 
				
			||||||
                 !GDK_WINDOW_IMPL_WIN32 (curr_window->impl)->cursor)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
              curr_window = curr_window->parent;
 | 
					 | 
				
			||||||
              if (curr_window == GDK_WINDOW (window))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                  SetCursor (GDK_WIN32_CURSOR (cursor)->hcursor);
 | 
					 | 
				
			||||||
                  break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  else if (previous_cursor != NULL &&
 | 
				
			||||||
  /* Unset the previous cursor: Need to make sure it's no longer in
 | 
					           GetCursor () == GDK_WIN32_CURSOR (previous_cursor)->hcursor)
 | 
				
			||||||
   * use before we destroy it, in case we're not over our window but
 | 
					 | 
				
			||||||
   * the cursor is still set to our old one.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  if (previous_cursor != NULL &&
 | 
					 | 
				
			||||||
      GetCursor () == GDK_WIN32_CURSOR (previous_cursor)->hcursor)
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      /* Look for a suitable cursor to use instead */
 | 
					      /* The caller will unref previous_cursor shortly,
 | 
				
			||||||
      replacement_cursor = NULL;
 | 
					       * but it holds the handle to currently-used cursor,
 | 
				
			||||||
      parent_window = GDK_WINDOW (window)->parent;
 | 
					       * and we can't call SetCursor(NULL).
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
      while (replacement_cursor == NULL)
 | 
					      g_warning (G_STRLOC ": Refusing to replace cursor %p (handle %p) with NULL. "
 | 
				
			||||||
        {
 | 
					                 "Expect ugly results.",
 | 
				
			||||||
          if (parent_window)
 | 
					                 previous_cursor, GDK_WIN32_CURSOR (previous_cursor)->hcursor);
 | 
				
			||||||
            {
 | 
					    }
 | 
				
			||||||
              impl = GDK_WINDOW_IMPL_WIN32 (parent_window->impl);
 | 
					  else
 | 
				
			||||||
              replacement_cursor = impl->cursor;
 | 
					    {
 | 
				
			||||||
              parent_window = parent_window->parent;
 | 
					      /* Up the stack all effors were made already to ensure that
 | 
				
			||||||
            }
 | 
					       * the "cursor" argument is non-NULL.
 | 
				
			||||||
          else
 | 
					       * If it is, calling SetCursor(NULL) is absolutely not
 | 
				
			||||||
            replacement_cursor = _gdk_win32_display_get_cursor_for_type (gdk_device_get_display (device), GDK_LEFT_PTR);
 | 
					       * the right decision, so we just warn and bail out.
 | 
				
			||||||
        }
 | 
					       */
 | 
				
			||||||
 | 
					      g_warning (G_STRLOC ": Refusing to set NULL cursor");
 | 
				
			||||||
      SetCursor (GDK_WIN32_CURSOR (replacement_cursor)->hcursor);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2460,6 +2460,8 @@ gdk_event_translate (MSG  *msg,
 | 
				
			|||||||
	  /* We keep the implicit grab until no buttons at all are held down */
 | 
						  /* 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)
 | 
						  if ((state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (button - 1))) == 0)
 | 
				
			||||||
	    {
 | 
						    {
 | 
				
			||||||
 | 
						      GdkWindow *native_window = pointer_grab->native_window;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      ReleaseCapture ();
 | 
						      ReleaseCapture ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      new_window = NULL;
 | 
						      new_window = NULL;
 | 
				
			||||||
@ -2473,8 +2475,9 @@ gdk_event_translate (MSG  *msg,
 | 
				
			|||||||
		  if (PtInRect (&rect, client_pt))
 | 
							  if (PtInRect (&rect, client_pt))
 | 
				
			||||||
		    new_window = gdk_win32_handle_table_lookup (hwnd);
 | 
							    new_window = gdk_win32_handle_table_lookup (hwnd);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      synthesize_crossing_events (display,
 | 
						      synthesize_crossing_events (display,
 | 
				
			||||||
					  pointer_grab->native_window, new_window,
 | 
										  native_window, new_window,
 | 
				
			||||||
					  GDK_CROSSING_UNGRAB,
 | 
										  GDK_CROSSING_UNGRAB,
 | 
				
			||||||
					  &msg->pt,
 | 
										  &msg->pt,
 | 
				
			||||||
					  0, /* TODO: Set right mask */
 | 
										  0, /* TODO: Set right mask */
 | 
				
			||||||
@ -2488,6 +2491,13 @@ gdk_event_translate (MSG  *msg,
 | 
				
			|||||||
      generate_button_event (GDK_BUTTON_RELEASE, button,
 | 
					      generate_button_event (GDK_BUTTON_RELEASE, button,
 | 
				
			||||||
			     window, msg);
 | 
								     window, msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* End a drag op when the same button that started it is released */
 | 
				
			||||||
 | 
					      if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE &&
 | 
				
			||||||
 | 
					          impl->drag_move_resize_context.button == button)
 | 
				
			||||||
 | 
					        gdk_win32_window_end_move_resize_drag (window);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return_val = TRUE;
 | 
					      return_val = TRUE;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2062,17 +2062,18 @@ gdk_win32_window_set_device_cursor (GdkWindow *window,
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  previous_cursor = impl->cursor;
 | 
					  previous_cursor = impl->cursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (cursor)
 | 
					  if (cursor)
 | 
				
			||||||
    impl->cursor = g_object_ref (cursor);
 | 
					    impl->cursor = g_object_ref (cursor);
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    /* Use default cursor otherwise. Setting it to NULL will make it use
 | 
					    /* Use default cursor otherwise. Don't just set NULL cursor,
 | 
				
			||||||
     * system-default cursor, which is not controlled by GTK cursor theming.
 | 
					     * because that will just hide the cursor, which is not
 | 
				
			||||||
 | 
					     * what the caller probably wanted.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    impl->cursor = _gdk_win32_display_get_cursor_for_type (gdk_display_get_default (),
 | 
					    impl->cursor = _gdk_win32_display_get_cursor_for_type (gdk_device_get_display (device),
 | 
				
			||||||
                                                           GDK_LEFT_PTR);
 | 
					                                                           GDK_LEFT_PTR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, impl->cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Destroy the previous cursor */
 | 
					  /* Destroy the previous cursor */
 | 
				
			||||||
  if (previous_cursor != NULL)
 | 
					  if (previous_cursor != NULL)
 | 
				
			||||||
    g_object_unref (previous_cursor);
 | 
					    g_object_unref (previous_cursor);
 | 
				
			||||||
@ -2823,6 +2824,49 @@ _gdk_window_get_functions (GdkWindow     *window,
 | 
				
			|||||||
  return (functions_set != NULL);
 | 
					  return (functions_set != NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const gchar *
 | 
				
			||||||
 | 
					get_cursor_name_from_op (GdkW32WindowDragOp op,
 | 
				
			||||||
 | 
					                         GdkWindowEdge      edge)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  switch (op)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    case GDK_WIN32_DRAGOP_MOVE:
 | 
				
			||||||
 | 
					      return "move";
 | 
				
			||||||
 | 
					    case GDK_WIN32_DRAGOP_RESIZE:
 | 
				
			||||||
 | 
					      switch (edge)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_NORTH_WEST:
 | 
				
			||||||
 | 
					          return "nw-resize";
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_NORTH:
 | 
				
			||||||
 | 
					          return "n-resize";
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_NORTH_EAST:
 | 
				
			||||||
 | 
					          return "ne-resize";
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_WEST:
 | 
				
			||||||
 | 
					          return "w-resize";
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_EAST:
 | 
				
			||||||
 | 
					          return "e-resize";
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_SOUTH_WEST:
 | 
				
			||||||
 | 
					          return "sw-resize";
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_SOUTH:
 | 
				
			||||||
 | 
					          return "s-resize";
 | 
				
			||||||
 | 
					        case GDK_WINDOW_EDGE_SOUTH_EAST:
 | 
				
			||||||
 | 
					          return "e-resize";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      /* default: warn about unhandled enum values,
 | 
				
			||||||
 | 
					       * fallthrough to GDK_WIN32_DRAGOP_NONE case
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					    case GDK_WIN32_DRAGOP_COUNT:
 | 
				
			||||||
 | 
					      g_assert_not_reached ();
 | 
				
			||||||
 | 
					    case GDK_WIN32_DRAGOP_NONE:
 | 
				
			||||||
 | 
					      return "default";
 | 
				
			||||||
 | 
					    /* default: warn about unhandled enum values */
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  g_assert_not_reached ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
setup_drag_move_resize_context (GdkWindow                   *window,
 | 
					setup_drag_move_resize_context (GdkWindow                   *window,
 | 
				
			||||||
                                GdkW32DragMoveResizeContext *context,
 | 
					                                GdkW32DragMoveResizeContext *context,
 | 
				
			||||||
@ -2835,9 +2879,33 @@ setup_drag_move_resize_context (GdkWindow                   *window,
 | 
				
			|||||||
                                guint32                      timestamp)
 | 
					                                guint32                      timestamp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  RECT rect;
 | 
					  RECT rect;
 | 
				
			||||||
 | 
					  const gchar *cursor_name;
 | 
				
			||||||
 | 
					  gint x, y;
 | 
				
			||||||
 | 
					  GdkWindow *pointer_window;
 | 
				
			||||||
 | 
					  GdkDisplay *display = gdk_device_get_display (device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _gdk_win32_get_window_rect (window, &rect);
 | 
					  _gdk_win32_get_window_rect (window, &rect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cursor_name = get_cursor_name_from_op (op, edge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  context->cursor = _gdk_win32_display_get_cursor_for_name (display, cursor_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gdk_window_get_root_origin (window, &x, &y);
 | 
				
			||||||
 | 
					  x -= root_x;
 | 
				
			||||||
 | 
					  y -= root_y;
 | 
				
			||||||
 | 
					  pointer_window = gdk_device_get_window_at_position (device, &x, &y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Note: This triggers a WM_CAPTURECHANGED, which will trigger
 | 
				
			||||||
 | 
					   * gdk_win32_window_end_move_resize_drag(), which will end
 | 
				
			||||||
 | 
					   * our op before it even begins, but only if context->op is not NONE.
 | 
				
			||||||
 | 
					   * This is why we first do the grab, *then* set the op.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  gdk_device_grab (device, pointer_window,
 | 
				
			||||||
 | 
					                   GDK_OWNERSHIP_NONE, FALSE,
 | 
				
			||||||
 | 
					                   GDK_ALL_EVENTS_MASK,
 | 
				
			||||||
 | 
					                   context->cursor,
 | 
				
			||||||
 | 
					                   timestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  context->op = op;
 | 
					  context->op = op;
 | 
				
			||||||
  context->edge = edge;
 | 
					  context->edge = edge;
 | 
				
			||||||
  context->device = device;
 | 
					  context->device = device;
 | 
				
			||||||
@ -2848,9 +2916,10 @@ setup_drag_move_resize_context (GdkWindow                   *window,
 | 
				
			|||||||
  context->start_rect = rect;
 | 
					  context->start_rect = rect;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GDK_NOTE (EVENTS,
 | 
					  GDK_NOTE (EVENTS,
 | 
				
			||||||
            g_print ("begin drag moveresize: "
 | 
					            g_print ("begin drag moveresize: window %p, toplevel %p, "
 | 
				
			||||||
                     "op %u, edge %d, device %p, "
 | 
					                     "op %u, edge %d, device %p, "
 | 
				
			||||||
                     "button %d, coord %d:%d, time %u\n",
 | 
					                     "button %d, coord %d:%d, time %u\n",
 | 
				
			||||||
 | 
					                     window, gdk_window_get_toplevel (window),
 | 
				
			||||||
                     context->op, context->edge, context->device,
 | 
					                     context->op, context->edge, context->device,
 | 
				
			||||||
                     context->button, context->start_root_x,
 | 
					                     context->button, context->start_root_x,
 | 
				
			||||||
                     context->start_root_y, context->timestamp));
 | 
					                     context->start_root_y, context->timestamp));
 | 
				
			||||||
@ -2863,10 +2932,16 @@ gdk_win32_window_end_move_resize_drag (GdkWindow *window)
 | 
				
			|||||||
  GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context;
 | 
					  GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  context->op = GDK_WIN32_DRAGOP_NONE;
 | 
					  context->op = GDK_WIN32_DRAGOP_NONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gdk_device_ungrab (context->device, GDK_CURRENT_TIME);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  g_clear_object (&context->cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GDK_NOTE (EVENTS,
 | 
					  GDK_NOTE (EVENTS,
 | 
				
			||||||
            g_print ("end drag moveresize: "
 | 
					            g_print ("end drag moveresize: window %p, toplevel %p,"
 | 
				
			||||||
                     "op %u, edge %d, device %p, "
 | 
					                     "op %u, edge %d, device %p, "
 | 
				
			||||||
                     "button %d, coord %d:%d, time %u\n",
 | 
					                     "button %d, coord %d:%d, time %u\n",
 | 
				
			||||||
 | 
					                     window, gdk_window_get_toplevel (window),
 | 
				
			||||||
                     context->op, context->edge, context->device,
 | 
					                     context->op, context->edge, context->device,
 | 
				
			||||||
                     context->button, context->start_root_x,
 | 
					                     context->button, context->start_root_x,
 | 
				
			||||||
                     context->start_root_y, context->timestamp));
 | 
					                     context->start_root_y, context->timestamp));
 | 
				
			||||||
 | 
				
			|||||||
@ -64,10 +64,15 @@ struct _GdkW32DragMoveResizeContext
 | 
				
			|||||||
  /* The edge that was grabbed for resizing. Not used for moving. */
 | 
					  /* The edge that was grabbed for resizing. Not used for moving. */
 | 
				
			||||||
  GdkWindowEdge      edge;
 | 
					  GdkWindowEdge      edge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Not used */
 | 
					  /* The device used to initiate the op.
 | 
				
			||||||
 | 
					   * We grab it at the beginning and ungrab it at the end.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  GdkDevice         *device;
 | 
					  GdkDevice         *device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Not used */
 | 
					  /* The button pressed down to initiate the op.
 | 
				
			||||||
 | 
					   * The op will be canceled only when *this* button
 | 
				
			||||||
 | 
					   * is released.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  gint               button;
 | 
					  gint               button;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Initial cursor position when the operation began.
 | 
					  /* Initial cursor position when the operation began.
 | 
				
			||||||
@ -89,6 +94,9 @@ struct _GdkW32DragMoveResizeContext
 | 
				
			|||||||
   * the window size and poistion to the native window.
 | 
					   * the window size and poistion to the native window.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  gboolean           native_move_resize_pending;
 | 
					  gboolean           native_move_resize_pending;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* The cursor we should use while the operation is running. */
 | 
				
			||||||
 | 
					  GdkCursor         *cursor;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext;
 | 
					typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext;
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user