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

View File

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

View File

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

View File

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