From 7b85a3417a86916753e973da2c8f622bf610f94b Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 21 Aug 2014 21:30:22 +0200 Subject: [PATCH] wayland: Implement drag sources The wl_data_source is retrieved from the selection object for the DnD selection, and used to initiate a drag. When the drag is finished, a button release or touch end event is synthesized to finish the DnD operation after the compositor grab is gone. https://bugzilla.gnome.org/show_bug.cgi?id=697855 --- gdk/wayland/gdkdevice-wayland.c | 19 +++- gdk/wayland/gdkdnd-wayland.c | 147 ++++++++++++++++++++++++++++- gdk/wayland/gdkprivate-wayland.h | 6 ++ gdk/wayland/gdkselection-wayland.c | 37 +++++++- 4 files changed, 203 insertions(+), 6 deletions(-) diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index 74142f308b..4e773179c2 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -523,7 +523,7 @@ data_device_enter (void *data, struct wl_data_offer *offer) { GdkWaylandDeviceData *device = (GdkWaylandDeviceData *)data; - GdkWindow *dest_window; + GdkWindow *dest_window, *dnd_owner; dest_window = wl_surface_get_user_data (surface); @@ -539,6 +539,12 @@ data_device_enter (void *data, device->surface_y = wl_fixed_to_double (y); gdk_wayland_drop_context_update_targets (device->drop_context); + + dnd_owner = gdk_selection_owner_get (gdk_drag_get_selection (device->drop_context)); + + if (dnd_owner) + _gdk_wayland_drag_context_set_source_window (device->drop_context, dnd_owner); + _gdk_wayland_drag_context_set_dest_window (device->drop_context, dest_window, serial); _gdk_wayland_drag_context_set_coords (device->drop_context, @@ -601,10 +607,21 @@ data_device_drop (void *data, struct wl_data_device *data_device) { GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data; + GdkWindow *local_dnd_owner; g_debug (G_STRLOC ": %s data_device = %p", G_STRFUNC, data_device); + local_dnd_owner = gdk_selection_owner_get (gdk_drag_get_selection (device->drop_context)); + + if (local_dnd_owner) + { + GdkDragContext *source_context; + + source_context = gdk_wayland_drag_context_lookup_by_source_window (local_dnd_owner); + gdk_wayland_drag_context_undo_grab (source_context); + } + _gdk_wayland_drag_context_emit_event (device->drop_context, GDK_DROP_START, GDK_CURRENT_TIME); } diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c index 099a95ae1b..fef24c6678 100644 --- a/gdk/wayland/gdkdnd-wayland.c +++ b/gdk/wayland/gdkdnd-wayland.c @@ -40,6 +40,9 @@ typedef struct _GdkWaylandDragContextClass GdkWaylandDragContextClass; struct _GdkWaylandDragContext { GdkDragContext context; + GdkWindow *dnd_window; + struct wl_surface *dnd_surface; + struct wl_data_source *data_source; struct wl_data_offer *offer; uint32_t serial; gdouble x; @@ -63,6 +66,10 @@ gdk_wayland_drag_context_finalize (GObject *object) contexts = g_list_remove (contexts, context); + if (context->is_source && + gdk_selection_owner_get (gdk_drag_get_selection (context)) == context->source_window) + gdk_wayland_selection_unset_data_source (gdk_drag_get_selection (context)); + if (wayland_context->data_source) wl_data_source_destroy (wayland_context->data_source); @@ -232,6 +239,8 @@ gdk_wayland_drag_context_drop_finish (GdkDragContext *context, gboolean success, guint32 time) { + if (gdk_selection_owner_get (gdk_drag_get_selection (context))) + gdk_wayland_selection_unset_data_source (gdk_drag_get_selection (context)); } static gboolean @@ -289,18 +298,63 @@ _gdk_wayland_window_register_dnd (GdkWindow *window) { } +static GdkWindow * +create_dnd_window (void) +{ + GdkWindowAttr attrs; + GdkScreen *screen; + guint mask; + + screen = gdk_display_get_default_screen (gdk_display_get_default ()); + + attrs.x = attrs.y = 0; + attrs.width = attrs.height = 100; + attrs.wclass = GDK_INPUT_OUTPUT; + attrs.window_type = GDK_WINDOW_TEMP; + attrs.type_hint = GDK_WINDOW_TYPE_HINT_DND; + attrs.visual = gdk_screen_get_system_visual (screen); + + mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT; + + return gdk_window_new (gdk_screen_get_root_window (screen), &attrs, mask); +} + GdkDragContext * _gdk_wayland_window_drag_begin (GdkWindow *window, GdkDevice *device, GList *targets) { + GdkWaylandDragContext *context_wayland; + GdkWaylandDisplay *display_wayland; GdkDragContext *context; + GdkWindow *toplevel; + GList *l; - context = (GdkDragContext *) g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL); - context->source_window = window; + toplevel = gdk_device_get_window_at_position (device, NULL, NULL); + + context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL); + context = GDK_DRAG_CONTEXT (context_wayland); + context->source_window = g_object_ref (window); context->is_source = TRUE; + 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 (); + context_wayland->dnd_surface = gdk_wayland_window_get_wl_surface (context_wayland->dnd_window); + context_wayland->data_source = + gdk_wayland_selection_get_data_source (window, + gdk_wayland_drag_context_get_selection (context)); + + for (l = context->targets; l; l = l->next) + wl_data_source_offer (context_wayland->data_source, gdk_atom_name (l->data)); + + 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; } @@ -359,3 +413,92 @@ _gdk_wayland_drag_context_set_dest_window (GdkDragContext *context, GDK_WAYLAND_DRAG_CONTEXT (context)->serial = serial; gdk_wayland_drop_context_update_targets (context); } + +GdkDragContext * +gdk_wayland_drag_context_lookup_by_data_source (struct wl_data_source *source) +{ + GList *l; + + for (l = contexts; l; l = l->next) + { + GdkWaylandDragContext *wayland_context = l->data; + + if (wayland_context->data_source == source) + return l->data; + } + + return NULL; +} + +GdkDragContext * +gdk_wayland_drag_context_lookup_by_source_window (GdkWindow *window) +{ + GList *l; + + for (l = contexts; l; l = l->next) + { + if (window == gdk_drag_context_get_source_window (l->data)) + return l->data; + } + + return NULL; +} + +struct wl_data_source * +gdk_wayland_drag_context_get_data_source (GdkDragContext *context) +{ + return GDK_WAYLAND_DRAG_CONTEXT (context)->data_source; +} + +void +gdk_wayland_drag_context_undo_grab (GdkDragContext *context) +{ + GdkEventSequence *sequence; + GdkModifierType state; + GdkDevice *device; + GdkEvent *event; + guint button; + gdouble x, y; + + device = gdk_drag_context_get_device (context); + _gdk_wayland_device_get_last_implicit_grab_serial (GDK_WAYLAND_DEVICE (device), &sequence); + gdk_window_get_device_position_double (gdk_drag_context_get_source_window (context), + device, &x, &y, &state); + + if (sequence) + { + event = gdk_event_new (GDK_TOUCH_END); + event->touch.window = g_object_ref (gdk_drag_context_get_source_window (context)); + 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 (gdk_drag_context_get_source_window (context)); + 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.x_root = y; + } + else + return; + + gdk_event_set_device (event, device); + gdk_event_set_source_device (event, device); + + _gdk_wayland_display_deliver_event (gdk_device_get_display (device), event); +} diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index 0d5eebc18a..b1e6e146aa 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -109,6 +109,12 @@ void _gdk_wayland_drag_context_set_coords (GdkDragContext *context, void gdk_wayland_drag_context_set_action (GdkDragContext *context, GdkDragAction action); +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); +struct wl_data_source * gdk_wayland_drag_context_get_data_source (GdkDragContext *context); + +void gdk_wayland_drag_context_undo_grab (GdkDragContext *context); + void gdk_wayland_drop_context_update_targets (GdkDragContext *context); void _gdk_wayland_display_create_window_impl (GdkDisplay *display, diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c index 37c197da5f..965bb2ce22 100644 --- a/gdk/wayland/gdkselection-wayland.c +++ b/gdk/wayland/gdkselection-wayland.c @@ -500,17 +500,32 @@ data_source_target (void *data, const char *mime_type) { GdkWaylandSelection *wayland_selection = data; - GdkDragContext *context; + GdkDragContext *context = NULL; GdkWindow *window; 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) - return; + { + if (context) + { + gdk_wayland_drag_context_set_action (context, 0); + _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; + { + 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); + } else if (source == wayland_selection->clipboard_source) window = wayland_selection->clipboard_owner; else @@ -528,6 +543,7 @@ 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", @@ -536,6 +552,8 @@ data_source_send (void *data, if (!mime_type) 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) @@ -548,6 +566,13 @@ data_source_send (void *data, fd)) gdk_wayland_selection_check_write (wayland_selection); + if (context) + { + gdk_wayland_drag_context_undo_grab (context); + _gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED, + GDK_CURRENT_TIME); + } + wayland_selection->source_requested_target = GDK_NONE; } @@ -556,14 +581,20 @@ data_source_cancelled (void *data, struct wl_data_source *source) { GdkWaylandSelection *wayland_selection = data; + GdkDragContext *context; g_debug (G_STRLOC ": %s source = %p", G_STRFUNC, source); + context = gdk_wayland_drag_context_lookup_by_data_source (source); + if (source == wayland_selection->dnd_source) gdk_wayland_selection_unset_data_source (atoms[ATOM_DND]); else if (source == wayland_selection->clipboard_source) gdk_wayland_selection_unset_data_source (atoms[ATOM_CLIPBOARD]); + + if (context) + gdk_wayland_drag_context_undo_grab (context); } static const struct wl_data_source_listener data_source_listener = {