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 | |
42 | typedef struct _GdkWaylandDrop GdkWaylandDrop; |
43 | typedef struct _GdkWaylandDropClass GdkWaylandDropClass; |
44 | |
45 | struct _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 | |
55 | struct _GdkWaylandDropClass |
56 | { |
57 | GdkDropClass parent_class; |
58 | }; |
59 | |
60 | GType gdk_wayland_drop_get_type (void); |
61 | |
62 | G_DEFINE_TYPE (GdkWaylandDrop, gdk_wayland_drop, GDK_TYPE_DROP) |
63 | |
64 | static void |
65 | gdk_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 | |
74 | static inline uint32_t |
75 | gdk_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 | |
89 | static void |
90 | gdk_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 | |
122 | static void |
123 | gdk_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 | |
146 | static void |
147 | gdk_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 | |
156 | static void |
157 | gdk_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 | |
176 | static void |
177 | gdk_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 | |
221 | static GInputStream * |
222 | gdk_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 | |
239 | static void |
240 | gdk_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 | |
253 | static void |
254 | gdk_wayland_drop_init (GdkWaylandDrop *drop) |
255 | { |
256 | } |
257 | |
258 | GdkDrop * |
259 | gdk_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 | |
281 | static void |
282 | gdk_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 | |
301 | void |
302 | gdk_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 | |
312 | void |
313 | gdk_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 | |