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:
Руслан Ижбулатов
2016-02-27 00:10:12 +00:00
parent 8ebc03a1d1
commit 84ddc6aab1
4 changed files with 122 additions and 58 deletions

View File

@ -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");
}
}

View File

@ -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;

View File

@ -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));

View File

@ -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;