[gtk+] GDK W32: Force correct mouse cursor for custom resize/move



commit 84ddc6aab1e0b498666c65a5283e1a0bdfa04599
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Sat Feb 27 00:10:12 2016 +0000

    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

 gdk/win32/gdkdevice-virtual.c |   67 +++++++++----------------------
 gdk/win32/gdkevents-win32.c   |   12 +++++-
 gdk/win32/gdkwindow-win32.c   |   89 +++++++++++++++++++++++++++++++++++++---
 gdk/win32/gdkwindow-win32.h   |   12 +++++-
 4 files changed, 122 insertions(+), 58 deletions(-)
---
diff --git a/gdk/win32/gdkdevice-virtual.c b/gdk/win32/gdkdevice-virtual.c
index a0f485e..64bf29c 100644
--- a/gdk/win32/gdkdevice-virtual.c
+++ b/gdk/win32/gdkdevice-virtual.c
@@ -94,7 +94,6 @@ gdk_device_virtual_set_window_cursor (GdkDevice *device,
 {
   GdkWindow *parent_window;
   GdkWindowImplWin32 *impl;
-  GdkCursor *replacement_cursor;
   GdkCursor *previous_cursor;
 
   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 the pointer is over our window, set new cursor */
-      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;
-                }
-            }
-        }
+      SetCursor (GDK_WIN32_CURSOR (cursor)->hcursor);
     }
-
-  /* Unset the previous cursor: Need to make sure it's no longer in
-   * 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)
+  else if (previous_cursor != NULL &&
+           GetCursor () == GDK_WIN32_CURSOR (previous_cursor)->hcursor)
+    {
+      /* The caller will unref previous_cursor shortly,
+       * but it holds the handle to currently-used cursor,
+       * and we can't call SetCursor(NULL).
+       */
+      g_warning (G_STRLOC ": Refusing to replace cursor %p (handle %p) with NULL. "
+                 "Expect ugly results.",
+                 previous_cursor, GDK_WIN32_CURSOR (previous_cursor)->hcursor);
+    }
+  else
     {
-      /* Look for a suitable cursor to use instead */
-      replacement_cursor = NULL;
-      parent_window = GDK_WINDOW (window)->parent;
-
-      while (replacement_cursor == NULL)
-        {
-          if (parent_window)
-            {
-              impl = GDK_WINDOW_IMPL_WIN32 (parent_window->impl);
-              replacement_cursor = impl->cursor;
-              parent_window = parent_window->parent;
-            }
-          else
-            replacement_cursor = _gdk_win32_display_get_cursor_for_type (gdk_device_get_display (device), 
GDK_LEFT_PTR);
-        }
-
-      SetCursor (GDK_WIN32_CURSOR (replacement_cursor)->hcursor);
+      /* Up the stack all effors were made already to ensure that
+       * the "cursor" argument is non-NULL.
+       * If it is, calling SetCursor(NULL) is absolutely not
+       * the right decision, so we just warn and bail out.
+       */
+      g_warning (G_STRLOC ": Refusing to set NULL cursor");
     }
 }
 
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index 5058545..b326b90 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -2460,6 +2460,8 @@ 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)
            {
+             GdkWindow *native_window = pointer_grab->native_window;
+
              ReleaseCapture ();
 
              new_window = NULL;
@@ -2473,8 +2475,9 @@ gdk_event_translate (MSG  *msg,
                  if (PtInRect (&rect, client_pt))
                    new_window = gdk_win32_handle_table_lookup (hwnd);
                }
+
              synthesize_crossing_events (display,
-                                         pointer_grab->native_window, new_window,
+                                         native_window, new_window,
                                          GDK_CROSSING_UNGRAB,
                                          &msg->pt,
                                          0, /* TODO: Set right mask */
@@ -2488,6 +2491,13 @@ gdk_event_translate (MSG  *msg,
       generate_button_event (GDK_BUTTON_RELEASE, button,
                             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;
       break;
 
diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c
index cc88413..09db5d6 100644
--- a/gdk/win32/gdkwindow-win32.c
+++ b/gdk/win32/gdkwindow-win32.c
@@ -2062,17 +2062,18 @@ gdk_win32_window_set_device_cursor (GdkWindow *window,
    */
   previous_cursor = impl->cursor;
 
-  GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor);
-
   if (cursor)
     impl->cursor = g_object_ref (cursor);
   else
-    /* Use default cursor otherwise. Setting it to NULL will make it use
-     * system-default cursor, which is not controlled by GTK cursor theming.
+    /* Use default cursor otherwise. Don't just set NULL cursor,
+     * 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_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, impl->cursor);
+
   /* Destroy the previous cursor */
   if (previous_cursor != NULL)
     g_object_unref (previous_cursor);
@@ -2823,6 +2824,49 @@ _gdk_window_get_functions (GdkWindow     *window,
   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
 setup_drag_move_resize_context (GdkWindow                   *window,
                                 GdkW32DragMoveResizeContext *context,
@@ -2835,9 +2879,33 @@ setup_drag_move_resize_context (GdkWindow                   *window,
                                 guint32                      timestamp)
 {
   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);
 
+  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->edge = edge;
   context->device = device;
@@ -2848,9 +2916,10 @@ setup_drag_move_resize_context (GdkWindow                   *window,
   context->start_rect = rect;
 
   GDK_NOTE (EVENTS,
-            g_print ("begin drag moveresize: "
+            g_print ("begin drag moveresize: window %p, toplevel %p, "
                      "op %u, edge %d, device %p, "
                      "button %d, coord %d:%d, time %u\n",
+                     window, gdk_window_get_toplevel (window),
                      context->op, context->edge, context->device,
                      context->button, context->start_root_x,
                      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;
 
   context->op = GDK_WIN32_DRAGOP_NONE;
+
+  gdk_device_ungrab (context->device, GDK_CURRENT_TIME);
+
+  g_clear_object (&context->cursor);
+
   GDK_NOTE (EVENTS,
-            g_print ("end drag moveresize: "
+            g_print ("end drag moveresize: window %p, toplevel %p,"
                      "op %u, edge %d, device %p, "
                      "button %d, coord %d:%d, time %u\n",
+                     window, gdk_window_get_toplevel (window),
                      context->op, context->edge, context->device,
                      context->button, context->start_root_x,
                      context->start_root_y, context->timestamp));
diff --git a/gdk/win32/gdkwindow-win32.h b/gdk/win32/gdkwindow-win32.h
index 6389f7b..24e7d18 100644
--- a/gdk/win32/gdkwindow-win32.h
+++ b/gdk/win32/gdkwindow-win32.h
@@ -64,10 +64,15 @@ struct _GdkW32DragMoveResizeContext
   /* The edge that was grabbed for resizing. Not used for moving. */
   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;
 
-  /* Not used */
+  /* The button pressed down to initiate the op.
+   * The op will be canceled only when *this* button
+   * is released.
+   */
   gint               button;
 
   /* Initial cursor position when the operation began.
@@ -89,6 +94,9 @@ struct _GdkW32DragMoveResizeContext
    * the window size and poistion to the native window.
    */
   gboolean           native_move_resize_pending;
+
+  /* The cursor we should use while the operation is running. */
+  GdkCursor         *cursor;
 };
 
 typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext;


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