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 "gdkdragprivate.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_DRAG (gdk_wayland_drag_get_type ())
36#define GDK_WAYLAND_DRAG(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_DRAG, GdkWaylandDrag))
37#define GDK_WAYLAND_DRAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_DRAG, GdkWaylandDragClass))
38#define GDK_IS_WAYLAND_DRAG(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_DRAG))
39#define GDK_IS_WAYLAND_DRAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_DRAG))
40#define GDK_WAYLAND_DRAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_DRAG, GdkWaylandDragClass))
41
42typedef struct _GdkWaylandDrag GdkWaylandDrag;
43typedef struct _GdkWaylandDragClass GdkWaylandDragClass;
44
45struct _GdkWaylandDrag
46{
47 GdkDrag drag;
48 GdkSurface *dnd_surface;
49 struct wl_surface *dnd_wl_surface;
50 struct wl_data_source *data_source;
51 struct wl_data_offer *offer;
52 uint32_t serial;
53 int hot_x;
54 int hot_y;
55};
56
57struct _GdkWaylandDragClass
58{
59 GdkDragClass parent_class;
60};
61
62static GList *drags;
63
64GType gdk_wayland_drag_get_type (void);
65
66G_DEFINE_TYPE (GdkWaylandDrag, gdk_wayland_drag, GDK_TYPE_DRAG)
67
68static void
69gdk_wayland_drag_finalize (GObject *object)
70{
71 GdkWaylandDrag *wayland_drag = GDK_WAYLAND_DRAG (object);
72 GdkDrag *drag = GDK_DRAG (object);
73 GdkSurface *dnd_surface;
74
75 drags = g_list_remove (list: drags, data: drag);
76
77 gdk_drag_set_cursor (drag, NULL);
78
79 g_clear_pointer (&wayland_drag->data_source, wl_data_source_destroy);
80 g_clear_pointer (&wayland_drag->offer, wl_data_offer_destroy);
81
82 dnd_surface = wayland_drag->dnd_surface;
83
84 G_OBJECT_CLASS (gdk_wayland_drag_parent_class)->finalize (object);
85
86 if (dnd_surface)
87 gdk_surface_destroy (surface: dnd_surface);
88}
89
90static inline uint32_t
91gdk_to_wl_actions (GdkDragAction action)
92{
93 uint32_t dnd_actions = 0;
94
95 if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK))
96 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
97 if (action & GDK_ACTION_MOVE)
98 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
99 if (action & GDK_ACTION_ASK)
100 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
101
102 return dnd_actions;
103}
104
105static void
106gdk_wayland_drag_init (GdkWaylandDrag *drag_wayland)
107{
108 GdkDrag *drag;
109
110 drag = GDK_DRAG (drag_wayland);
111 drags = g_list_prepend (list: drags, data: drag);
112
113 gdk_drag_set_selected_action (drag, action: GDK_ACTION_COPY);
114}
115
116static GdkSurface *
117gdk_wayland_drag_get_drag_surface (GdkDrag *drag)
118{
119 return GDK_WAYLAND_DRAG (drag)->dnd_surface;
120}
121
122static void
123gdk_wayland_drag_set_hotspot (GdkDrag *drag,
124 int hot_x,
125 int hot_y)
126{
127 GdkWaylandDrag *drag_wayland = GDK_WAYLAND_DRAG (drag);
128 int prev_hot_x = drag_wayland->hot_x;
129 int prev_hot_y = drag_wayland->hot_y;
130 const GdkRectangle damage_rect = { .width = 1, .height = 1 };
131
132 drag_wayland->hot_x = hot_x;
133 drag_wayland->hot_y = hot_y;
134
135 if (prev_hot_x == hot_x && prev_hot_y == hot_y)
136 return;
137
138 _gdk_wayland_surface_offset_next_wl_buffer (surface: drag_wayland->dnd_surface,
139 x: prev_hot_x - hot_x, y: prev_hot_y - hot_y);
140 gdk_surface_invalidate_rect (surface: drag_wayland->dnd_surface, rect: &damage_rect);
141}
142
143static void
144gdk_wayland_drag_set_cursor (GdkDrag *drag,
145 GdkCursor *cursor)
146{
147 GdkDevice *device = gdk_drag_get_device (drag);
148
149 if (device != NULL)
150 gdk_wayland_seat_set_global_cursor (seat: gdk_device_get_seat (device), cursor);
151}
152
153static void
154gdk_wayland_drag_drop_performed (GdkDrag *drag,
155 guint32 time_)
156{
157 gdk_drag_set_cursor (drag, NULL);
158}
159
160static void
161gdk_wayland_drag_cancel (GdkDrag *drag,
162 GdkDragCancelReason reason)
163{
164 gdk_drag_set_cursor (drag, NULL);
165 gdk_drag_drop_done (drag, FALSE);
166}
167
168static void
169gdk_wayland_drag_drop_done (GdkDrag *drag,
170 gboolean success)
171{
172 GdkWaylandDrag *drag_wayland = GDK_WAYLAND_DRAG (drag);
173 GdkDevice *device = gdk_drag_get_device (drag);
174
175 gdk_wayland_seat_set_drag (seat: gdk_device_get_seat (device), NULL);
176
177 if (success)
178 {
179 if (drag_wayland->dnd_surface)
180 gdk_surface_hide (surface: drag_wayland->dnd_surface);
181 }
182}
183
184static void
185gdk_wayland_drag_class_init (GdkWaylandDragClass *klass)
186{
187 GObjectClass *object_class = G_OBJECT_CLASS (klass);
188 GdkDragClass *drag_class = GDK_DRAG_CLASS (klass);
189
190 object_class->finalize = gdk_wayland_drag_finalize;
191
192 drag_class->get_drag_surface = gdk_wayland_drag_get_drag_surface;
193 drag_class->set_hotspot = gdk_wayland_drag_set_hotspot;
194 drag_class->drop_done = gdk_wayland_drag_drop_done;
195 drag_class->set_cursor = gdk_wayland_drag_set_cursor;
196 drag_class->drop_performed = gdk_wayland_drag_drop_performed;
197 drag_class->cancel = gdk_wayland_drag_cancel;
198}
199
200static inline GdkDragAction
201_wl_to_gdk_actions (uint32_t dnd_actions)
202{
203 GdkDragAction actions = 0;
204
205 if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
206 actions |= GDK_ACTION_COPY;
207 if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
208 actions |= GDK_ACTION_MOVE;
209 if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
210 actions |= GDK_ACTION_ASK;
211
212 return actions;
213}
214
215static void
216data_source_target (void *data,
217 struct wl_data_source *source,
218 const char *mime_type)
219{
220 GDK_NOTE (EVENTS,
221 g_message ("data source target, source = %p, mime_type = %s",
222 source, mime_type));
223}
224
225static void
226gdk_wayland_drag_write_done (GObject *drag,
227 GAsyncResult *result,
228 gpointer user_data)
229{
230 GError *error = NULL;
231
232 if (!gdk_drag_write_finish (GDK_DRAG (drag), result, error: &error))
233 {
234 GDK_DISPLAY_NOTE (gdk_drag_get_display (GDK_DRAG (drag)), DND, g_message ("%p: failed to write stream: %s", drag, error->message));
235 g_error_free (error);
236 }
237}
238
239static void
240data_source_send (void *data,
241 struct wl_data_source *source,
242 const char *mime_type,
243 int32_t fd)
244{
245 GdkDrag *drag = data;
246 GOutputStream *stream;
247
248 GDK_DISPLAY_NOTE (gdk_drag_get_display (drag), DND, g_message ("%p: data source send request for %s on fd %d\n",
249 source, mime_type, fd));
250
251 //mime_type = gdk_intern_mime_type (mime_type);
252 mime_type = g_intern_string (string: mime_type);
253 stream = g_unix_output_stream_new (fd, TRUE);
254
255 gdk_drag_write_async (drag,
256 mime_type,
257 stream,
258 G_PRIORITY_DEFAULT,
259 NULL,
260 callback: gdk_wayland_drag_write_done,
261 user_data: drag);
262 g_object_unref (object: stream);
263}
264
265static void
266data_source_cancelled (void *data,
267 struct wl_data_source *source)
268{
269 GdkDrag *drag = data;
270
271 GDK_DISPLAY_NOTE (gdk_drag_get_display (drag), EVENTS,
272 g_message ("data source cancelled, source = %p", source));
273
274 gdk_drag_cancel (drag, reason: GDK_DRAG_CANCEL_ERROR);
275}
276
277static void
278data_source_dnd_drop_performed (void *data,
279 struct wl_data_source *source)
280{
281 GdkDrag *drag = data;
282
283 g_signal_emit_by_name (instance: drag, detailed_signal: "drop-performed");
284}
285
286static void
287data_source_dnd_finished (void *data,
288 struct wl_data_source *source)
289{
290 GdkDrag *drag = data;
291
292 g_object_ref (drag);
293 g_signal_emit_by_name (instance: drag, detailed_signal: "dnd-finished");
294 gdk_drag_drop_done (drag, TRUE);
295 g_object_unref (object: drag);
296}
297
298static void
299data_source_action (void *data,
300 struct wl_data_source *source,
301 uint32_t action)
302{
303 GdkDrag *drag = data;
304
305 GDK_DISPLAY_NOTE (gdk_drag_get_display (drag), EVENTS,
306 g_message ("data source action, source = %p action=%x",
307 source, action));
308
309 gdk_drag_set_selected_action (drag, action: _wl_to_gdk_actions (dnd_actions: action));
310}
311
312static const struct wl_data_source_listener data_source_listener = {
313 data_source_target,
314 data_source_send,
315 data_source_cancelled,
316 data_source_dnd_drop_performed,
317 data_source_dnd_finished,
318 data_source_action,
319};
320
321static void
322gdk_wayland_drag_create_data_source (GdkDrag *drag)
323{
324 GdkWaylandDrag *drag_wayland = GDK_WAYLAND_DRAG (drag);
325 GdkDisplay *display = gdk_drag_get_display (drag);
326 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
327 const char *const *mimetypes;
328 GdkContentFormats *formats;
329 gsize i, n_mimetypes;
330
331 drag_wayland->data_source = wl_data_device_manager_create_data_source (wl_data_device_manager: display_wayland->data_device_manager);
332 wl_data_source_add_listener (wl_data_source: drag_wayland->data_source,
333 listener: &data_source_listener,
334 data: drag);
335
336 formats = gdk_content_formats_ref (formats: gdk_drag_get_formats (drag));
337 formats = gdk_content_formats_union_serialize_mime_types (formats);
338
339 mimetypes = gdk_content_formats_get_mime_types (formats, n_mime_types: &n_mimetypes);
340
341 GDK_DISPLAY_NOTE (gdk_drag_get_display (drag), EVENTS,
342 {char *s = g_strjoinv (" ", (char **)mimetypes);
343 g_message ("create data source, mime types=%s", s);
344 g_free (s);});
345
346 wl_data_source_offer (wl_data_source: drag_wayland->data_source, GDK_WAYLAND_LOCAL_DND_MIME_TYPE);
347 for (i = 0; i < n_mimetypes; i++)
348 wl_data_source_offer (wl_data_source: drag_wayland->data_source, mime_type: mimetypes[i]);
349
350 gdk_content_formats_unref (formats);
351}
352
353GdkDrag *
354_gdk_wayland_surface_drag_begin (GdkSurface *surface,
355 GdkDevice *device,
356 GdkContentProvider *content,
357 GdkDragAction actions,
358 double dx,
359 double dy)
360{
361 GdkWaylandDrag *drag_wayland;
362 GdkDrag *drag;
363 GdkSeat *seat;
364 GdkWaylandDisplay *display_wayland;
365 GdkCursor *cursor;
366
367 display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (device));
368 seat = gdk_device_get_seat (device);
369
370 drag_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG,
371 first_property_name: "surface", surface,
372 "device", device,
373 "content", content,
374 "actions", actions,
375 NULL);
376
377 drag = GDK_DRAG (drag_wayland);
378
379 drag_wayland->dnd_surface = create_dnd_surface (display: gdk_surface_get_display (surface));
380 drag_wayland->dnd_wl_surface = gdk_wayland_surface_get_wl_surface (surface: drag_wayland->dnd_surface);
381
382 gdk_wayland_drag_create_data_source (drag);
383
384 if (display_wayland->data_device_manager_version >=
385 WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION)
386 {
387 wl_data_source_set_actions (wl_data_source: drag_wayland->data_source,
388 dnd_actions: gdk_to_wl_actions (action: actions));
389 }
390
391 gdk_wayland_seat_set_drag (seat, drag);
392
393 wl_data_device_start_drag (wl_data_device: gdk_wayland_device_get_data_device (gdk_device: device),
394 source: drag_wayland->data_source,
395 origin: gdk_wayland_surface_get_wl_surface (surface),
396 icon: drag_wayland->dnd_wl_surface,
397 serial: _gdk_wayland_display_get_serial (display_wayland));
398
399 cursor = gdk_drag_get_cursor (drag, action: gdk_drag_get_selected_action (drag));
400 gdk_drag_set_cursor (drag, cursor);
401
402 gdk_seat_ungrab (seat);
403
404 return drag;
405}
406

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