1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 2017 Red Hat, Inc.
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 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
18#include "config.h"
19
20#include "gdkclipboardprivate.h"
21#include "gdkclipboard-wayland.h"
22
23#include "gdkcontentformats.h"
24#include "gdkintl.h"
25#include "gdkprivate-wayland.h"
26#include "gdk-private.h"
27
28#include <glib-unix.h>
29#include <gio/gunixinputstream.h>
30#include <gio/gunixoutputstream.h>
31
32typedef struct _GdkWaylandClipboardClass GdkWaylandClipboardClass;
33
34struct _GdkWaylandClipboard
35{
36 GdkClipboard parent;
37
38 struct wl_data_offer *offer;
39 GdkContentFormats *offer_formats;
40
41 struct wl_data_source *source;
42};
43
44struct _GdkWaylandClipboardClass
45{
46 GdkClipboardClass parent_class;
47};
48
49G_DEFINE_TYPE (GdkWaylandClipboard, gdk_wayland_clipboard, GDK_TYPE_CLIPBOARD)
50
51static void
52gdk_wayland_clipboard_discard_offer (GdkWaylandClipboard *cb)
53{
54 g_clear_pointer (&cb->offer_formats, gdk_content_formats_unref);
55 g_clear_pointer (&cb->offer, wl_data_offer_destroy);
56}
57
58static void
59gdk_wayland_clipboard_discard_source (GdkWaylandClipboard *cb)
60{
61 g_clear_pointer (&cb->source, wl_data_source_destroy);
62}
63
64static void
65gdk_wayland_clipboard_finalize (GObject *object)
66{
67 GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (object);
68
69 gdk_wayland_clipboard_discard_offer (cb);
70 gdk_wayland_clipboard_discard_source (cb);
71
72 G_OBJECT_CLASS (gdk_wayland_clipboard_parent_class)->finalize (object);
73}
74
75static void
76gdk_wayland_clipboard_data_source_target (void *data,
77 struct wl_data_source *source,
78 const char *mime_type)
79{
80 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (data)), CLIPBOARD, g_message ("%p: Huh? data_source.target() events?", data));
81}
82
83static void
84gdk_wayland_clipboard_write_done (GObject *clipboard,
85 GAsyncResult *result,
86 gpointer user_data)
87{
88 GError *error = NULL;
89
90 if (!gdk_clipboard_write_finish (GDK_CLIPBOARD (clipboard), result, error: &error))
91 {
92 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (clipboard)), CLIPBOARD, g_message ("%p: failed to write stream: %s", clipboard, error->message));
93 g_error_free (error);
94 }
95}
96
97static void
98gdk_wayland_clipboard_data_source_send (void *data,
99 struct wl_data_source *source,
100 const char *mime_type,
101 int32_t fd)
102{
103 GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (data);
104 GOutputStream *stream;
105
106 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (data)), CLIPBOARD, g_message ("%p: data source send request for %s on fd %d",
107 source, mime_type, fd));
108
109 mime_type = gdk_intern_mime_type (string: mime_type);
110 stream = g_unix_output_stream_new (fd, TRUE);
111
112 gdk_clipboard_write_async (GDK_CLIPBOARD (cb),
113 mime_type,
114 stream,
115 G_PRIORITY_DEFAULT,
116 NULL,
117 callback: gdk_wayland_clipboard_write_done,
118 user_data: cb);
119 g_object_unref (object: stream);
120}
121
122static void
123gdk_wayland_clipboard_data_source_cancelled (void *data,
124 struct wl_data_source *source)
125{
126 GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (data);
127
128 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (data)), CLIPBOARD, g_message ("%p: data source cancelled", data));
129
130 if (cb->source == source)
131 {
132 gdk_wayland_clipboard_discard_source (cb);
133 gdk_wayland_clipboard_claim_remote (cb, NULL, formats: gdk_content_formats_new (NULL, n_mime_types: 0));
134 }
135}
136
137static void
138gdk_wayland_clipboard_data_source_dnd_drop_performed (void *data,
139 struct wl_data_source *source)
140{
141 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (data)),
142 CLIPBOARD, g_message ("%p: Huh? data_source.dnd_drop_performed() events?", data));
143}
144
145static void
146gdk_wayland_clipboard_data_source_dnd_finished (void *data,
147 struct wl_data_source *source)
148{
149 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (data)),
150 CLIPBOARD, g_message ("%p: Huh? data_source.dnd_finished() events?", data));
151}
152
153static void
154gdk_wayland_clipboard_data_source_action (void *data,
155 struct wl_data_source *source,
156 uint32_t action)
157{
158 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (data)),
159 CLIPBOARD, g_message ("%p: Huh? data_source.action() events?", data));
160}
161
162static const struct wl_data_source_listener data_source_listener = {
163 gdk_wayland_clipboard_data_source_target,
164 gdk_wayland_clipboard_data_source_send,
165 gdk_wayland_clipboard_data_source_cancelled,
166 gdk_wayland_clipboard_data_source_dnd_drop_performed,
167 gdk_wayland_clipboard_data_source_dnd_finished,
168 gdk_wayland_clipboard_data_source_action,
169};
170
171static gboolean
172gdk_wayland_clipboard_claim (GdkClipboard *clipboard,
173 GdkContentFormats *formats,
174 gboolean local,
175 GdkContentProvider *content)
176{
177 GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (clipboard);
178
179 if (local)
180 {
181 GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (gdk_clipboard_get_display (clipboard));
182 GdkDevice *device;
183 const char * const *mime_types;
184 gsize i, n_mime_types;
185
186 gdk_wayland_clipboard_discard_offer (cb);
187 gdk_wayland_clipboard_discard_source (cb);
188
189 cb->source = wl_data_device_manager_create_data_source (wl_data_device_manager: wayland_display->data_device_manager);
190 wl_data_source_add_listener (wl_data_source: cb->source, listener: &data_source_listener, data: cb);
191
192 mime_types = gdk_content_formats_get_mime_types (formats, n_mime_types: &n_mime_types);
193 for (i = 0; i < n_mime_types; i++)
194 {
195 wl_data_source_offer (wl_data_source: cb->source, mime_type: mime_types[i]);
196 }
197
198 device = gdk_seat_get_pointer (seat: gdk_display_get_default_seat (GDK_DISPLAY (wayland_display)));
199 gdk_wayland_device_set_selection (gdk_device: device, source: cb->source);
200 }
201
202 return GDK_CLIPBOARD_CLASS (gdk_wayland_clipboard_parent_class)->claim (clipboard, formats, local, content);
203}
204
205static void
206gdk_wayland_clipboard_read_async (GdkClipboard *clipboard,
207 GdkContentFormats *formats,
208 int io_priority,
209 GCancellable *cancellable,
210 GAsyncReadyCallback callback,
211 gpointer user_data)
212{
213 GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (clipboard);
214 GInputStream *stream;
215 const char *mime_type;
216 int pipe_fd[2];
217 GError *error = NULL;
218 GTask *task;
219
220 task = g_task_new (source_object: clipboard, cancellable, callback, callback_data: user_data);
221 g_task_set_priority (task, priority: io_priority);
222 g_task_set_source_tag (task, gdk_wayland_clipboard_read_async);
223
224 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), CLIPBOARD, char *s = gdk_content_formats_to_string (formats);
225 g_message ("%p: read for %s", cb, s);
226 g_free (s); );
227 mime_type = gdk_content_formats_match_mime_type (first: formats, second: cb->offer_formats);
228 if (mime_type == NULL)
229 {
230 g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
231 _("No compatible transfer format found"));
232 return;
233 }
234 /* offer formats should be empty if we have no offer */
235 g_assert (cb->offer);
236
237 g_task_set_task_data (task, task_data: (gpointer) mime_type, NULL);
238
239 if (!g_unix_open_pipe (fds: pipe_fd, FD_CLOEXEC, error: &error))
240 {
241 g_task_return_error (task, error);
242 return;
243 }
244
245 wl_data_offer_receive (wl_data_offer: cb->offer, mime_type, fd: pipe_fd[1]);
246 stream = g_unix_input_stream_new (fd: pipe_fd[0], TRUE);
247 close (fd: pipe_fd[1]);
248 g_task_return_pointer (task, result: stream, result_destroy: g_object_unref);
249}
250
251static GInputStream *
252gdk_wayland_clipboard_read_finish (GdkClipboard *clipboard,
253 GAsyncResult *result,
254 const char **out_mime_type,
255 GError **error)
256{
257 GTask *task;
258
259 g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (clipboard)), NULL);
260 task = G_TASK (result);
261 g_return_val_if_fail (g_task_get_source_tag (task) == gdk_wayland_clipboard_read_async, NULL);
262
263 if (out_mime_type)
264 *out_mime_type = g_task_get_task_data (task);
265
266 return g_task_propagate_pointer (task, error);
267}
268
269static void
270gdk_wayland_clipboard_class_init (GdkWaylandClipboardClass *class)
271{
272 GObjectClass *object_class = G_OBJECT_CLASS (class);
273 GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class);
274
275 object_class->finalize = gdk_wayland_clipboard_finalize;
276
277 clipboard_class->claim = gdk_wayland_clipboard_claim;
278 clipboard_class->read_async = gdk_wayland_clipboard_read_async;
279 clipboard_class->read_finish = gdk_wayland_clipboard_read_finish;
280}
281
282static void
283gdk_wayland_clipboard_init (GdkWaylandClipboard *cb)
284{
285}
286
287GdkClipboard *
288gdk_wayland_clipboard_new (GdkDisplay *display)
289{
290 GdkWaylandClipboard *cb;
291
292 cb = g_object_new (GDK_TYPE_WAYLAND_CLIPBOARD,
293 first_property_name: "display", display,
294 NULL);
295
296 return GDK_CLIPBOARD (cb);
297}
298
299void
300gdk_wayland_clipboard_claim_remote (GdkWaylandClipboard *cb,
301 struct wl_data_offer *offer,
302 GdkContentFormats *formats)
303{
304 g_return_if_fail (GDK_IS_WAYLAND_CLIPBOARD (cb));
305
306 if (cb->source)
307 {
308 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), CLIPBOARD, g_message ("%p: Ignoring clipboard offer for self", cb));
309 gdk_content_formats_unref (formats);
310 return;
311 }
312
313 gdk_wayland_clipboard_discard_offer (cb);
314
315 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), CLIPBOARD, char *s = gdk_content_formats_to_string (formats);
316 g_message ("%p: remote clipboard claim for %s", cb, s);
317 g_free (s); );
318 cb->offer_formats = formats;
319 cb->offer = offer;
320
321 gdk_clipboard_claim_remote (GDK_CLIPBOARD (cb),
322 formats: cb->offer_formats);
323}
324
325

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