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; | ||||
|   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) | ||||
|     { | ||||
|       /* 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); | ||||
|       /* 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 | ||||
|     { | ||||
|       /* 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"); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
|  | ||||
| @ -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)); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Руслан Ижбулатов
					Руслан Ижбулатов