/* * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library. If not, see . */ #include "config.h" #include "gdkdropprivate.h" #include "gdkprivate-wayland.h" #include "gdkcontentformats.h" #include "gdkdisplay-wayland.h" #include "gdkintl.h" #include "gdkseat-wayland.h" #include "gdkdeviceprivate.h" #include #include #include #include #define GDK_TYPE_WAYLAND_DROP (gdk_wayland_drop_get_type ()) #define GDK_WAYLAND_DROP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_DROP, GdkWaylandDrop)) #define GDK_WAYLAND_DROP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_DROP, GdkWaylandDropClass)) #define GDK_IS_WAYLAND_DROP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_DROP)) #define GDK_IS_WAYLAND_DROP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_DROP)) #define GDK_WAYLAND_DROP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_DROP, GdkWaylandDropClass)) typedef struct _GdkWaylandDrop GdkWaylandDrop; typedef struct _GdkWaylandDropClass GdkWaylandDropClass; struct _GdkWaylandDrop { GdkDrop drop; struct wl_data_offer *offer; uint32_t source_actions; uint32_t action; uint32_t serial; }; struct _GdkWaylandDropClass { GdkDropClass parent_class; }; GType gdk_wayland_drop_get_type (void); G_DEFINE_TYPE (GdkWaylandDrop, gdk_wayland_drop, GDK_TYPE_DROP) static void gdk_wayland_drop_finalize (GObject *object) { GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (object); g_clear_pointer (&wayland_drop->offer, wl_data_offer_destroy); G_OBJECT_CLASS (gdk_wayland_drop_parent_class)->finalize (object); } static inline uint32_t gdk_to_wl_actions (GdkDragAction action) { uint32_t dnd_actions = 0; if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK)) 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; } static void gdk_wayland_drop_drop_set_status (GdkWaylandDrop *drop_wayland, gboolean accepted) { if (accepted) { const char *const *mimetypes; gsize i, n_mimetypes; /* This is a local drag, treat it like that */ if (gdk_drop_get_drag (GDK_DROP (drop_wayland))) { wl_data_offer_accept (drop_wayland->offer, drop_wayland->serial, GDK_WAYLAND_LOCAL_DND_MIME_TYPE); return; } mimetypes = gdk_content_formats_get_mime_types (gdk_drop_get_formats (GDK_DROP (drop_wayland)), &n_mimetypes); for (i = 0; i < n_mimetypes; i++) { if (mimetypes[i] != g_intern_static_string ("DELETE")) break; } if (i < n_mimetypes) { wl_data_offer_accept (drop_wayland->offer, drop_wayland->serial, mimetypes[i]); return; } } wl_data_offer_accept (drop_wayland->offer, drop_wayland->serial, NULL); } static void gdk_wayland_drop_commit_status (GdkWaylandDrop *wayland_drop, GdkDragAction actions, GdkDragAction preferred) { GdkDisplay *display; display = gdk_drop_get_display (GDK_DROP (wayland_drop)); if (GDK_WAYLAND_DISPLAY (display)->data_device_manager_version >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { uint32_t dnd_actions; uint32_t preferred_action; dnd_actions = gdk_to_wl_actions (actions); preferred_action = gdk_to_wl_actions (preferred); wl_data_offer_set_actions (wayland_drop->offer, dnd_actions, preferred_action); } gdk_wayland_drop_drop_set_status (wayland_drop, actions != 0); } static void gdk_wayland_drop_status (GdkDrop *drop, GdkDragAction actions, GdkDragAction preferred) { GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop); gdk_wayland_drop_commit_status (wayland_drop, actions, preferred); } static void gdk_wayland_drop_finish (GdkDrop *drop, GdkDragAction action) { GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop); GdkDisplay *display = gdk_drop_get_display (drop); GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); if (action) { gdk_wayland_drop_commit_status (wayland_drop, action, action); if (display_wayland->data_device_manager_version >= WL_DATA_OFFER_FINISH_SINCE_VERSION) wl_data_offer_finish (wayland_drop->offer); } g_clear_pointer (&wayland_drop->offer, wl_data_offer_destroy); } static void gdk_wayland_drop_read_async (GdkDrop *drop, GdkContentFormats *formats, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop); GInputStream *stream; const char *mime_type; int pipe_fd[2]; GError *error = NULL; GTask *task; task = g_task_new (drop, cancellable, callback, user_data); g_task_set_priority (task, io_priority); g_task_set_source_tag (task, gdk_wayland_drop_read_async); GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, char *s = gdk_content_formats_to_string (formats); g_message ("%p: read for %s", drop, s); g_free (s); ); mime_type = gdk_content_formats_match_mime_type (formats, gdk_drop_get_formats (drop)); if (mime_type == NULL) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("No compatible transfer format found")); return; } g_task_set_task_data (task, (gpointer) mime_type, NULL); if (!g_unix_open_pipe (pipe_fd, FD_CLOEXEC, &error)) { g_task_return_error (task, error); return; } wl_data_offer_receive (wayland_drop->offer, mime_type, pipe_fd[1]); stream = g_unix_input_stream_new (pipe_fd[0], TRUE); close (pipe_fd[1]); g_task_return_pointer (task, stream, g_object_unref); } static GInputStream * gdk_wayland_drop_read_finish (GdkDrop *drop, GAsyncResult *result, const char **out_mime_type, GError **error) { GTask *task; g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (drop)), NULL); task = G_TASK (result); g_return_val_if_fail (g_task_get_source_tag (task) == gdk_wayland_drop_read_async, NULL); if (out_mime_type) *out_mime_type = g_task_get_task_data (task); return g_task_propagate_pointer (task, error); } static void gdk_wayland_drop_class_init (GdkWaylandDropClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkDropClass *drop_class = GDK_DROP_CLASS (klass); object_class->finalize = gdk_wayland_drop_finalize; drop_class->status = gdk_wayland_drop_status; drop_class->finish = gdk_wayland_drop_finish; drop_class->read_async = gdk_wayland_drop_read_async; drop_class->read_finish = gdk_wayland_drop_read_finish; } static void gdk_wayland_drop_init (GdkWaylandDrop *drop) { } GdkDrop * gdk_wayland_drop_new (GdkDevice *device, GdkDrag *drag, GdkContentFormats *formats, GdkSurface *surface, struct wl_data_offer *offer, uint32_t serial) { GdkWaylandDrop *drop; drop = g_object_new (GDK_TYPE_WAYLAND_DROP, "device", device, "drag", drag, "formats", formats, "surface", surface, NULL); drop->offer = offer; drop->serial = serial; return GDK_DROP (drop); } static void gdk_wayland_drop_update_actions (GdkWaylandDrop *drop) { GdkDragAction gdk_actions = 0; uint32_t wl_actions; if (drop->action == 0 || drop->action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) wl_actions = drop->source_actions; else wl_actions = drop->action; if (wl_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) gdk_actions |= GDK_ACTION_COPY; if (wl_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) gdk_actions |= GDK_ACTION_MOVE; gdk_drop_set_actions (GDK_DROP (drop), gdk_actions); } void gdk_wayland_drop_set_source_actions (GdkDrop *drop, uint32_t source_actions) { GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop); wayland_drop->source_actions = source_actions; gdk_wayland_drop_update_actions (wayland_drop); } void gdk_wayland_drop_set_action (GdkDrop *drop, uint32_t action) { GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop); wayland_drop->action = action; gdk_wayland_drop_update_actions (wayland_drop); }