1/*
2 * Copyright © 2010 Intel Corporation
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gdkdropprivate.h"
21
22#include "gdkprivate-wayland.h"
23#include "gdkcontentformats.h"
24#include "gdkdisplay-wayland.h"
25#include "gdkintl.h"
26#include "gdkseat-wayland.h"
27
28#include "gdkdeviceprivate.h"
29
30#include <glib-unix.h>
31#include <gio/gunixinputstream.h>
32#include <gio/gunixoutputstream.h>
33#include <string.h>
34
35#define GDK_TYPE_WAYLAND_DROP (gdk_wayland_drop_get_type ())
36#define GDK_WAYLAND_DROP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_DROP, GdkWaylandDrop))
37#define GDK_WAYLAND_DROP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_DROP, GdkWaylandDropClass))
38#define GDK_IS_WAYLAND_DROP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_DROP))
39#define GDK_IS_WAYLAND_DROP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_DROP))
40#define GDK_WAYLAND_DROP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_DROP, GdkWaylandDropClass))
41
42typedef struct _GdkWaylandDrop GdkWaylandDrop;
43typedef struct _GdkWaylandDropClass GdkWaylandDropClass;
44
45struct _GdkWaylandDrop
46{
47 GdkDrop drop;
48
49 struct wl_data_offer *offer;
50 uint32_t source_actions;
51 uint32_t action;
52 uint32_t serial;
53};
54
55struct _GdkWaylandDropClass
56{
57 GdkDropClass parent_class;
58};
59
60GType gdk_wayland_drop_get_type (void);
61
62G_DEFINE_TYPE (GdkWaylandDrop, gdk_wayland_drop, GDK_TYPE_DROP)
63
64static void
65gdk_wayland_drop_finalize (GObject *object)
66{
67 GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (object);
68
69 g_clear_pointer (&wayland_drop->offer, wl_data_offer_destroy);
70
71 G_OBJECT_CLASS (gdk_wayland_drop_parent_class)->finalize (object);
72}
73
74static inline uint32_t
75gdk_to_wl_actions (GdkDragAction action)
76{
77 uint32_t dnd_actions = 0;
78
79 if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK))
80 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
81 if (action & GDK_ACTION_MOVE)
82 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
83 if (action & GDK_ACTION_ASK)
84 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
85
86 return dnd_actions;
87}
88
89static void
90gdk_wayland_drop_drop_set_status (GdkWaylandDrop *drop_wayland,
91 gboolean accepted)
92{
93 if (accepted)
94 {
95 const char *const *mimetypes;
96 gsize i, n_mimetypes;
97
98 /* This is a local drag, treat it like that */
99 if (gdk_drop_get_drag (GDK_DROP (drop_wayland)))
100 {
101 wl_data_offer_accept (wl_data_offer: drop_wayland->offer, serial: drop_wayland->serial, GDK_WAYLAND_LOCAL_DND_MIME_TYPE);
102 return;
103 }
104
105 mimetypes = gdk_content_formats_get_mime_types (formats: gdk_drop_get_formats (GDK_DROP (drop_wayland)), n_mime_types: &n_mimetypes);
106 for (i = 0; i < n_mimetypes; i++)
107 {
108 if (mimetypes[i] != g_intern_static_string (string: "DELETE"))
109 break;
110 }
111
112 if (i < n_mimetypes)
113 {
114 wl_data_offer_accept (wl_data_offer: drop_wayland->offer, serial: drop_wayland->serial, mime_type: mimetypes[i]);
115 return;
116 }
117 }
118
119 wl_data_offer_accept (wl_data_offer: drop_wayland->offer, serial: drop_wayland->serial, NULL);
120}
121
122static void
123gdk_wayland_drop_commit_status (GdkWaylandDrop *wayland_drop,
124 GdkDragAction actions,
125 GdkDragAction preferred)
126{
127 GdkDisplay *display;
128
129 display = gdk_drop_get_display (GDK_DROP (wayland_drop));
130
131 if (GDK_WAYLAND_DISPLAY (display)->data_device_manager_version >=
132 WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION)
133 {
134 uint32_t dnd_actions;
135 uint32_t preferred_action;
136
137 dnd_actions = gdk_to_wl_actions (action: actions);
138 preferred_action = gdk_to_wl_actions (action: preferred);
139
140 wl_data_offer_set_actions (wl_data_offer: wayland_drop->offer, dnd_actions, preferred_action);
141 }
142
143 gdk_wayland_drop_drop_set_status (drop_wayland: wayland_drop, accepted: actions != 0);
144}
145
146static void
147gdk_wayland_drop_status (GdkDrop *drop,
148 GdkDragAction actions,
149 GdkDragAction preferred)
150{
151 GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop);
152
153 gdk_wayland_drop_commit_status (wayland_drop, actions, preferred);
154}
155
156static void
157gdk_wayland_drop_finish (GdkDrop *drop,
158 GdkDragAction action)
159{
160 GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop);
161 GdkDisplay *display = gdk_drop_get_display (self: drop);
162 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
163
164 if (action)
165 {
166 gdk_wayland_drop_commit_status (wayland_drop, actions: action, preferred: action);
167
168 if (display_wayland->data_device_manager_version >=
169 WL_DATA_OFFER_FINISH_SINCE_VERSION)
170 wl_data_offer_finish (wl_data_offer: wayland_drop->offer);
171 }
172
173 g_clear_pointer (&wayland_drop->offer, wl_data_offer_destroy);
174}
175
176static void
177gdk_wayland_drop_read_async (GdkDrop *drop,
178 GdkContentFormats *formats,
179 int io_priority,
180 GCancellable *cancellable,
181 GAsyncReadyCallback callback,
182 gpointer user_data)
183{
184 GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop);
185 GInputStream *stream;
186 const char *mime_type;
187 int pipe_fd[2];
188 GError *error = NULL;
189 GTask *task;
190
191 task = g_task_new (source_object: drop, cancellable, callback, callback_data: user_data);
192 g_task_set_priority (task, priority: io_priority);
193 g_task_set_source_tag (task, gdk_wayland_drop_read_async);
194
195 GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, char *s = gdk_content_formats_to_string (formats);
196 g_message ("%p: read for %s", drop, s);
197 g_free (s); );
198 mime_type = gdk_content_formats_match_mime_type (first: formats,
199 second: gdk_drop_get_formats (self: drop));
200 if (mime_type == NULL)
201 {
202 g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
203 _("No compatible transfer format found"));
204 return;
205 }
206
207 g_task_set_task_data (task, task_data: (gpointer) mime_type, NULL);
208
209 if (!g_unix_open_pipe (fds: pipe_fd, FD_CLOEXEC, error: &error))
210 {
211 g_task_return_error (task, error);
212 return;
213 }
214
215 wl_data_offer_receive (wl_data_offer: wayland_drop->offer, mime_type, fd: pipe_fd[1]);
216 stream = g_unix_input_stream_new (fd: pipe_fd[0], TRUE);
217 close (fd: pipe_fd[1]);
218 g_task_return_pointer (task, result: stream, result_destroy: g_object_unref);
219}
220
221static GInputStream *
222gdk_wayland_drop_read_finish (GdkDrop *drop,
223 GAsyncResult *result,
224 const char **out_mime_type,
225 GError **error)
226{
227 GTask *task;
228
229 g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (drop)), NULL);
230 task = G_TASK (result);
231 g_return_val_if_fail (g_task_get_source_tag (task) == gdk_wayland_drop_read_async, NULL);
232
233 if (out_mime_type)
234 *out_mime_type = g_task_get_task_data (task);
235
236 return g_task_propagate_pointer (task, error);
237}
238
239static void
240gdk_wayland_drop_class_init (GdkWaylandDropClass *klass)
241{
242 GObjectClass *object_class = G_OBJECT_CLASS (klass);
243 GdkDropClass *drop_class = GDK_DROP_CLASS (klass);
244
245 object_class->finalize = gdk_wayland_drop_finalize;
246
247 drop_class->status = gdk_wayland_drop_status;
248 drop_class->finish = gdk_wayland_drop_finish;
249 drop_class->read_async = gdk_wayland_drop_read_async;
250 drop_class->read_finish = gdk_wayland_drop_read_finish;
251}
252
253static void
254gdk_wayland_drop_init (GdkWaylandDrop *drop)
255{
256}
257
258GdkDrop *
259gdk_wayland_drop_new (GdkDevice *device,
260 GdkDrag *drag,
261 GdkContentFormats *formats,
262 GdkSurface *surface,
263 struct wl_data_offer *offer,
264 uint32_t serial)
265
266{
267 GdkWaylandDrop *drop;
268
269 drop = g_object_new (GDK_TYPE_WAYLAND_DROP,
270 first_property_name: "device", device,
271 "drag", drag,
272 "formats", formats,
273 "surface", surface,
274 NULL);
275 drop->offer = offer;
276 drop->serial = serial;
277
278 return GDK_DROP (drop);
279}
280
281static void
282gdk_wayland_drop_update_actions (GdkWaylandDrop *drop)
283{
284 GdkDragAction gdk_actions = 0;
285 uint32_t wl_actions;
286
287 if (drop->action == 0 ||
288 drop->action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
289 wl_actions = drop->source_actions;
290 else
291 wl_actions = drop->action;
292
293 if (wl_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
294 gdk_actions |= GDK_ACTION_COPY;
295 if (wl_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
296 gdk_actions |= GDK_ACTION_MOVE;
297
298 gdk_drop_set_actions (GDK_DROP (drop), actions: gdk_actions);
299}
300
301void
302gdk_wayland_drop_set_source_actions (GdkDrop *drop,
303 uint32_t source_actions)
304{
305 GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop);
306
307 wayland_drop->source_actions = source_actions;
308
309 gdk_wayland_drop_update_actions (drop: wayland_drop);
310}
311
312void
313gdk_wayland_drop_set_action (GdkDrop *drop,
314 uint32_t action)
315{
316 GdkWaylandDrop *wayland_drop = GDK_WAYLAND_DROP (drop);
317
318 wayland_drop->action = action;
319
320 gdk_wayland_drop_update_actions (drop: wayland_drop);
321}
322
323

source code of gtk/gdk/wayland/gdkdrop-wayland.c