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