diff --git a/gdk/win32/gdkdevice-virtual.c b/gdk/win32/gdkdevice-virtual.c index a0f485ed2f..64bf29c5f2 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) { - /* 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"); } } diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 505854564e..b326b901b2 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 cc88413112..09db5d6d2d 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 6389f7b953..24e7d18a49 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;