Compare commits

...

4 Commits

Author SHA1 Message Date
Carlos Garnacho
fd3e02c7c2 wayland: Apply GdkDragContext::status action in a delayed manner
In gtkdnd.c, the drag dest event handler will run through all, which may
result in multiple gdk_drag_status() calls along the way if there's
multiple widgets in the hierarchy doing DnD.

This may make us a lot more verbose than desired, so let the wayland
::status handler only cache the result, and commit it after the DnD
event handler traversed the whole tree.

This could be seen on eog, both the window and inner widgets handle DnD,
which result in the selected action ping-ponging between "none" and "copy".
2015-06-29 18:43:51 +02:00
Carlos Garnacho
86c18a807a wayland: Implement DnD actions as per wl_data_device v3
Instead of faking drag status all around, communicate these to the
compositor (and other peer), and trigger the right events on both sides:

  - Changes in the selected action trigger GDK_DRAG_STATUS events on the
    drag source, which trigger grab (and grab cursor) updates.
  - In the case of move actions, the "DELETE" target request is completely
    emulated on the source side as well, that target will be filtered out
    from the offered mimetypes list as well.
2015-06-29 18:43:51 +02:00
Carlos Garnacho
0aeebf691f wayland: Offer internal getter for the device drop context
This will be needed in other places when implementing DnD actions.
2015-06-29 18:43:51 +02:00
Carlos Garnacho
69f3b7dbc5 gtkdnd: Disable cursor + drag-icon merging on Wayland
There, we always want to use the surface given on wl_data_device_start_drag
so the compositor can freely run its "drag cancelled" animations. Besides,
the only benefit for this merge on X11 was sync-draw, which the compositor
should guarantee for us.
2015-06-29 18:43:51 +02:00
7 changed files with 240 additions and 31 deletions

View File

@@ -2247,3 +2247,14 @@ gdk_wayland_device_unset_grab (GdkDevice *gdk_device)
_gdk_wayland_display_deliver_event (gdk_device_get_display (gdk_device), event);
}
GdkDragContext *
gdk_wayland_device_get_drop_context (GdkDevice *gdk_device)
{
GdkWaylandDeviceData *device;
g_return_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device));
device = GDK_WAYLAND_DEVICE (gdk_device)->device;
return device->drop_context;
}

View File

@@ -350,7 +350,7 @@ gdk_registry_handle_global (void *data,
else if (strcmp (interface, "wl_data_device_manager") == 0)
{
display_wayland->data_device_manager =
wl_registry_bind (display_wayland->wl_registry, id, &wl_data_device_manager_interface, 1);
wl_registry_bind (display_wayland->wl_registry, id, &wl_data_device_manager_interface, 3);
}
else if (strcmp (interface, "wl_subcompositor") == 0)
{

View File

@@ -43,6 +43,7 @@ struct _GdkWaylandDragContext
GdkWindow *dnd_window;
struct wl_surface *dnd_surface;
struct wl_data_source *data_source;
GdkDragAction selected_action;
uint32_t serial;
gdouble x;
gdouble y;
@@ -143,11 +144,30 @@ gdk_wayland_drag_context_find_window (GdkDragContext *context,
return NULL;
}
void
gdk_wayland_drag_context_set_action (GdkDragContext *context,
GdkDragAction action)
static inline uint32_t
gdk_to_wl_actions (GdkDragAction action)
{
context->suggested_action = context->action = action;
uint32_t dnd_actions = 0;
if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE))
dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
if (action & GDK_ACTION_MOVE)
dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
if (action & GDK_ACTION_ASK)
dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
return dnd_actions;
}
void
gdk_wayland_drag_context_set_actions (GdkDragContext *context,
GdkDragAction actions)
{
GdkWaylandDragContext *wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
context->actions = actions;
wl_data_source_set_actions (wayland_context->data_source,
gdk_to_wl_actions (actions));
}
static gboolean
@@ -167,7 +187,7 @@ gdk_wayland_drag_context_drag_motion (GdkDragContext *context,
_gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS, time);
}
gdk_wayland_drag_context_set_action (context, suggested_action);
gdk_wayland_drag_context_set_actions (context, possible_actions);
return context->dest_window != NULL;
}
@@ -196,6 +216,9 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context,
context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
if (!context->dest_window)
return;
display = gdk_device_get_display (gdk_drag_context_get_device (context));
wl_offer = gdk_wayland_selection_get_offer (display,
gdk_drag_get_selection (context));
@@ -231,7 +254,11 @@ gdk_wayland_drag_context_drag_status (GdkDragContext *context,
GdkDragAction action,
guint32 time_)
{
gdk_wayland_drop_context_set_status (context, action != 0);
GdkWaylandDragContext *wayland_context;
wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
wayland_context->selected_action = action;
}
static void
@@ -485,3 +512,19 @@ gdk_wayland_drag_context_get_dnd_window (GdkDragContext *context)
wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
return wayland_context->dnd_window;
}
void
gdk_wayland_drag_context_commit_status (GdkDragContext *context)
{
GdkWaylandDragContext *wayland_context;
GdkDisplay *display;
uint32_t dnd_actions;
wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
display = gdk_device_get_display (gdk_drag_context_get_device (context));
dnd_actions = gdk_to_wl_actions (wayland_context->selected_action);
gdk_wayland_selection_set_current_offer_actions (display, dnd_actions);
gdk_wayland_drop_context_set_status (context, wayland_context->selected_action != 0);
}

View File

@@ -118,8 +118,8 @@ void _gdk_wayland_drag_context_set_coords (GdkDragContext *context,
gdouble x,
gdouble y);
void gdk_wayland_drag_context_set_action (GdkDragContext *context,
GdkDragAction action);
void gdk_wayland_drag_context_set_actions (GdkDragContext *context,
GdkDragAction actions);
GdkDragContext * gdk_wayland_drag_context_lookup_by_data_source (struct wl_data_source *source);
GdkDragContext * gdk_wayland_drag_context_lookup_by_source_window (GdkWindow *window);
@@ -184,6 +184,7 @@ uint32_t _gdk_wayland_device_get_last_implicit_grab_serial (GdkWaylandDevice *d
struct wl_data_device * gdk_wayland_device_get_data_device (GdkDevice *gdk_device);
void gdk_wayland_device_set_selection (GdkDevice *gdk_device,
struct wl_data_source *source);
GdkDragContext * gdk_wayland_device_get_drop_context (GdkDevice *gdk_device);
void gdk_wayland_device_unset_grab (GdkDevice *device);
@@ -255,6 +256,8 @@ void gdk_wayland_selection_store (GdkWindow *window,
struct wl_data_source * gdk_wayland_selection_get_data_source (GdkWindow *owner,
GdkAtom selection);
void gdk_wayland_selection_unset_data_source (GdkDisplay *display, GdkAtom selection);
gboolean gdk_wayland_selection_set_current_offer_actions (GdkDisplay *display,
uint32_t actions);
EGLSurface gdk_wayland_window_get_egl_surface (GdkWindow *window,
EGLConfig config);

View File

@@ -350,8 +350,69 @@ data_offer_offer (void *data,
info->targets = g_list_prepend (info->targets, atom);
}
static inline GdkDragAction
_wl_to_gdk_actions (uint32_t dnd_actions)
{
GdkDragAction actions = 0;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
actions |= GDK_ACTION_COPY;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
actions |= GDK_ACTION_MOVE;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
actions |= GDK_ACTION_ASK;
return actions;
}
static void
data_offer_source_actions (void *data,
struct wl_data_offer *wl_data_offer,
uint32_t source_actions)
{
GdkDeviceManager *device_manager;
GdkDragContext *drop_context;
GdkDisplay *display;
GdkDevice *device;
display = gdk_display_get_default ();
device_manager = gdk_display_get_device_manager (display);
device = gdk_device_manager_get_client_pointer (device_manager);
drop_context = gdk_wayland_device_get_drop_context (device);
drop_context->actions = _wl_to_gdk_actions (source_actions);
if (gdk_drag_context_get_dest_window (drop_context))
_gdk_wayland_drag_context_emit_event (drop_context, GDK_DRAG_MOTION,
GDK_CURRENT_TIME);
}
static void
data_offer_action (void *data,
struct wl_data_offer *wl_data_offer,
uint32_t action)
{
GdkDeviceManager *device_manager;
GdkDragContext *drop_context;
GdkDisplay *display;
GdkDevice *device;
display = gdk_display_get_default ();
device_manager = gdk_display_get_device_manager (display);
device = gdk_device_manager_get_client_pointer (device_manager);
drop_context = gdk_wayland_device_get_drop_context (device);
drop_context->action = _wl_to_gdk_actions (action);
if (gdk_drag_context_get_dest_window (drop_context))
_gdk_wayland_drag_context_emit_event (drop_context, GDK_DRAG_MOTION,
GDK_CURRENT_TIME);
}
static const struct wl_data_offer_listener data_offer_listener = {
data_offer_offer,
data_offer_source_actions,
data_offer_action
};
DataOfferData *
@@ -406,7 +467,6 @@ gdk_wayland_selection_set_offer (GdkDisplay *display,
else if (selection_atom == atoms[ATOM_DND])
selection->dnd_offer = info;
/* Clear all buffers */
g_hash_table_remove_all (selection->selection_buffers);
}
@@ -569,6 +629,9 @@ gdk_wayland_selection_store (GdkWindow *window,
GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
GArray *array;
if (type == gdk_atom_intern_static_string ("NULL"))
return;
array = g_array_new (TRUE, FALSE, sizeof (guchar));
g_array_append_vals (array, data, len);
@@ -715,18 +778,15 @@ data_source_target (void *data,
if (!mime_type)
{
if (context)
{
gdk_wayland_drag_context_set_action (context, 0);
_gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
GDK_CURRENT_TIME);
}
_gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
GDK_CURRENT_TIME);
return;
}
if (source == wayland_selection->dnd_source)
{
window = wayland_selection->dnd_owner;
gdk_wayland_drag_context_set_action (context, GDK_ACTION_COPY);
_gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
GDK_CURRENT_TIME);
}
@@ -748,7 +808,6 @@ data_source_send (void *data,
int32_t fd)
{
GdkWaylandSelection *wayland_selection = data;
GdkDragContext *context;
GdkWindow *window;
g_debug (G_STRLOC ": %s source = %p, mime_type = %s, fd = %d",
@@ -760,8 +819,6 @@ data_source_send (void *data,
return;
}
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (source == wayland_selection->dnd_source)
window = wayland_selection->dnd_owner;
else if (source == wayland_selection->clipboard_source)
@@ -779,13 +836,6 @@ data_source_send (void *data,
gdk_atom_intern (mime_type, FALSE),
fd))
gdk_wayland_selection_check_write (wayland_selection);
if (context)
{
gdk_wayland_device_unset_grab (gdk_drag_context_get_device (context));
_gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
GDK_CURRENT_TIME);
}
}
static void
@@ -814,10 +864,62 @@ data_source_cancelled (void *data,
gdk_wayland_selection_unset_data_source (display, atoms[ATOM_CLIPBOARD]);
}
static void
data_source_action (void *data,
struct wl_data_source *source,
uint32_t action)
{
GdkDragContext *context;
g_debug (G_STRLOC ": %s source = %p action=%x",
G_STRFUNC, source, action);
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (context)
{
context->action = _wl_to_gdk_actions (action);
_gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
GDK_CURRENT_TIME);
}
}
static void
data_source_drop_performed (void *data,
struct wl_data_source *source)
{
}
static void
data_source_drag_finished (void *data,
struct wl_data_source *source)
{
GdkDragContext *context;
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (!context)
return;
if (context->action == GDK_ACTION_MOVE)
{
gdk_wayland_selection_emit_request (context->source_window,
atoms[ATOM_DND],
gdk_atom_intern_static_string ("DELETE"));
}
gdk_wayland_device_unset_grab (gdk_drag_context_get_device (context));
_gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
GDK_CURRENT_TIME);
}
static const struct wl_data_source_listener data_source_listener = {
data_source_target,
data_source_send,
data_source_cancelled
data_source_cancelled,
data_source_action,
data_source_drop_performed,
data_source_drag_finished
};
struct wl_data_source *
@@ -1015,7 +1117,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
offer = gdk_wayland_selection_get_offer (display, selection);
target_list = gdk_wayland_selection_get_targets (display, selection);
if (!offer)
if (!offer || target == gdk_atom_intern_static_string ("DELETE"))
{
GdkEvent *event;
@@ -1192,3 +1294,24 @@ gdk_wayland_selection_clear_targets (GdkDisplay *display,
g_array_set_size (wayland_selection->source_targets, 0);
gdk_wayland_selection_unset_data_source (display, selection);
}
gboolean
gdk_wayland_selection_set_current_offer_actions (GdkDisplay *display,
uint32_t action)
{
struct wl_data_offer *offer;
uint32_t all_actions = 0;
offer = gdk_wayland_selection_get_offer (display, atoms[ATOM_DND]);
if (!offer)
return FALSE;
if (action != 0)
all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE |
WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
wl_data_offer_set_actions (offer, all_actions, action);
return TRUE;
}

View File

@@ -52,6 +52,11 @@ GDK_AVAILABLE_IN_ALL
GdkWindow *
gdk_wayland_drag_context_get_dnd_window (GdkDragContext *context);
#define gdk_wayland_drag_context_commit_status gdk_wayland_drag_context_commit_status_libgtk_only
GDK_AVAILABLE_IN_ALL
void
gdk_wayland_drag_context_commit_status (GdkDragContext *context);
#endif
G_END_DECLS

View File

@@ -786,7 +786,7 @@ gtk_drag_can_use_rgba_cursor (GdkDisplay *display,
gint height)
{
guint max_width, max_height;
if (!gdk_display_supports_cursor_color (display))
return FALSE;
@@ -805,6 +805,25 @@ gtk_drag_can_use_rgba_cursor (GdkDisplay *display,
return TRUE;
}
static gboolean
gtk_drag_cursor_can_merge_drag_icon (GdkDisplay *display,
gint width,
gint height)
{
#ifdef GDK_WINDOWING_WAYLAND
/* On wayland all benefits from merging the drag icon into
* the cursor are moot, and furthermore the compositor is
* responsible of running any "drag cancelled" animations,
* which we won't get for free unless we use the drag
* surface.
*/
if (GDK_IS_WAYLAND_DISPLAY (display))
return FALSE;
#endif
return gtk_drag_can_use_rgba_cursor (display, width, height);
}
static void
ensure_drag_cursor_pixbuf (int i)
{
@@ -927,7 +946,7 @@ gtk_drag_get_cursor (GtkWidget *widget,
width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
if (gtk_drag_can_use_rgba_cursor (display, width * scale, height * scale))
if (gtk_drag_cursor_can_merge_drag_icon (display, width * scale, height * scale))
{
cairo_surface_t *surface;
cairo_t *cr;
@@ -1696,6 +1715,11 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel,
default:
g_assert_not_reached ();
}
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (toplevel)))
gdk_wayland_drag_context_commit_status (context);
#endif
}
/**
@@ -3128,7 +3152,7 @@ set_icon_helper (GdkDragContext *context,
&width, &height);
if (!force_window &&
gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
gtk_drag_cursor_can_merge_drag_icon (display, width + 2, height + 2))
{
GtkDragSourceInfo *info;