wayland: Implement DnD actions as per wl_data_device v3

Implement as a managed GdkDragContext, which actually fits nicely
with the drag-and-drop model in wayland.
This commit is contained in:
Carlos Garnacho 2016-01-13 21:00:34 +01:00
parent 0747a60129
commit 1045dda035
7 changed files with 309 additions and 114 deletions

View File

@ -883,16 +883,10 @@ data_device_drop (void *data,
struct wl_data_device *data_device)
{
GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
GdkWindow *local_dnd_owner;
GDK_NOTE (EVENTS,
g_message ("data device drop, data device %p", data_device));
local_dnd_owner = gdk_selection_owner_get_for_display (device->display, gdk_drag_get_selection (device->drop_context));
if (local_dnd_owner)
gdk_wayland_device_unset_grab (device->master_pointer);
_gdk_wayland_drag_context_emit_event (device->drop_context,
GDK_DROP_START, GDK_CURRENT_TIME);
}
@ -3105,61 +3099,6 @@ gdk_wayland_device_set_selection (GdkDevice *gdk_device,
_gdk_wayland_display_get_serial (display_wayland));
}
void
gdk_wayland_device_unset_grab (GdkDevice *gdk_device)
{
GdkWaylandDeviceData *device;
GdkEventSequence *sequence;
GdkModifierType state;
GdkEvent *event;
guint button;
gdouble x, y;
device = GDK_WAYLAND_DEVICE (gdk_device)->device;
_gdk_wayland_device_get_last_implicit_grab_serial (GDK_WAYLAND_DEVICE (gdk_device), &sequence);
gdk_window_get_device_position_double (device->pointer_grab_window,
gdk_device, &x, &y, &state);
if (sequence)
{
event = gdk_event_new (GDK_TOUCH_END);
event->touch.window = g_object_ref (device->pointer_grab_window);
event->touch.send_event = TRUE;
event->touch.sequence = sequence;
event->touch.time = GDK_CURRENT_TIME;
event->touch.x = event->touch.x_root = x;
event->touch.y = event->touch.y_root = y;
}
else if (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK))
{
if (state & GDK_BUTTON1_MASK)
button = 1;
else if (state & GDK_BUTTON2_MASK)
button = 2;
else if (state & GDK_BUTTON3_MASK)
button = 3;
else
return;
event = gdk_event_new (GDK_BUTTON_RELEASE);
event->button.window = g_object_ref (device->pointer_grab_window);
event->button.send_event = TRUE;
event->button.button = button;
event->button.time = GDK_CURRENT_TIME;
event->button.x = event->button.x_root = x;
event->button.y = event->button.y_root = y;
}
else
return;
device->button_modifiers = 0;
gdk_event_set_device (event, gdk_device);
gdk_event_set_source_device (event, gdk_device);
gdk_event_set_seat (event, gdk_device_get_seat (gdk_device));
_gdk_wayland_display_deliver_event (gdk_device_get_display (gdk_device), event);
}
struct wl_seat *
gdk_wayland_seat_get_wl_seat (GdkSeat *seat)
{
@ -3167,3 +3106,11 @@ gdk_wayland_seat_get_wl_seat (GdkSeat *seat)
return GDK_WAYLAND_SEAT (seat)->wl_seat;
}
GdkDragContext *
gdk_wayland_device_get_drop_context (GdkDevice *device)
{
GdkSeat *seat = gdk_device_get_seat (device);
return GDK_WAYLAND_SEAT (seat)->drop_context;
}

View File

@ -314,8 +314,10 @@ gdk_registry_handle_global (void *data,
}
else if (strcmp (interface, "wl_data_device_manager") == 0)
{
display_wayland->data_device_manager_version = MIN (version, 3);
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,
display_wayland->data_device_manager_version);
}
else if (strcmp (interface, "wl_subcompositor") == 0)
{

View File

@ -93,6 +93,7 @@ struct _GdkWaylandDisplay
int compositor_version;
int seat_version;
int data_device_manager_version;
struct xkb_context *xkb_context;

View File

@ -24,6 +24,7 @@
#include "gdkproperty.h"
#include "gdkprivate-wayland.h"
#include "gdkdisplay-wayland.h"
#include "gdkseat-wayland.h"
#include "gdkdeviceprivate.h"
@ -45,6 +46,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;
@ -82,6 +84,8 @@ gdk_wayland_drag_context_finalize (GObject *object)
selection_owner = gdk_selection_owner_get_for_display (display, selection);
if (selection_owner == context->source_window)
gdk_wayland_selection_unset_data_source (display, selection);
gdk_drag_context_set_cursor (context, NULL);
}
if (wayland_context->data_source)
@ -157,6 +161,21 @@ gdk_wayland_drag_context_find_window (GdkDragContext *context,
return NULL;
}
static inline uint32_t
gdk_to_wl_actions (GdkDragAction 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_action (GdkDragContext *context,
GdkDragAction action)
@ -208,6 +227,9 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context,
GdkDisplay *display;
struct wl_data_offer *wl_offer;
if (!context->dest_window)
return;
context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
display = gdk_device_get_display (gdk_drag_context_get_device (context));
@ -240,12 +262,33 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context,
wl_data_offer_accept (wl_offer, context_wayland->serial, NULL);
}
static 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);
}
static void
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;
gdk_wayland_drag_context_commit_status (context);
}
static void
@ -263,12 +306,24 @@ gdk_wayland_drag_context_drop_finish (GdkDragContext *context,
guint32 time)
{
GdkDisplay *display = gdk_device_get_display (gdk_drag_context_get_device (context));
GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (display);
GdkWaylandDragContext *wayland_context;
struct wl_data_offer *wl_offer;
GdkAtom selection;
wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
selection = gdk_drag_get_selection (context);
wl_offer = gdk_wayland_selection_get_offer (display, selection);
if (gdk_selection_owner_get_for_display (display, selection))
gdk_wayland_selection_unset_data_source (display, selection);
if (success && wayland_context->selected_action &&
wayland_context->selected_action != GDK_ACTION_ASK)
{
gdk_wayland_drag_context_commit_status (context);
if (wayland_display->data_device_manager_version >=
WL_DATA_OFFER_FINISH_SINCE_VERSION)
wl_data_offer_finish (wl_offer);
}
gdk_wayland_selection_set_offer (display, selection, NULL);
}
@ -325,6 +380,72 @@ gdk_wayland_drag_context_set_hotspot (GdkDragContext *context,
gdk_window_invalidate_rect (context_wayland->dnd_window, &damage_rect, FALSE);
}
static gboolean
gdk_wayland_drag_context_manage_dnd (GdkDragContext *context,
GdkWindow *ipc_window,
GdkDragAction actions)
{
GdkWaylandDragContext *context_wayland;
GdkWaylandDisplay *display_wayland;
GdkDevice *device;
GdkWindow *toplevel;
device = gdk_drag_context_get_device (context);
display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (device));
toplevel = _gdk_device_window_at_position (device, NULL, NULL, NULL, TRUE);
context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
if (display_wayland->data_device_manager_version >=
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION)
{
wl_data_source_set_actions (context_wayland->data_source,
gdk_to_wl_actions (actions));
}
wl_data_device_start_drag (gdk_wayland_device_get_data_device (device),
context_wayland->data_source,
gdk_wayland_window_get_wl_surface (toplevel),
context_wayland->dnd_surface,
_gdk_wayland_display_get_serial (display_wayland));
gdk_seat_ungrab (gdk_device_get_seat (device));
return TRUE;
}
static void
gdk_wayland_drag_context_set_cursor (GdkDragContext *context,
GdkCursor *cursor)
{
GdkDevice *device = gdk_drag_context_get_device (context);
gdk_wayland_seat_set_global_cursor (gdk_device_get_seat (device), cursor);
}
static void
gdk_wayland_drag_context_action_changed (GdkDragContext *context,
GdkDragAction action)
{
GdkCursor *cursor;
cursor = gdk_drag_get_cursor (action);
gdk_drag_context_set_cursor (context, cursor);
}
static void
gdk_wayland_drag_context_drop_performed (GdkDragContext *context,
guint32 time_)
{
gdk_drag_context_set_cursor (context, NULL);
}
static void
gdk_wayland_drag_context_cancel (GdkDragContext *context)
{
gdk_drag_context_set_cursor (context, NULL);
}
static void
gdk_wayland_drag_context_class_init (GdkWaylandDragContextClass *klass)
{
@ -344,6 +465,11 @@ gdk_wayland_drag_context_class_init (GdkWaylandDragContextClass *klass)
context_class->get_selection = gdk_wayland_drag_context_get_selection;
context_class->get_drag_window = gdk_wayland_drag_context_get_drag_window;
context_class->set_hotspot = gdk_wayland_drag_context_set_hotspot;
context_class->manage_dnd = gdk_wayland_drag_context_manage_dnd;
context_class->set_cursor = gdk_wayland_drag_context_set_cursor;
context_class->action_changed = gdk_wayland_drag_context_action_changed;
context_class->drop_performed = gdk_wayland_drag_context_drop_performed;
context_class->cancel = gdk_wayland_drag_context_cancel;
}
GdkDragProtocol
@ -383,13 +509,9 @@ _gdk_wayland_window_drag_begin (GdkWindow *window,
gint y_root)
{
GdkWaylandDragContext *context_wayland;
GdkWaylandDisplay *display_wayland;
GdkDragContext *context;
GdkWindow *toplevel;
GList *l;
toplevel = _gdk_device_window_at_position (device, NULL, NULL, NULL, TRUE);
context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL);
context = GDK_DRAG_CONTEXT (context_wayland);
context->source_window = g_object_ref (window);
@ -397,7 +519,6 @@ _gdk_wayland_window_drag_begin (GdkWindow *window,
context->targets = g_list_copy (targets);
gdk_drag_context_set_device (context, device);
display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (device));
context_wayland->dnd_window = create_dnd_window (gdk_window_get_screen (window));
context_wayland->dnd_surface = gdk_wayland_window_get_wl_surface (context_wayland->dnd_window);
@ -413,12 +534,6 @@ _gdk_wayland_window_drag_begin (GdkWindow *window,
g_free (mimetype);
}
wl_data_device_start_drag (gdk_wayland_device_get_data_device (device),
context_wayland->data_source,
gdk_wayland_window_get_wl_surface (toplevel),
context_wayland->dnd_surface,
_gdk_wayland_display_get_serial (display_wayland));
return context;
}

View File

@ -188,7 +188,7 @@ struct wl_data_device * gdk_wayland_device_get_data_device (GdkDevice *gdk_devic
void gdk_wayland_device_set_selection (GdkDevice *gdk_device,
struct wl_data_source *source);
void gdk_wayland_device_unset_grab (GdkDevice *device);
GdkDragContext * gdk_wayland_device_get_drop_context (GdkDevice *gdk_device);
void gdk_wayland_device_unset_touch_grab (GdkDevice *device,
GdkEventSequence *sequence);
@ -258,6 +258,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)
{
GdkDragContext *drop_context;
GdkDisplay *display;
GdkDevice *device;
GdkSeat *seat;
display = gdk_display_get_default ();
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
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)
{
GdkDragContext *drop_context;
GdkDisplay *display;
GdkDevice *device;
GdkSeat *seat;
display = gdk_display_get_default ();
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
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 *
@ -569,6 +630,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);
@ -701,32 +765,16 @@ data_source_target (void *data,
const char *mime_type)
{
GdkWaylandSelection *wayland_selection = data;
GdkDragContext *context = NULL;
GdkWindow *window = NULL;
g_debug (G_STRLOC ": %s source = %p, mime_type = %s",
G_STRFUNC, source, mime_type);
context = gdk_wayland_drag_context_lookup_by_data_source (source);
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);
}
return;
}
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);
}
window = wayland_selection->dnd_owner;
else if (source == wayland_selection->clipboard_source)
window = wayland_selection->clipboard_owner;
@ -745,7 +793,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",
@ -757,8 +804,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)
@ -776,13 +821,6 @@ data_source_send (void *data,
gdk_atom_intern (mime_type, FALSE),
fd))
gdk_wayland_selection_check_write (wayland_selection);
if (context)
{
_gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
GDK_CURRENT_TIME);
gdk_wayland_device_unset_grab (gdk_drag_context_get_device (context));
}
}
static void
@ -806,22 +844,79 @@ data_source_cancelled (void *data,
else
return;
gdk_wayland_selection_unset_data_source (display, atom);
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (context)
gdk_drag_context_cancel (context);
gdk_selection_owner_set (NULL, atom, GDK_CURRENT_TIME, TRUE);
gdk_wayland_selection_unset_data_source (display, atom);
}
if (source == wayland_selection->dnd_source)
static void
data_source_dnd_drop_performed (void *data,
struct wl_data_source *source)
{
GdkDragContext *context;
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (!context)
return;
g_signal_emit_by_name (context, "drop-performed", GDK_CURRENT_TIME);
}
static void
data_source_dnd_finished (void *data,
struct wl_data_source *source)
{
GdkDisplay *display = gdk_display_get_default ();
GdkDragContext *context;
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (!context)
return;
if (context->action == GDK_ACTION_MOVE)
{
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (context)
gdk_wayland_device_unset_grab (gdk_drag_context_get_device (context));
gdk_wayland_selection_emit_request (context->source_window,
atoms[ATOM_DND],
gdk_atom_intern_static_string ("DELETE"));
}
g_signal_emit_by_name (context, "dnd-finished");
gdk_selection_owner_set (NULL, atoms[ATOM_DND], GDK_CURRENT_TIME, TRUE);
gdk_wayland_selection_unset_data_source (display, atoms[ATOM_DND]);
}
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)
return;
context->action = _wl_to_gdk_actions (action);
g_signal_emit_by_name (context, "action-changed", context->action);
}
static const struct wl_data_source_listener data_source_listener = {
data_source_target,
data_source_send,
data_source_cancelled
data_source_cancelled,
data_source_dnd_drop_performed,
data_source_dnd_finished,
data_source_action,
};
struct wl_data_source *
@ -1009,7 +1104,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;
@ -1185,3 +1280,27 @@ 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)
{
GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (display);
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;
if (wayland_display->data_device_manager_version >=
WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION)
wl_data_offer_set_actions (offer, all_actions, action);
return TRUE;
}

View File

@ -42,6 +42,10 @@
#endif
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
#endif
#include "gtkgesturedrag.h"
#include "gtkgesturesingle.h"
#include "gtkicontheme.h"
@ -2174,9 +2178,14 @@ gtk_drag_begin_internal (GtkWidget *widget,
GdkAtom selection;
gboolean managed = FALSE;
managed =
#ifdef GDK_WINDOWING_X11
managed = GDK_IS_X11_DISPLAY (gtk_widget_get_display (widget));
GDK_IS_X11_DISPLAY (gtk_widget_get_display (widget)) ||
#endif
#ifdef GDK_WINDOWING_WAYLAND
GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget)) ||
#endif
FALSE;
pointer = keyboard = NULL;
ipc_widget = gtk_drag_get_ipc_widget (widget);