1 /*
2 * Copyright © 2011 Kristian Høgsberg
3 * 2020 Red Hat Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24 #include "config.h"
25
26 #include <gio/gunixoutputstream.h>
27 #include <glib-unix.h>
28 #include <glib.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "meta/meta-selection.h"
35 #include "wayland/meta-wayland-data-device.h"
36 #include "wayland/meta-wayland-private.h"
37
38 #include "meta-wayland-data-offer.h"
39
40 #define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
41 WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
42 WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
43
44 static void
data_offer_accept(struct wl_client * client,struct wl_resource * resource,guint32 serial,const char * mime_type)45 data_offer_accept (struct wl_client *client,
46 struct wl_resource *resource,
47 guint32 serial,
48 const char *mime_type)
49 {
50 MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
51
52 /* FIXME: Check that client is currently focused by the input
53 * device that is currently dragging this data source. Should
54 * this be a wl_data_device request? */
55
56 if (offer->source)
57 {
58 meta_wayland_data_source_target (offer->source, mime_type);
59 meta_wayland_data_source_set_has_target (offer->source,
60 mime_type != NULL);
61 }
62
63 offer->accepted = mime_type != NULL;
64 }
65
66 static void
transfer_cb(MetaSelection * selection,GAsyncResult * res,GOutputStream * stream)67 transfer_cb (MetaSelection *selection,
68 GAsyncResult *res,
69 GOutputStream *stream)
70 {
71 GError *error = NULL;
72
73 if (!meta_selection_transfer_finish (selection, res, &error))
74 {
75 g_warning ("Could not fetch selection data: %s", error->message);
76 g_error_free (error);
77 }
78
79 g_output_stream_close (stream, NULL, NULL);
80 g_object_unref (stream);
81 }
82
83 static void
data_offer_receive(struct wl_client * client,struct wl_resource * resource,const char * mime_type,int32_t fd)84 data_offer_receive (struct wl_client *client, struct wl_resource *resource,
85 const char *mime_type, int32_t fd)
86 {
87 MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
88 MetaDisplay *display = meta_get_display ();
89 MetaSelectionType selection_type;
90 GList *mime_types;
91 gboolean found;
92
93 selection_type = offer->selection_type;
94 mime_types = meta_selection_get_mimetypes (meta_display_get_selection (display),
95 selection_type);
96 found = g_list_find_custom (mime_types, mime_type, (GCompareFunc) g_strcmp0) != NULL;
97 g_list_free_full (mime_types, g_free);
98
99 if (found)
100 {
101 GOutputStream *stream;
102
103 stream = g_unix_output_stream_new (fd, TRUE);
104 meta_selection_transfer_async (meta_display_get_selection (display),
105 selection_type,
106 mime_type,
107 -1,
108 stream,
109 NULL,
110 (GAsyncReadyCallback) transfer_cb,
111 stream);
112 }
113 else
114 {
115 close (fd);
116 }
117 }
118
119 static void
data_offer_destroy(struct wl_client * client,struct wl_resource * resource)120 data_offer_destroy (struct wl_client *client,
121 struct wl_resource *resource)
122 {
123 wl_resource_destroy (resource);
124 }
125
126 static void
data_offer_finish(struct wl_client * client,struct wl_resource * resource)127 data_offer_finish (struct wl_client *client,
128 struct wl_resource *resource)
129 {
130 MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
131 enum wl_data_device_manager_dnd_action current_action;
132
133 if (!offer->source ||
134 offer != meta_wayland_data_source_get_current_offer (offer->source))
135 return;
136
137 if (!offer->accepted || !offer->action_sent)
138 {
139 wl_resource_post_error (offer->resource,
140 WL_DATA_OFFER_ERROR_INVALID_FINISH,
141 "premature finish request");
142 return;
143 }
144
145 current_action = meta_wayland_data_source_get_current_action (offer->source);
146
147 if (current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE ||
148 current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
149 {
150 wl_resource_post_error (offer->resource,
151 WL_DATA_OFFER_ERROR_INVALID_OFFER,
152 "offer finished with an invalid action");
153 return;
154 }
155
156 meta_wayland_data_source_notify_finish (offer->source);
157 }
158
159 static void
data_offer_set_actions(struct wl_client * client,struct wl_resource * resource,uint32_t dnd_actions,uint32_t preferred_action)160 data_offer_set_actions (struct wl_client *client,
161 struct wl_resource *resource,
162 uint32_t dnd_actions,
163 uint32_t preferred_action)
164 {
165 MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
166
167 if (dnd_actions & ~(ALL_ACTIONS))
168 {
169 wl_resource_post_error (offer->resource,
170 WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
171 "invalid actions mask %x", dnd_actions);
172 return;
173 }
174
175 if (preferred_action &&
176 (!(preferred_action & dnd_actions) ||
177 __builtin_popcount (preferred_action) > 1))
178 {
179 wl_resource_post_error (offer->resource,
180 WL_DATA_OFFER_ERROR_INVALID_ACTION,
181 "invalid action %x", preferred_action);
182 return;
183 }
184
185 offer->dnd_actions = dnd_actions;
186 offer->preferred_dnd_action = preferred_action;
187
188 meta_wayland_data_offer_update_action (offer);
189 }
190
191 static const struct wl_data_offer_interface data_offer_interface = {
192 data_offer_accept,
193 data_offer_receive,
194 data_offer_destroy,
195 data_offer_finish,
196 data_offer_set_actions,
197 };
198
199 static void
destroy_data_offer(struct wl_resource * resource)200 destroy_data_offer (struct wl_resource *resource)
201 {
202 MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
203 MetaWaylandSeat *seat;
204
205 if (offer->source)
206 {
207 seat = meta_wayland_data_source_get_seat (offer->source);
208
209 if (offer == meta_wayland_data_source_get_current_offer (offer->source))
210 {
211 if (seat->data_device.dnd_data_source == offer->source)
212 {
213 if (wl_resource_get_version (offer->resource) <
214 WL_DATA_OFFER_ACTION_SINCE_VERSION)
215 meta_wayland_data_source_notify_finish (offer->source);
216 else if (meta_wayland_data_source_get_drop_performed (offer->source))
217 meta_wayland_data_source_cancel (offer->source);
218 }
219 else
220 {
221 meta_wayland_data_source_set_current_offer (offer->source, NULL);
222 meta_wayland_data_source_set_has_target (offer->source, FALSE);
223 }
224 }
225
226 g_object_remove_weak_pointer (G_OBJECT (offer->source),
227 (gpointer *)&offer->source);
228 offer->source = NULL;
229 }
230
231 meta_display_sync_wayland_input_focus (meta_get_display ());
232 g_free (offer);
233 }
234
235 MetaWaylandDataOffer *
meta_wayland_data_offer_new(MetaSelectionType selection_type,MetaWaylandDataSource * source,struct wl_resource * target)236 meta_wayland_data_offer_new (MetaSelectionType selection_type,
237 MetaWaylandDataSource *source,
238 struct wl_resource *target)
239 {
240 MetaWaylandDataOffer *offer;
241
242 offer = g_new0 (MetaWaylandDataOffer, 1);
243 offer->selection_type = selection_type;
244 offer->resource = wl_resource_create (wl_resource_get_client (target),
245 &wl_data_offer_interface,
246 wl_resource_get_version (target), 0);
247 wl_resource_set_implementation (offer->resource,
248 &data_offer_interface,
249 offer,
250 destroy_data_offer);
251 if (source)
252 {
253 offer->source = source;
254 g_object_add_weak_pointer (G_OBJECT (source), (gpointer *)&offer->source);
255 }
256
257 return offer;
258 }
259
260 static enum wl_data_device_manager_dnd_action
data_offer_choose_action(MetaWaylandDataOffer * offer)261 data_offer_choose_action (MetaWaylandDataOffer *offer)
262 {
263 MetaWaylandDataSource *source = offer->source;
264 uint32_t actions, user_action, available_actions;
265
266 if (wl_resource_get_version (offer->resource) <
267 WL_DATA_OFFER_ACTION_SINCE_VERSION)
268 return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
269
270 meta_wayland_data_source_get_actions (source, &actions);
271 user_action = meta_wayland_data_source_get_user_action (source);
272
273 available_actions = actions & offer->dnd_actions;
274
275 if (!available_actions)
276 return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
277
278 /* If the user is forcing an action, go for it */
279 if ((user_action & available_actions) != 0 &&
280 !(user_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK &&
281 meta_wayland_data_source_get_drop_performed (source)))
282 return user_action;
283
284 /* If the dest side has a preferred DnD action, use it */
285 if ((offer->preferred_dnd_action & available_actions) != 0)
286 return offer->preferred_dnd_action;
287
288 /* Use the first found action, in bit order */
289 return 1 << (ffs (available_actions) - 1);
290 }
291
292 void
meta_wayland_data_offer_update_action(MetaWaylandDataOffer * offer)293 meta_wayland_data_offer_update_action (MetaWaylandDataOffer *offer)
294 {
295 enum wl_data_device_manager_dnd_action current_action, action;
296 MetaWaylandDataSource *source;
297
298 if (!offer->source)
299 return;
300
301 source = offer->source;
302 current_action = meta_wayland_data_source_get_current_action (source);
303 action = data_offer_choose_action (offer);
304
305 if (current_action == action)
306 return;
307
308 meta_wayland_data_source_set_current_action (source, action);
309
310 if (!meta_wayland_data_source_get_in_ask (source) &&
311 wl_resource_get_version (offer->resource) >=
312 WL_DATA_OFFER_ACTION_SINCE_VERSION)
313 {
314 wl_data_offer_send_action (offer->resource, action);
315 offer->action_sent = TRUE;
316 }
317 }
318
319 struct wl_resource *
meta_wayland_data_offer_get_resource(MetaWaylandDataOffer * offer)320 meta_wayland_data_offer_get_resource (MetaWaylandDataOffer *offer)
321 {
322 return offer->resource;
323 }
324
325 MetaWaylandDataSource *
meta_wayland_data_offer_get_source(MetaWaylandDataOffer * offer)326 meta_wayland_data_offer_get_source (MetaWaylandDataOffer *offer)
327 {
328 return offer->source;
329 }
330