1 /*
2  * Copyright © 2018 Benjamin Otte
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19 
20 /**
21  * GdkDrop:
22  *
23  * The `GdkDrop` object represents the target of an ongoing DND operation.
24  *
25  * Possible drop sites get informed about the status of the ongoing drag
26  * operation with events of type %GDK_DRAG_ENTER, %GDK_DRAG_LEAVE,
27  * %GDK_DRAG_MOTION and %GDK_DROP_START. The `GdkDrop` object can be obtained
28  * from these [class@Gdk.Event] types using [method@Gdk.DNDEvent.get_drop].
29  *
30  * The actual data transfer is initiated from the target side via an async
31  * read, using one of the `GdkDrop` methods for this purpose:
32  * [method@Gdk.Drop.read_async] or [method@Gdk.Drop.read_value_async].
33  *
34  * GTK provides a higher level abstraction based on top of these functions,
35  * and so they are not normally needed in GTK applications. See the
36  * "Drag and Drop" section of the GTK documentation for more information.
37  */
38 
39 #include "config.h"
40 
41 #include "gdkdropprivate.h"
42 
43 #include "gdkcontentdeserializer.h"
44 #include "gdkcontentformats.h"
45 #include "gdkcontentprovider.h"
46 #include "gdkcontentserializer.h"
47 #include "gdkcursor.h"
48 #include "gdkdisplay.h"
49 #include "gdkenumtypes.h"
50 #include "gdkeventsprivate.h"
51 #include "gdkinternals.h"
52 #include "gdkintl.h"
53 #include "gdkpipeiostreamprivate.h"
54 #include "gdksurface.h"
55 
56 typedef struct _GdkDropPrivate GdkDropPrivate;
57 
58 struct _GdkDropPrivate {
59   GdkDevice *device;
60   GdkDrag *drag;
61   GdkContentFormats *formats;
62   GdkSurface *surface;
63   GdkDragAction actions;
64 
65   guint entered : 1;            /* TRUE if we got an enter event but not a leave event yet */
66   enum {
67     GDK_DROP_STATE_NONE,        /* pointer is dragging along */
68     GDK_DROP_STATE_DROPPING,    /* DROP_START has been sent */
69     GDK_DROP_STATE_FINISHED     /* gdk_drop_finish() has been called */
70   } state : 2;
71 };
72 
73 enum {
74   PROP_0,
75   PROP_ACTIONS,
76   PROP_DEVICE,
77   PROP_DISPLAY,
78   PROP_DRAG,
79   PROP_FORMATS,
80   PROP_SURFACE,
81   N_PROPERTIES
82 };
83 
84 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
85 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(GdkDrop,gdk_drop,G_TYPE_OBJECT)86 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkDrop, gdk_drop, G_TYPE_OBJECT)
87 
88 static void
89 gdk_drop_default_status (GdkDrop       *self,
90                          GdkDragAction  actions,
91                          GdkDragAction  preferred)
92 {
93 }
94 
95 static void
gdk_drop_read_local_write_done(GObject * drag,GAsyncResult * result,gpointer stream)96 gdk_drop_read_local_write_done (GObject      *drag,
97                                 GAsyncResult *result,
98                                 gpointer      stream)
99 {
100   /* we don't care about the error, we just want to clean up */
101   gdk_drag_write_finish (GDK_DRAG (drag), result, NULL);
102 
103   /* XXX: Do we need to close_async() here? */
104   g_output_stream_close (stream, NULL, NULL);
105 
106   g_object_unref (stream);
107 }
108 
109 static void
gdk_drop_read_local_async(GdkDrop * self,GdkContentFormats * formats,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)110 gdk_drop_read_local_async (GdkDrop             *self,
111                            GdkContentFormats   *formats,
112                            int                  io_priority,
113                            GCancellable        *cancellable,
114                            GAsyncReadyCallback  callback,
115                            gpointer             user_data)
116 {
117   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
118   GdkContentFormats *content_formats;
119   const char *mime_type;
120   GTask *task;
121   GdkContentProvider *content;
122 
123   task = g_task_new (self, cancellable, callback, user_data);
124   g_task_set_priority (task, io_priority);
125   g_task_set_source_tag (task, gdk_drop_read_local_async);
126 
127   if (priv->drag == NULL)
128     {
129       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
130                                      _("Drag’n’drop from other applications is not supported."));
131       g_object_unref (task);
132       return;
133     }
134 
135   g_object_get (priv->drag, "content", &content, NULL);
136   content_formats = gdk_content_provider_ref_formats (content);
137   g_object_unref (content);
138   content_formats = gdk_content_formats_union_serialize_mime_types (content_formats);
139   mime_type = gdk_content_formats_match_mime_type (content_formats, formats);
140 
141   if (mime_type != NULL)
142     {
143       GOutputStream *output_stream;
144       GIOStream *stream;
145 
146       stream = gdk_pipe_io_stream_new ();
147       output_stream = g_io_stream_get_output_stream (stream);
148       gdk_drag_write_async (priv->drag,
149                                     mime_type,
150                                     output_stream,
151                                     io_priority,
152                                     cancellable,
153                                     gdk_drop_read_local_write_done,
154                                     g_object_ref (output_stream));
155       g_task_set_task_data (task, (gpointer) mime_type, NULL);
156       g_task_return_pointer (task, g_object_ref (g_io_stream_get_input_stream (stream)), g_object_unref);
157 
158       g_object_unref (stream);
159     }
160   else
161     {
162       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
163                                      _("No compatible formats to transfer contents."));
164     }
165 
166   gdk_content_formats_unref (content_formats);
167   g_object_unref (task);
168 }
169 
170 static GInputStream *
gdk_drop_read_local_finish(GdkDrop * self,GAsyncResult * result,const char ** out_mime_type,GError ** error)171 gdk_drop_read_local_finish (GdkDrop         *self,
172                             GAsyncResult    *result,
173                             const char     **out_mime_type,
174                             GError         **error)
175 {
176   g_return_val_if_fail (g_task_is_valid (result, self), NULL);
177   g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_drop_read_local_async, NULL);
178 
179   if (out_mime_type)
180     *out_mime_type = g_task_get_task_data (G_TASK (result));
181 
182   return g_task_propagate_pointer (G_TASK (result), error);
183 }
184 
185 static void
gdk_drop_add_formats(GdkDrop * self,GdkContentFormats * formats)186 gdk_drop_add_formats (GdkDrop           *self,
187                       GdkContentFormats *formats)
188 {
189   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
190 
191   formats = gdk_content_formats_union_deserialize_gtypes (gdk_content_formats_ref (formats));
192 
193   if (priv->formats)
194     {
195       formats = gdk_content_formats_union (formats, priv->formats);
196       gdk_content_formats_unref (priv->formats);
197     }
198 
199   priv->formats = formats;
200 }
201 
202 static void
gdk_drop_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)203 gdk_drop_set_property (GObject      *gobject,
204                        guint         prop_id,
205                        const GValue *value,
206                        GParamSpec   *pspec)
207 {
208   GdkDrop *self = GDK_DROP (gobject);
209   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
210 
211   switch (prop_id)
212     {
213     case PROP_ACTIONS:
214       gdk_drop_set_actions (self, g_value_get_flags (value));
215       break;
216 
217     case PROP_DEVICE:
218       priv->device = g_value_dup_object (value);
219       g_assert (priv->device != NULL);
220       if (priv->surface)
221         g_assert (gdk_surface_get_display (priv->surface) == gdk_device_get_display (priv->device));
222       break;
223 
224     case PROP_DRAG:
225       priv->drag = g_value_dup_object (value);
226       if (priv->drag)
227         gdk_drop_add_formats (self, gdk_drag_get_formats (priv->drag));
228       break;
229 
230     case PROP_FORMATS:
231       gdk_drop_add_formats (self, g_value_get_boxed (value));
232       g_assert (priv->formats != NULL);
233       break;
234 
235     case PROP_SURFACE:
236       priv->surface = g_value_dup_object (value);
237       g_assert (priv->surface != NULL);
238       if (priv->device)
239         g_assert (gdk_surface_get_display (priv->surface) == gdk_device_get_display (priv->device));
240       break;
241 
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
244       break;
245     }
246 }
247 
248 static void
gdk_drop_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)249 gdk_drop_get_property (GObject    *gobject,
250                        guint       prop_id,
251                        GValue     *value,
252                        GParamSpec *pspec)
253 {
254   GdkDrop *self = GDK_DROP (gobject);
255   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
256 
257   switch (prop_id)
258     {
259     case PROP_ACTIONS:
260       g_value_set_flags (value, priv->actions);
261       break;
262 
263     case PROP_DEVICE:
264       g_value_set_object (value, priv->device);
265       break;
266 
267     case PROP_DISPLAY:
268       g_value_set_object (value, gdk_device_get_display (priv->device));
269       break;
270 
271     case PROP_DRAG:
272       g_value_set_object (value, priv->drag);
273       break;
274 
275     case PROP_FORMATS:
276       g_value_set_boxed (value, priv->formats);
277       break;
278 
279     case PROP_SURFACE:
280       g_value_set_object (value, priv->surface);
281       break;
282 
283     default:
284       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
285       break;
286     }
287 }
288 
289 static void
gdk_drop_finalize(GObject * object)290 gdk_drop_finalize (GObject *object)
291 {
292   GdkDrop *self = GDK_DROP (object);
293   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
294 
295   /* someone forgot to send a LEAVE signal */
296   g_warn_if_fail (!priv->entered);
297 
298   /* Should we emit finish() here if necessary?
299    * For now that's the backends' job
300    */
301   g_warn_if_fail (priv->state != GDK_DROP_STATE_DROPPING);
302 
303   g_clear_object (&priv->device);
304   g_clear_object (&priv->drag);
305   g_clear_object (&priv->surface);
306   g_clear_pointer (&priv->formats, gdk_content_formats_unref);
307 
308   G_OBJECT_CLASS (gdk_drop_parent_class)->finalize (object);
309 }
310 
311 static void
gdk_drop_class_init(GdkDropClass * klass)312 gdk_drop_class_init (GdkDropClass *klass)
313 {
314   GObjectClass *object_class = G_OBJECT_CLASS (klass);
315 
316   klass->status = gdk_drop_default_status;
317 
318   object_class->get_property = gdk_drop_get_property;
319   object_class->set_property = gdk_drop_set_property;
320   object_class->finalize = gdk_drop_finalize;
321 
322   /**
323    * GdkDrop:actions: (attributes org.gtk.Property.get=gdk_drop_get_actions)
324    *
325    * The possible actions for this drop
326    */
327   properties[PROP_ACTIONS] =
328     g_param_spec_flags ("actions",
329                         "Actions",
330                         "The possible actions for this drop",
331                          GDK_TYPE_DRAG_ACTION,
332                          GDK_ACTION_ALL,
333                          G_PARAM_READWRITE |
334                          G_PARAM_CONSTRUCT_ONLY |
335                          G_PARAM_STATIC_STRINGS |
336                          G_PARAM_EXPLICIT_NOTIFY);
337 
338   /**
339    * GdkDrop:device: (attributes org.gtk.Property.get=gdk_drop_get_device)
340    *
341    * The `GdkDevice` performing the drop
342    */
343   properties[PROP_DEVICE] =
344     g_param_spec_object ("device",
345                          "Device",
346                          "The device performing the drop",
347                          GDK_TYPE_DEVICE,
348                          G_PARAM_READWRITE |
349                          G_PARAM_CONSTRUCT_ONLY |
350                          G_PARAM_STATIC_STRINGS |
351                          G_PARAM_EXPLICIT_NOTIFY);
352 
353   /**
354    * GdkDrop:display: (attributes org.gtk.Property.get=gdk_drop_get_display)
355    *
356    * The `GdkDisplay` that the drop belongs to.
357    */
358   properties[PROP_DISPLAY] =
359     g_param_spec_object ("display",
360                          "Display",
361                          "Display this drag belongs to",
362                          GDK_TYPE_DISPLAY,
363                          G_PARAM_READABLE |
364                          G_PARAM_STATIC_STRINGS |
365                          G_PARAM_EXPLICIT_NOTIFY);
366 
367   /**
368    * GdkDrop:drag: (attributes org.gtk.Property.get=gdk_drop_get_drag)
369    *
370    * The `GdkDrag` that initiated this drop
371    */
372   properties[PROP_DRAG] =
373     g_param_spec_object ("drag",
374                          "Drag",
375                          "The drag that initiated this drop",
376                          GDK_TYPE_DRAG,
377                          G_PARAM_READWRITE |
378                          G_PARAM_CONSTRUCT_ONLY |
379                          G_PARAM_STATIC_STRINGS |
380                          G_PARAM_EXPLICIT_NOTIFY);
381 
382   /**
383    * GdkDrop:formats: (attributes org.gtk.Property.get=gdk_drop_get_formats)
384    *
385    * The possible formats that the drop can provide its data in.
386    */
387   properties[PROP_FORMATS] =
388     g_param_spec_boxed ("formats",
389                         "Formats",
390                         "The possible formats for data",
391                         GDK_TYPE_CONTENT_FORMATS,
392                         G_PARAM_READWRITE |
393                         G_PARAM_CONSTRUCT_ONLY |
394                         G_PARAM_STATIC_STRINGS |
395                         G_PARAM_EXPLICIT_NOTIFY);
396 
397   /**
398    * GdkDrop:surface: (attributes org.gtk.Property.get=gdk_drop_get_surface)
399    *
400    * The `GdkSurface` the drop happens on
401    */
402   properties[PROP_SURFACE] =
403     g_param_spec_object ("surface",
404                          "Surface",
405                          "The surface the drop is happening on",
406                          GDK_TYPE_SURFACE,
407                          G_PARAM_READWRITE |
408                          G_PARAM_CONSTRUCT_ONLY |
409                          G_PARAM_STATIC_STRINGS |
410                          G_PARAM_EXPLICIT_NOTIFY);
411 
412   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
413 }
414 
415 static void
gdk_drop_init(GdkDrop * self)416 gdk_drop_init (GdkDrop *self)
417 {
418 }
419 
420 /**
421  * gdk_drop_get_display: (attributes org.gtk.Method.get_property=display)
422  * @self: a `GdkDrop`
423  *
424  * Gets the `GdkDisplay` that @self was created for.
425  *
426  * Returns: (transfer none): a `GdkDisplay`
427  */
428 GdkDisplay *
gdk_drop_get_display(GdkDrop * self)429 gdk_drop_get_display (GdkDrop *self)
430 {
431   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
432 
433   g_return_val_if_fail (GDK_IS_DROP (self), NULL);
434 
435   return gdk_device_get_display (priv->device);
436 }
437 
438 /**
439  * gdk_drop_get_device: (attributes org.gtk.Method.get_property=device)
440  * @self: a `GdkDrop`
441  *
442  * Returns the `GdkDevice` performing the drop.
443  *
444  * Returns: (transfer none): The `GdkDevice` performing the drop.
445  */
446 GdkDevice *
gdk_drop_get_device(GdkDrop * self)447 gdk_drop_get_device (GdkDrop *self)
448 {
449   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
450 
451   g_return_val_if_fail (GDK_IS_DROP (self), NULL);
452 
453   return priv->device;
454 }
455 
456 /**
457  * gdk_drop_get_formats: (attributes org.gtk.Method.get_property=formats)
458  * @self: a `GdkDrop`
459  *
460  * Returns the `GdkContentFormats` that the drop offers the data
461  * to be read in.
462  *
463  * Returns: (transfer none): The possible `GdkContentFormats`
464  */
465 GdkContentFormats *
gdk_drop_get_formats(GdkDrop * self)466 gdk_drop_get_formats (GdkDrop *self)
467 {
468   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
469 
470   g_return_val_if_fail (GDK_IS_DROP (self), NULL);
471 
472   return priv->formats;
473 }
474 
475 /**
476  * gdk_drop_get_surface: (attributes org.gtk.Method.get_property=surface)
477  * @self: a `GdkDrop`
478  *
479  * Returns the `GdkSurface` performing the drop.
480  *
481  * Returns: (transfer none): The `GdkSurface` performing the drop.
482  */
483 GdkSurface *
gdk_drop_get_surface(GdkDrop * self)484 gdk_drop_get_surface (GdkDrop *self)
485 {
486   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
487 
488   g_return_val_if_fail (GDK_IS_DROP (self), NULL);
489 
490   return priv->surface;
491 }
492 
493 /**
494  * gdk_drop_get_actions: (attributes org.gtk.Method.get_property=actions)
495  * @self: a `GdkDrop`
496  *
497  * Returns the possible actions for this `GdkDrop`.
498  *
499  * If this value contains multiple actions - i.e.
500  * [func@Gdk.DragAction.is_unique] returns %FALSE for the result -
501  * [method@Gdk.Drop.finish] must choose the action to use when
502  * accepting the drop. This will only happen if you passed
503  * %GDK_ACTION_ASK as one of the possible actions in
504  * [method@Gdk.Drop.status]. %GDK_ACTION_ASK itself will not
505  * be included in the actions returned by this function.
506  *
507  * This value may change over the lifetime of the [class@Gdk.Drop]
508  * both as a response to source side actions as well as to calls to
509  * [method@Gdk.Drop.status] or [method@Gdk.Drop.finish]. The source
510  * side will not change this value anymore once a drop has started.
511  *
512  * Returns: The possible `GdkDragActions`
513  */
514 GdkDragAction
gdk_drop_get_actions(GdkDrop * self)515 gdk_drop_get_actions (GdkDrop *self)
516 {
517   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
518 
519   g_return_val_if_fail (GDK_IS_DROP (self), 0);
520 
521   return priv->actions;
522 }
523 
524 void
gdk_drop_set_actions(GdkDrop * self,GdkDragAction actions)525 gdk_drop_set_actions (GdkDrop       *self,
526                       GdkDragAction  actions)
527 {
528   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
529 
530   g_return_if_fail (GDK_IS_DROP (self));
531   g_return_if_fail (priv->state == GDK_DROP_STATE_NONE);
532   g_return_if_fail ((actions & GDK_ACTION_ASK) == 0);
533 
534   if (priv->actions == actions)
535     return;
536 
537   priv->actions = actions;
538 
539   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
540 }
541 
542 /**
543  * gdk_drop_get_drag: (attributes org.gtk.Method.get_property=drag)
544  * @self: a `GdkDrop`
545  *
546  * If this is an in-app drag-and-drop operation, returns the `GdkDrag`
547  * that corresponds to this drop.
548  *
549  * If it is not, %NULL is returned.
550  *
551  * Returns: (transfer none) (nullable): the corresponding `GdkDrag`
552  */
553 GdkDrag *
gdk_drop_get_drag(GdkDrop * self)554 gdk_drop_get_drag (GdkDrop *self)
555 {
556   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
557 
558   g_return_val_if_fail (GDK_IS_DROP (self), 0);
559 
560   return priv->drag;
561 }
562 
563 /**
564  * gdk_drop_status:
565  * @self: a `GdkDrop`
566  * @actions: Supported actions of the destination, or 0 to indicate
567  *    that a drop will not be accepted
568  * @preferred: A unique action that's a member of @actions indicating the
569  *    preferred action
570  *
571  * Selects all actions that are potentially supported by the destination.
572  *
573  * When calling this function, do not restrict the passed in actions to
574  * the ones provided by [method@Gdk.Drop.get_actions]. Those actions may
575  * change in the future, even depending on the actions you provide here.
576  *
577  * The @preferred action is a hint to the drag-and-drop mechanism about which
578  * action to use when multiple actions are possible.
579  *
580  * This function should be called by drag destinations in response to
581  * %GDK_DRAG_ENTER or %GDK_DRAG_MOTION events. If the destination does
582  * not yet know the exact actions it supports, it should set any possible
583  * actions first and then later call this function again.
584  */
585 void
gdk_drop_status(GdkDrop * self,GdkDragAction actions,GdkDragAction preferred)586 gdk_drop_status (GdkDrop       *self,
587                  GdkDragAction  actions,
588                  GdkDragAction  preferred)
589 {
590 #ifndef G_DISABLE_CHECKS
591   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
592 #endif
593 
594   g_return_if_fail (GDK_IS_DROP (self));
595   g_return_if_fail (priv->state != GDK_DROP_STATE_FINISHED);
596   g_return_if_fail (gdk_drag_action_is_unique (preferred));
597   g_return_if_fail ((preferred & actions) == preferred);
598 
599   GDK_DROP_GET_CLASS (self)->status (self, actions, preferred);
600 }
601 
602 /**
603  * gdk_drop_finish:
604  * @self: a `GdkDrop`
605  * @action: the action performed by the destination or 0 if the drop failed
606  *
607  * Ends the drag operation after a drop.
608  *
609  * The @action must be a single action selected from the actions
610  * available via [method@Gdk.Drop.get_actions].
611  */
612 void
gdk_drop_finish(GdkDrop * self,GdkDragAction action)613 gdk_drop_finish (GdkDrop       *self,
614                  GdkDragAction  action)
615 {
616   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
617 
618   g_return_if_fail (GDK_IS_DROP (self));
619   g_return_if_fail (priv->state == GDK_DROP_STATE_DROPPING);
620   g_return_if_fail (gdk_drag_action_is_unique (action));
621 
622   GDK_DROP_GET_CLASS (self)->finish (self, action);
623 
624   priv->state = GDK_DROP_STATE_FINISHED;
625 }
626 
627 static void
gdk_drop_read_internal(GdkDrop * self,GdkContentFormats * formats,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)628 gdk_drop_read_internal (GdkDrop             *self,
629                         GdkContentFormats   *formats,
630                         int                  io_priority,
631                         GCancellable        *cancellable,
632                         GAsyncReadyCallback  callback,
633                         gpointer             user_data)
634 {
635   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
636 
637   g_return_if_fail (priv->state != GDK_DROP_STATE_FINISHED);
638 
639   if (priv->drag)
640     {
641       gdk_drop_read_local_async (self,
642                                  formats,
643                                  io_priority,
644                                  cancellable,
645                                  callback,
646                                  user_data);
647     }
648   else
649     {
650       GDK_DROP_GET_CLASS (self)->read_async (self,
651                                              formats,
652                                              io_priority,
653                                              cancellable,
654                                              callback,
655                                              user_data);
656     }
657 }
658 
659 /**
660  * gdk_drop_read_async:
661  * @self: a `GdkDrop`
662  * @mime_types: (array zero-terminated=1) (element-type utf8):
663  *   pointer to an array of mime types
664  * @io_priority: the I/O priority for the read operation
665  * @cancellable: (nullable): optional `GCancellable` object
666  * @callback: (scope async): a `GAsyncReadyCallback` to call when
667  *   the request is satisfied
668  * @user_data: (closure): the data to pass to @callback
669  *
670  * Asynchronously read the dropped data from a `GdkDrop`
671  * in a format that complies with one of the mime types.
672  */
673 void
gdk_drop_read_async(GdkDrop * self,const char ** mime_types,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)674 gdk_drop_read_async (GdkDrop             *self,
675                      const char         **mime_types,
676                      int                  io_priority,
677                      GCancellable        *cancellable,
678                      GAsyncReadyCallback  callback,
679                      gpointer             user_data)
680 {
681   GdkContentFormats *formats;
682 
683   g_return_if_fail (GDK_IS_DROP (self));
684   g_return_if_fail (mime_types != NULL && mime_types[0] != NULL);
685   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
686   g_return_if_fail (callback != NULL);
687 
688   formats = gdk_content_formats_new (mime_types, g_strv_length ((char **) mime_types));
689 
690   gdk_drop_read_internal (self, formats, io_priority, cancellable, callback, user_data);
691 
692   gdk_content_formats_unref (formats);
693 }
694 
695 /**
696  * gdk_drop_read_finish:
697  * @self: a `GdkDrop`
698  * @result: a `GAsyncResult`
699  * @out_mime_type: (out) (type utf8): return location for the used mime type
700  * @error: (nullable): location to store error information on failure
701  *
702  * Finishes an async drop read operation.
703  *
704  * Note that you must not use blocking read calls on the returned stream
705  * in the GTK thread, since some platforms might require communication with
706  * GTK to complete the data transfer. You can use async APIs such as
707  * g_input_stream_read_bytes_async().
708  *
709  * See [method@Gdk.Drop.read_async].
710  *
711  * Returns: (nullable) (transfer full): the `GInputStream`
712  */
713 GInputStream *
gdk_drop_read_finish(GdkDrop * self,GAsyncResult * result,const char ** out_mime_type,GError ** error)714 gdk_drop_read_finish (GdkDrop       *self,
715                       GAsyncResult  *result,
716                       const char   **out_mime_type,
717                       GError       **error)
718 {
719   g_return_val_if_fail (GDK_IS_DROP (self), NULL);
720   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
721 
722   if (g_async_result_is_tagged (result, gdk_drop_read_local_async))
723     {
724       return gdk_drop_read_local_finish (self, result, out_mime_type, error);
725     }
726   else
727     {
728       return GDK_DROP_GET_CLASS (self)->read_finish (self, result, out_mime_type, error);
729     }
730 }
731 
732 static void
gdk_drop_read_value_done(GObject * source,GAsyncResult * result,gpointer data)733 gdk_drop_read_value_done (GObject      *source,
734                           GAsyncResult *result,
735                           gpointer      data)
736 {
737   GTask *task = data;
738   GError *error = NULL;
739   GValue *value;
740 
741   value = g_task_get_task_data (task);
742 
743   if (!gdk_content_deserialize_finish (result, value, &error))
744     g_task_return_error (task, error);
745   else
746     g_task_return_pointer (task, value, NULL);
747 
748   g_object_unref (task);
749 }
750 
751 static void
gdk_drop_read_value_got_stream(GObject * source,GAsyncResult * result,gpointer data)752 gdk_drop_read_value_got_stream (GObject      *source,
753                                 GAsyncResult *result,
754                                 gpointer      data)
755 {
756   GInputStream *stream;
757   GError *error = NULL;
758   GTask *task = data;
759   const char *mime_type;
760 
761   stream = gdk_drop_read_finish (GDK_DROP (source), result, &mime_type, &error);
762   if (stream == NULL)
763     {
764       g_task_return_error (task, error);
765       return;
766     }
767 
768   gdk_content_deserialize_async (stream,
769                                  mime_type,
770                                  G_VALUE_TYPE (g_task_get_task_data (task)),
771                                  g_task_get_priority (task),
772                                  g_task_get_cancellable (task),
773                                  gdk_drop_read_value_done,
774                                  task);
775   g_object_unref (stream);
776 }
777 
778 static void
free_value(gpointer value)779 free_value (gpointer value)
780 {
781   g_value_unset (value);
782   g_slice_free (GValue, value);
783 }
784 
785 static void
gdk_drop_read_value_internal(GdkDrop * self,GType type,gpointer source_tag,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)786 gdk_drop_read_value_internal (GdkDrop             *self,
787                               GType                type,
788                               gpointer             source_tag,
789                               int                  io_priority,
790                               GCancellable        *cancellable,
791                               GAsyncReadyCallback  callback,
792                               gpointer             user_data)
793 {
794   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
795   GdkContentFormatsBuilder *builder;
796   GdkContentFormats *formats;
797   GValue *value;
798   GTask *task;
799 
800   g_return_if_fail (priv->state != GDK_DROP_STATE_FINISHED);
801 
802   task = g_task_new (self, cancellable, callback, user_data);
803   g_task_set_priority (task, io_priority);
804   g_task_set_source_tag (task, source_tag);
805   value = g_slice_new0 (GValue);
806   g_value_init (value, type);
807   g_task_set_task_data (task, value, free_value);
808 
809   if (priv->drag)
810     {
811       GError *error = NULL;
812       gboolean res;
813 
814       res = gdk_content_provider_get_value (gdk_drag_get_content (priv->drag),
815                                             value,
816                                             &error);
817 
818       if (res)
819         {
820           g_task_return_pointer (task, value, NULL);
821           g_object_unref (task);
822           return;
823         }
824       else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
825         {
826           g_task_return_error (task, error);
827           g_object_unref (task);
828           return;
829         }
830       else
831         {
832           /* fall through to regular stream transfer */
833           g_clear_error (&error);
834         }
835     }
836 
837   builder = gdk_content_formats_builder_new ();
838   gdk_content_formats_builder_add_gtype (builder, type);
839   formats = gdk_content_formats_builder_free_to_formats (builder);
840   formats = gdk_content_formats_union_deserialize_mime_types (formats);
841 
842   gdk_drop_read_internal (self,
843                           formats,
844                           io_priority,
845                           cancellable,
846                           gdk_drop_read_value_got_stream,
847                           task);
848 
849   gdk_content_formats_unref (formats);
850 }
851 
852 /**
853  * gdk_drop_read_value_async:
854  * @self: a `GdkDrop`
855  * @type: a `GType` to read
856  * @io_priority: the I/O priority of the request.
857  * @cancellable: (nullable): optional `GCancellable` object, %NULL to ignore.
858  * @callback: (scope async): callback to call when the request is satisfied
859  * @user_data: (closure): the data to pass to callback function
860  *
861  * Asynchronously request the drag operation's contents converted
862  * to the given @type.
863  *
864  * When the operation is finished @callback will be called. You must
865  * then call [method@Gdk.Drop.read_value_finish] to get the resulting
866  * `GValue`.
867  *
868  * For local drag-and-drop operations that are available in the given
869  * `GType`, the value will be copied directly. Otherwise, GDK will
870  * try to use [func@Gdk.content_deserialize_async] to convert the data.
871  */
872 void
gdk_drop_read_value_async(GdkDrop * self,GType type,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)873 gdk_drop_read_value_async (GdkDrop             *self,
874                            GType                type,
875                            int                  io_priority,
876                            GCancellable        *cancellable,
877                            GAsyncReadyCallback  callback,
878                            gpointer             user_data)
879 {
880   g_return_if_fail (GDK_IS_DROP (self));
881   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
882   g_return_if_fail (callback != NULL);
883 
884   gdk_drop_read_value_internal (self,
885                                 type,
886                                 gdk_drop_read_value_async,
887                                 io_priority,
888                                 cancellable,
889                                 callback,
890                                 user_data);
891 }
892 
893 /**
894  * gdk_drop_read_value_finish:
895  * @self: a `GdkDrop`
896  * @result: a `GAsyncResult`
897  * @error: a `GError` location to store the error occurring
898  *
899  * Finishes an async drop read.
900  *
901  * See [method@Gdk.Drop.read_value_async].
902  *
903  * Returns: (transfer none): a `GValue` containing the result.
904  */
905 const GValue *
gdk_drop_read_value_finish(GdkDrop * self,GAsyncResult * result,GError ** error)906 gdk_drop_read_value_finish (GdkDrop       *self,
907                             GAsyncResult  *result,
908                             GError       **error)
909 {
910   g_return_val_if_fail (g_task_is_valid (result, self), NULL);
911   g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_drop_read_value_async, NULL);
912   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
913 
914   return g_task_propagate_pointer (G_TASK (result), error);
915 }
916 
917 static void
gdk_drop_do_emit_event(GdkEvent * event,gboolean dont_queue)918 gdk_drop_do_emit_event (GdkEvent *event,
919                         gboolean  dont_queue)
920 {
921   if (dont_queue)
922     {
923       _gdk_event_emit (event);
924       gdk_event_unref (event);
925     }
926   else
927     {
928       _gdk_event_queue_append (gdk_event_get_display (event), event);
929     }
930 }
931 void
gdk_drop_emit_enter_event(GdkDrop * self,gboolean dont_queue,double x,double y,guint32 time)932 gdk_drop_emit_enter_event (GdkDrop  *self,
933                            gboolean  dont_queue,
934                            double    x,
935                            double    y,
936                            guint32   time)
937 {
938   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
939   GdkEvent *event;
940 
941   g_warn_if_fail (!priv->entered);
942 
943   event = gdk_dnd_event_new (GDK_DRAG_ENTER,
944                              priv->surface,
945                              priv->device,
946                              self,
947                              time,
948                              0, 0);
949 
950   priv->entered = TRUE;
951 
952   gdk_drop_do_emit_event (event, dont_queue);
953 }
954 
955 void
gdk_drop_emit_motion_event(GdkDrop * self,gboolean dont_queue,double x,double y,guint32 time)956 gdk_drop_emit_motion_event (GdkDrop  *self,
957                             gboolean  dont_queue,
958                             double    x,
959                             double    y,
960                             guint32   time)
961 {
962   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
963   GdkEvent *event;
964 
965   g_warn_if_fail (priv->entered);
966 
967   event = gdk_dnd_event_new (GDK_DRAG_MOTION,
968                              priv->surface,
969                              priv->device,
970                              self,
971                              time,
972                              x, y);
973 
974   gdk_drop_do_emit_event (event, dont_queue);
975 }
976 
977 void
gdk_drop_emit_leave_event(GdkDrop * self,gboolean dont_queue,guint32 time)978 gdk_drop_emit_leave_event (GdkDrop  *self,
979                            gboolean  dont_queue,
980                            guint32   time)
981 {
982   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
983   GdkEvent *event;
984 
985   g_warn_if_fail (priv->entered);
986 
987   event = gdk_dnd_event_new (GDK_DRAG_LEAVE,
988                              priv->surface,
989                              priv->device,
990                              self,
991                              time,
992                              0, 0);
993 
994   priv->entered = FALSE;
995 
996   gdk_drop_do_emit_event (event, dont_queue);
997 }
998 
999 void
gdk_drop_emit_drop_event(GdkDrop * self,gboolean dont_queue,double x,double y,guint32 time)1000 gdk_drop_emit_drop_event (GdkDrop  *self,
1001                           gboolean  dont_queue,
1002                           double    x,
1003                           double    y,
1004                           guint32   time)
1005 {
1006   GdkDropPrivate *priv = gdk_drop_get_instance_private (self);
1007   GdkEvent *event;
1008 
1009   g_warn_if_fail (priv->entered);
1010   g_warn_if_fail (priv->state == GDK_DROP_STATE_NONE);
1011 
1012   event = gdk_dnd_event_new (GDK_DROP_START,
1013                              priv->surface,
1014                              priv->device,
1015                              self,
1016                              time,
1017                              x, y);
1018 
1019   priv->state = GDK_DROP_STATE_DROPPING;
1020 
1021   gdk_drop_do_emit_event (event, dont_queue);
1022 }
1023