Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-mutter-clipboard"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <gio/gunixinputstream.h>
10 : : #include <gio/gunixoutputstream.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-mutter-clipboard.h"
14 : :
15 : : #define REMOTE_DESKTOP_NAME "org.gnome.Mutter.RemoteDesktop"
16 : : #define REMOTE_DESKTOP_PATH "/org/gnome/Mutter/RemoteDesktop"
17 : : #define REMOTE_DESKTOP_IFACE "org.gnome.Mutter.RemoteDesktop"
18 : : #define REMOTE_DESKTOP_SESSION_IFACE "org.gnome.Mutter.RemoteDesktop.Session"
19 : :
20 : : #define CLIPBOARD_MAXSIZE (16 * 1024)
21 : :
22 : :
23 : : struct _ValentMutterClipboard
24 : : {
25 : : ValentClipboardAdapter parent_instance;
26 : :
27 : : GDBusConnection *connection;
28 : : char *session_path;
29 : : unsigned int closed_id;
30 : : unsigned int selection_owner_changed_id;
31 : : unsigned int selection_transfer_id;
32 : : unsigned int watcher_id;
33 : :
34 : : GBytes *content;
35 : : GVariant *mimetypes;
36 : : int64_t timestamp;
37 : : gboolean is_owner;
38 : : };
39 : :
40 [ + + + - ]: 34 : G_DEFINE_FINAL_TYPE (ValentMutterClipboard, valent_mutter_clipboard, VALENT_TYPE_CLIPBOARD_ADAPTER);
41 : :
42 : :
43 : : /*< private >
44 : : * SelectionData:
45 : : * @content: the data being transferred
46 : : * @mimetype: the data format
47 : : * @fd: the file descriptor
48 : : * @serial: the transfer ID
49 : : *
50 : : * A `struct` for clipboard transfer data.
51 : : */
52 : : typedef struct
53 : : {
54 : : GBytes *content;
55 : : char *mimetype;
56 : : int fd;
57 : : uint32_t serial;
58 : : } SelectionData;
59 : :
60 : : static void
61 : 0 : selection_data_free (gpointer data)
62 : : {
63 : 0 : SelectionData *selection = (SelectionData *)data;
64 : :
65 [ # # ]: 0 : g_clear_pointer (&selection->content, g_bytes_unref);
66 [ # # ]: 0 : g_clear_pointer (&selection->mimetype, g_free);
67 : 0 : g_clear_pointer (&selection, g_free);
68 : 0 : }
69 : :
70 : : static int
71 : 0 : unix_fd_list_get (GUnixFDList *list,
72 : : int index_,
73 : : GError **error)
74 : : {
75 : 0 : int fd;
76 : 0 : int flags;
77 : :
78 [ # # # # : 0 : g_assert (G_IS_UNIX_FD_LIST (list));
# # # # ]
79 : :
80 [ # # ]: 0 : if ((fd = g_unix_fd_list_get (list, index_, error)) == -1)
81 : : return -1;
82 : :
83 [ # # ]: 0 : if ((flags = fcntl (fd, F_GETFD)) == -1)
84 : : {
85 : 0 : g_set_error (error,
86 : : G_IO_ERROR,
87 : 0 : g_io_error_from_errno (errno),
88 : 0 : "fcntl: %s", g_strerror (errno));
89 : 0 : close (fd);
90 : 0 : return -1;
91 : : }
92 : :
93 [ # # ]: 0 : if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
94 : : {
95 : 0 : g_set_error (error,
96 : : G_IO_ERROR,
97 : 0 : g_io_error_from_errno (errno),
98 : 0 : "fcntl: %s", g_strerror (errno));
99 : 0 : close (fd);
100 : 0 : return -1;
101 : : }
102 : :
103 : : return fd;
104 : : }
105 : :
106 : : /*
107 : : * Read
108 : : */
109 : : static void
110 : 0 : g_input_stream_read_bytes_cb (GInputStream *stream,
111 : : GAsyncResult *result,
112 : : gpointer user_data)
113 : : {
114 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
115 [ # # ]: 0 : g_autoptr (GBytes) bytes = NULL;
116 [ # # ]: 0 : g_autoptr (GError) error = NULL;
117 : :
118 : 0 : bytes = g_input_stream_read_bytes_finish (stream, result, &error);
119 : :
120 [ # # ]: 0 : if (bytes == NULL)
121 : 0 : g_task_return_error (task, g_steal_pointer (&error));
122 : :
123 [ # # ]: 0 : g_task_return_pointer (task,
124 : : g_steal_pointer (&bytes),
125 : : (GDestroyNotify)g_bytes_unref);
126 : 0 : }
127 : :
128 : : static void
129 : 0 : selection_read_cb (GDBusConnection *connection,
130 : : GAsyncResult *result,
131 : : gpointer user_data)
132 : : {
133 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
134 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
135 [ # # ]: 0 : g_autoptr (GVariant) reply = NULL;
136 [ # # ]: 0 : g_autoptr (GInputStream) stream = NULL;
137 : 0 : GUnixFDList *list = NULL;
138 : 0 : int index_;
139 : 0 : int fd;
140 [ # # # # ]: 0 : g_autoptr (GError) error = NULL;
141 : :
142 : 0 : reply = g_dbus_connection_call_with_unix_fd_list_finish (connection,
143 : : &list,
144 : : result,
145 : : &error);
146 [ # # ]: 0 : if (reply == NULL)
147 : : {
148 : 0 : g_dbus_error_strip_remote_error (error);
149 : 0 : g_task_return_error (task, g_steal_pointer (&error));
150 : 0 : return;
151 : : }
152 : :
153 : 0 : g_variant_get (reply, "(h)", &index_);
154 : 0 : fd = unix_fd_list_get (list, index_, &error);
155 [ # # ]: 0 : if (fd == -1)
156 : : {
157 : 0 : g_task_return_error (task, g_steal_pointer (&error));
158 : 0 : return;
159 : : }
160 : :
161 : 0 : stream = g_unix_input_stream_new (fd, TRUE);
162 [ # # ]: 0 : g_input_stream_read_bytes_async (stream,
163 : : CLIPBOARD_MAXSIZE,
164 : : G_PRIORITY_DEFAULT,
165 : : cancellable,
166 : : (GAsyncReadyCallback)g_input_stream_read_bytes_cb,
167 : : g_steal_pointer (&task));
168 : : }
169 : :
170 : : /*
171 : : * Write
172 : : */
173 : : static void
174 : 0 : selection_transfer_cb (GObject *object,
175 : : GAsyncResult *result,
176 : : gpointer user_data)
177 : : {
178 : 0 : g_autoptr (GError) error = NULL;
179 : :
180 [ # # # # ]: 0 : if (!g_task_propagate_boolean (G_TASK (result), &error) &&
181 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
182 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (object), error->message);
183 : 0 : }
184 : :
185 : : static void
186 : 0 : selection_write_done_cb (GDBusConnection *connection,
187 : : GAsyncResult *result,
188 : : gpointer user_data)
189 : : {
190 : 0 : g_autoptr (GVariant) reply = NULL;
191 [ # # ]: 0 : g_autoptr (GError) error = NULL;
192 : :
193 : 0 : reply = g_dbus_connection_call_finish (connection, result, &error);
194 [ # # ]: 0 : if (reply == NULL)
195 : : {
196 : 0 : g_dbus_error_strip_remote_error (error);
197 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
198 : : }
199 : 0 : }
200 : :
201 : : static void
202 : 0 : g_output_stream_write_bytes_cb (GOutputStream *stream,
203 : : GAsyncResult *result,
204 : : gpointer user_data)
205 : : {
206 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
207 : 0 : ValentMutterClipboard *self = g_task_get_source_object (task);
208 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
209 : 0 : SelectionData *selection = g_task_get_task_data (task);
210 : 0 : gboolean success = FALSE;
211 [ # # # # ]: 0 : g_autoptr (GError) error = NULL;
212 : :
213 : 0 : success = g_output_stream_write_bytes_finish (stream, result, &error) != -1;
214 : 0 : g_dbus_connection_call (self->connection,
215 : : REMOTE_DESKTOP_NAME,
216 : 0 : self->session_path,
217 : : REMOTE_DESKTOP_SESSION_IFACE,
218 : : "SelectionWriteDone",
219 : : g_variant_new ("(ub)", selection->serial, success),
220 : : NULL,
221 : : G_DBUS_CALL_FLAGS_NONE,
222 : : -1,
223 : : cancellable,
224 : : (GAsyncReadyCallback)selection_write_done_cb,
225 : : NULL);
226 [ # # ]: 0 : if (!success)
227 : : {
228 : 0 : g_task_return_error (task, g_steal_pointer (&error));
229 [ # # ]: 0 : return;
230 : : }
231 : :
232 [ # # ]: 0 : g_task_return_boolean (task, TRUE);
233 : : }
234 : :
235 : : static void
236 : 0 : selection_write_cb (GDBusConnection *connection,
237 : : GAsyncResult *result,
238 : : gpointer user_data)
239 : : {
240 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
241 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
242 : 0 : SelectionData *selection = g_task_get_task_data (task);
243 [ # # ]: 0 : g_autoptr (GVariant) reply = NULL;
244 [ # # ]: 0 : g_autoptr (GOutputStream) stream = NULL;
245 [ # # # # ]: 0 : g_autoptr (GUnixFDList) fd_list = NULL;
246 : 0 : int index_;
247 [ # # # # ]: 0 : g_autoptr (GError) error = NULL;
248 : :
249 : 0 : reply = g_dbus_connection_call_with_unix_fd_list_finish (connection,
250 : : &fd_list,
251 : : result,
252 : : &error);
253 [ # # ]: 0 : if (reply == NULL)
254 : : {
255 : 0 : g_dbus_error_strip_remote_error (error);
256 : 0 : g_task_return_error (task, g_steal_pointer (&error));
257 : 0 : return;
258 : : }
259 : :
260 : 0 : g_variant_get (reply, "(h)", &index_);
261 : 0 : selection->fd = unix_fd_list_get (fd_list, index_, &error);
262 [ # # ]: 0 : if (selection->fd == -1)
263 : : {
264 : 0 : g_task_return_error (task, g_steal_pointer (&error));
265 : 0 : return;
266 : : }
267 : :
268 : 0 : stream = g_unix_output_stream_new (selection->fd, TRUE);
269 [ # # ]: 0 : g_output_stream_write_bytes_async (stream,
270 : : selection->content,
271 : : G_PRIORITY_DEFAULT,
272 : : cancellable,
273 : : (GAsyncReadyCallback)g_output_stream_write_bytes_cb,
274 : : g_steal_pointer (&task));
275 : : }
276 : :
277 : : static void
278 : 0 : valent_mutter_clipboard_selection_write (ValentMutterClipboard *self,
279 : : const char *mimetype,
280 : : uint32_t serial)
281 : : {
282 : 0 : g_autoptr (GCancellable) cancellable = NULL;
283 [ # # ]: 0 : g_autoptr (GTask) task = NULL;
284 : 0 : SelectionData *selection = NULL;
285 : :
286 [ # # ]: 0 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
287 : :
288 : 0 : selection = g_new0 (SelectionData, 1);
289 : 0 : selection->content = g_bytes_ref (self->content);
290 [ # # ]: 0 : selection->mimetype = g_strdup (mimetype);
291 : 0 : selection->serial = serial;
292 : :
293 : 0 : cancellable = valent_object_ref_cancellable (VALENT_OBJECT (self));
294 : :
295 : 0 : task = g_task_new (self, cancellable, selection_transfer_cb, NULL);
296 : 0 : g_task_set_task_data (task, selection, selection_data_free);
297 [ # # ]: 0 : g_task_set_source_tag (task, valent_mutter_clipboard_selection_write);
298 : :
299 [ # # ]: 0 : g_dbus_connection_call (self->connection,
300 : : REMOTE_DESKTOP_NAME,
301 : 0 : self->session_path,
302 : : REMOTE_DESKTOP_SESSION_IFACE,
303 : : "SelectionWrite",
304 : : g_variant_new ("(u)", selection->serial),
305 : : G_VARIANT_TYPE ("(h)"),
306 : : G_DBUS_CALL_FLAGS_NONE,
307 : : -1,
308 : : cancellable,
309 : : (GAsyncReadyCallback)selection_write_cb,
310 : : g_steal_pointer (&task));
311 : 0 : }
312 : :
313 : : /*
314 : : * org.gnome.Mutter.RemoteDesktop.Session Callbacks
315 : : */
316 : : static void
317 : 0 : on_closed (GDBusConnection *connection,
318 : : const char *sender_name,
319 : : const char *object_path,
320 : : const char *interface_name,
321 : : const char *signal_name,
322 : : GVariant *parameters,
323 : : gpointer user_data)
324 : : {
325 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (user_data);
326 : :
327 [ # # ]: 0 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
328 [ # # ]: 0 : g_assert (g_str_equal (signal_name, "Closed"));
329 : :
330 [ # # ]: 0 : if (self->closed_id != 0)
331 : : {
332 : 0 : g_dbus_connection_signal_unsubscribe (connection,
333 : : self->closed_id);
334 : 0 : self->closed_id = 0;
335 : : }
336 : :
337 [ # # ]: 0 : if (self->selection_owner_changed_id != 0)
338 : : {
339 : 0 : g_dbus_connection_signal_unsubscribe (connection,
340 : : self->selection_owner_changed_id);
341 : 0 : self->selection_owner_changed_id = 0;
342 : : }
343 : :
344 [ # # ]: 0 : if (self->selection_transfer_id != 0)
345 : : {
346 : 0 : g_dbus_connection_signal_unsubscribe (connection,
347 : : self->selection_transfer_id);
348 : 0 : self->selection_transfer_id = 0;
349 : : }
350 : :
351 [ # # ]: 0 : g_clear_pointer (&self->session_path, g_free);
352 : 0 : }
353 : :
354 : : static void
355 : 0 : on_selection_owner_changed (GDBusConnection *connection,
356 : : const char *sender_name,
357 : : const char *object_path,
358 : : const char *interface_name,
359 : : const char *signal_name,
360 : : GVariant *parameters,
361 : : gpointer user_data)
362 : : {
363 : 0 : ValentClipboardAdapter *adapter = VALENT_CLIPBOARD_ADAPTER (user_data);
364 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (user_data);
365 : 0 : g_autoptr (GVariant) options = NULL;
366 [ # # ]: 0 : g_autoptr (GVariant) mimetypes = NULL;
367 : :
368 [ # # ]: 0 : g_assert (VALENT_IS_CLIPBOARD_ADAPTER (adapter));
369 [ # # ]: 0 : g_assert (g_str_equal (signal_name, "SelectionOwnerChanged"));
370 : :
371 : 0 : options = g_variant_get_child_value (parameters, 0);
372 : :
373 [ # # ]: 0 : if (g_variant_lookup (options, "mime-types", "(@as)", &mimetypes))
374 : : {
375 [ # # ]: 0 : g_clear_pointer (&self->mimetypes, g_variant_unref);
376 : 0 : self->mimetypes = g_variant_ref_sink (g_steal_pointer (&mimetypes));
377 : : }
378 : :
379 [ # # ]: 0 : if (!g_variant_lookup (options, "session-is-owner", "b", &self->is_owner))
380 : 0 : self->is_owner = FALSE;
381 : :
382 : : /* Free the cache if ownership of the selection has been lost */
383 [ # # ]: 0 : if (!self->is_owner)
384 : : {
385 [ # # ]: 0 : g_clear_pointer (&self->content, g_bytes_unref);
386 : 0 : self->timestamp = valent_timestamp_ms ();
387 : : }
388 : :
389 [ # # ]: 0 : valent_clipboard_adapter_changed (adapter);
390 : 0 : }
391 : :
392 : : static void
393 : 0 : on_selection_transfer (GDBusConnection *connection,
394 : : const char *sender_name,
395 : : const char *object_path,
396 : : const char *interface_name,
397 : : const char *signal_name,
398 : : GVariant *parameters,
399 : : gpointer user_data)
400 : : {
401 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (user_data);
402 : 0 : const char *mimetype;
403 : 0 : uint32_t serial;
404 : :
405 [ # # ]: 0 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
406 [ # # ]: 0 : g_assert (g_str_equal (signal_name, "SelectionTransfer"));
407 : :
408 : 0 : g_variant_get (parameters, "(&su)", &mimetype, &serial);
409 : 0 : valent_mutter_clipboard_selection_write (self, mimetype, serial);
410 : 0 : }
411 : :
412 : : /*
413 : : * ValentClipboardAdapter
414 : : */
415 : : static GStrv
416 : 0 : valent_mutter_clipboard_get_mimetypes (ValentClipboardAdapter *adapter)
417 : : {
418 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (adapter);
419 : :
420 [ # # ]: 0 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
421 : :
422 [ # # ]: 0 : if (self->mimetypes == NULL)
423 : : return NULL;
424 : :
425 : 0 : return g_variant_dup_strv (self->mimetypes, NULL);
426 : : }
427 : :
428 : : static int64_t
429 : 2 : valent_mutter_clipboard_get_timestamp (ValentClipboardAdapter *adapter)
430 : : {
431 : 2 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (adapter);
432 : :
433 [ - + ]: 2 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
434 : :
435 : 2 : return self->timestamp;
436 : : }
437 : :
438 : : static void
439 : 0 : valent_mutter_clipboard_read_bytes (ValentClipboardAdapter *adapter,
440 : : const char *mimetype,
441 : : GCancellable *cancellable,
442 : : GAsyncReadyCallback callback,
443 : : gpointer user_data)
444 : : {
445 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (adapter);
446 : 0 : g_autoptr (GTask) task = NULL;
447 [ # # ]: 0 : g_autofree const char **mimetypes = NULL;
448 : :
449 [ # # ]: 0 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
450 [ # # # # ]: 0 : g_assert (mimetype != NULL && *mimetype != '\0');
451 : :
452 [ # # # # ]: 0 : if (self->connection == NULL || self->session_path == NULL)
453 : : {
454 : 0 : g_task_report_new_error (adapter, callback, user_data, callback,
455 : : G_IO_ERROR,
456 : : G_IO_ERROR_DBUS_ERROR,
457 : : "Clipboard service not available.");
458 : 0 : return;
459 : : }
460 : :
461 [ # # ]: 0 : if (self->mimetypes == NULL)
462 : : {
463 : 0 : g_task_report_new_error (adapter, callback, user_data, callback,
464 : : G_IO_ERROR,
465 : : G_IO_ERROR_NOT_SUPPORTED,
466 : : "Clipboard empty");
467 : 0 : return;
468 : : }
469 : :
470 : 0 : mimetypes = g_variant_get_strv (self->mimetypes, NULL);
471 [ # # ]: 0 : if (!g_strv_contains (mimetypes, mimetype))
472 : : {
473 : 0 : g_task_report_new_error (adapter, callback, user_data, callback,
474 : : G_IO_ERROR,
475 : : G_IO_ERROR_NOT_SUPPORTED,
476 : : "%s format not available.",
477 : : mimetype);
478 : 0 : return;
479 : : }
480 : :
481 : 0 : task = g_task_new (adapter, cancellable, callback, user_data);
482 [ # # ]: 0 : g_task_set_source_tag (task, valent_mutter_clipboard_read_bytes);
483 : :
484 [ # # # # ]: 0 : if (self->is_owner && self->content != NULL)
485 : : {
486 : 0 : g_task_return_pointer (task,
487 : 0 : g_bytes_ref (self->content),
488 : : (GDestroyNotify)g_bytes_unref);
489 : 0 : return;
490 : : }
491 : :
492 : 0 : g_dbus_connection_call (self->connection,
493 : : REMOTE_DESKTOP_NAME,
494 : 0 : self->session_path,
495 : : REMOTE_DESKTOP_SESSION_IFACE,
496 : : "SelectionRead",
497 : : g_variant_new ("(s)", mimetype),
498 : : G_VARIANT_TYPE ("(h)"),
499 : : G_DBUS_CALL_FLAGS_NONE,
500 : : -1,
501 : : cancellable,
502 : : (GAsyncReadyCallback)selection_read_cb,
503 : : g_steal_pointer (&task));
504 : : }
505 : :
506 : : static void
507 : 0 : set_selection_cb (GDBusConnection *connection,
508 : : GAsyncResult *result,
509 : : gpointer user_data)
510 : : {
511 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
512 [ # # # # ]: 0 : g_autoptr (GVariant) reply = NULL;
513 [ # # ]: 0 : g_autoptr (GError) error = NULL;
514 : :
515 : 0 : reply = g_dbus_connection_call_finish (connection, result, &error);
516 [ # # ]: 0 : if (reply == NULL)
517 : : {
518 : 0 : g_dbus_error_strip_remote_error (error);
519 : 0 : g_task_return_error (task, g_steal_pointer (&error));
520 [ # # ]: 0 : return;
521 : : }
522 : :
523 [ # # ]: 0 : g_task_return_boolean (task, TRUE);
524 : : }
525 : :
526 : : static void
527 : 0 : valent_mutter_clipboard_write_bytes (ValentClipboardAdapter *adapter,
528 : : const char *mimetype,
529 : : GBytes *bytes,
530 : : GCancellable *cancellable,
531 : : GAsyncReadyCallback callback,
532 : : gpointer user_data)
533 : : {
534 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (adapter);
535 : 0 : g_autoptr (GTask) task = NULL;
536 : 0 : GVariantBuilder options;
537 : :
538 [ # # ]: 0 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
539 [ # # # # : 0 : g_assert (bytes != NULL || (mimetype != NULL && *mimetype != '\0'));
# # ]
540 : :
541 [ # # # # ]: 0 : if (self->connection == NULL || self->session_path == NULL)
542 : : {
543 : 0 : g_task_report_new_error (adapter, callback, user_data, callback,
544 : : G_IO_ERROR,
545 : : G_IO_ERROR_DBUS_ERROR,
546 : : "Clipboard service not available.");
547 : 0 : return;
548 : : }
549 : :
550 : : /* Update the local content */
551 [ # # ]: 0 : g_clear_pointer (&self->content, g_bytes_unref);
552 : 0 : self->content = g_bytes_ref (bytes);
553 [ # # ]: 0 : g_clear_pointer (&self->mimetypes, g_variant_unref);
554 : 0 : self->mimetypes = g_variant_new_strv (VALENT_STRV_INIT (mimetype), -1);
555 : 0 : g_variant_ref_sink (self->mimetypes);
556 : 0 : self->timestamp = valent_timestamp_ms ();
557 : :
558 : : /* Inform Mutter */
559 : 0 : task = g_task_new (adapter, cancellable, callback, user_data);
560 [ # # ]: 0 : g_task_set_source_tag (task, valent_mutter_clipboard_write_bytes);
561 : :
562 : 0 : g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
563 : 0 : g_variant_builder_add (&options, "{sv}", "mime-types", self->mimetypes);
564 : :
565 : 0 : g_dbus_connection_call (self->connection,
566 : : REMOTE_DESKTOP_NAME,
567 : 0 : self->session_path,
568 : : REMOTE_DESKTOP_SESSION_IFACE,
569 : : "SetSelection",
570 : : g_variant_new ("(@a{sv})",
571 : : g_variant_builder_end (&options)),
572 : : NULL,
573 : : G_DBUS_CALL_FLAGS_NONE,
574 : : -1,
575 : : cancellable,
576 : : (GAsyncReadyCallback)set_selection_cb,
577 : : g_steal_pointer (&task));
578 : : }
579 : :
580 : : /*
581 : : * GAsyncInitable
582 : : */
583 : : static void
584 : 0 : create_session_error_handler (GObject *object,
585 : : GAsyncResult *result,
586 : : gpointer user_data)
587 : : {
588 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (object);
589 : 0 : g_autoptr (GError) error = NULL;
590 : :
591 [ # # # # ]: 0 : if (g_task_propagate_boolean (G_TASK (result), &error) ||
592 : 0 : valent_object_in_destruction (VALENT_OBJECT (self)))
593 [ # # ]: 0 : return;
594 : :
595 [ # # ]: 0 : if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
596 : : {
597 : 0 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
598 : : VALENT_PLUGIN_STATE_INACTIVE,
599 : : NULL);
600 : : }
601 : : else
602 : : {
603 : 0 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
604 : : VALENT_PLUGIN_STATE_ERROR,
605 : : error);
606 : : }
607 : : }
608 : :
609 : : static void
610 : 0 : enable_clipboard_cb (GDBusConnection *connection,
611 : : GAsyncResult *result,
612 : : gpointer user_data)
613 : : {
614 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
615 : 0 : ValentMutterClipboard *self = g_task_get_source_object (task);
616 [ # # # # ]: 0 : g_autoptr (GVariant) reply = NULL;
617 [ # # ]: 0 : g_autoptr (GError) error = NULL;
618 : :
619 : 0 : reply = g_dbus_connection_call_finish (connection, result, &error);
620 [ # # ]: 0 : if (reply == NULL)
621 : : {
622 : 0 : g_dbus_error_strip_remote_error (error);
623 : 0 : g_task_return_error (task, g_steal_pointer (&error));
624 [ # # ]: 0 : return;
625 : : }
626 : :
627 : 0 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
628 : : VALENT_PLUGIN_STATE_ACTIVE,
629 : : NULL);
630 [ # # ]: 0 : g_task_return_boolean (task, TRUE);
631 : : }
632 : :
633 : : static void
634 : 0 : create_session_cb (GDBusConnection *connection,
635 : : GAsyncResult *result,
636 : : gpointer user_data)
637 : : {
638 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
639 : 0 : ValentMutterClipboard *self = g_task_get_source_object (task);
640 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
641 [ # # ]: 0 : g_autoptr (GVariant) reply = NULL;
642 [ # # ]: 0 : g_autoptr (GError) error = NULL;
643 : :
644 : 0 : reply = g_dbus_connection_call_finish (connection, result, &error);
645 [ # # ]: 0 : if (reply == NULL)
646 : : {
647 : 0 : g_dbus_error_strip_remote_error (error);
648 : 0 : g_task_return_error (task, g_steal_pointer (&error));
649 [ # # ]: 0 : return;
650 : : }
651 : :
652 [ # # ]: 0 : g_clear_pointer (&self->session_path, g_free);
653 : 0 : g_variant_get (reply, "(o)", &self->session_path);
654 : :
655 : 0 : self->closed_id =
656 : 0 : g_dbus_connection_signal_subscribe (connection,
657 : : NULL,
658 : : REMOTE_DESKTOP_SESSION_IFACE,
659 : : "Closed",
660 : 0 : self->session_path,
661 : : NULL,
662 : : G_DBUS_SIGNAL_FLAGS_NONE,
663 : : on_closed,
664 : : self, NULL);
665 : :
666 : 0 : self->selection_owner_changed_id =
667 : 0 : g_dbus_connection_signal_subscribe (connection,
668 : : NULL,
669 : : REMOTE_DESKTOP_SESSION_IFACE,
670 : : "SelectionOwnerChanged",
671 : 0 : self->session_path,
672 : : NULL,
673 : : G_DBUS_SIGNAL_FLAGS_NONE,
674 : : on_selection_owner_changed,
675 : : self, NULL);
676 : :
677 : 0 : self->selection_transfer_id =
678 : 0 : g_dbus_connection_signal_subscribe (connection,
679 : : NULL,
680 : : REMOTE_DESKTOP_SESSION_IFACE,
681 : : "SelectionTransfer",
682 : 0 : self->session_path,
683 : : NULL,
684 : : G_DBUS_SIGNAL_FLAGS_NONE,
685 : : on_selection_transfer,
686 : : self, NULL);
687 : :
688 [ # # ]: 0 : g_dbus_connection_call (connection,
689 : : REMOTE_DESKTOP_NAME,
690 : 0 : self->session_path,
691 : : REMOTE_DESKTOP_SESSION_IFACE,
692 : : "EnableClipboard",
693 : : g_variant_parse (G_VARIANT_TYPE ("(a{sv})"),
694 : : "(@a{sv} {},)",
695 : : NULL,
696 : : NULL,
697 : : &error),
698 : : NULL,
699 : : G_DBUS_CALL_FLAGS_NONE,
700 : : -1,
701 : : cancellable,
702 : : (GAsyncReadyCallback)enable_clipboard_cb,
703 : : g_steal_pointer (&task));
704 : : }
705 : :
706 : : static void
707 : 0 : on_name_appeared (GDBusConnection *connection,
708 : : const char *name,
709 : : const char *name_owner,
710 : : ValentMutterClipboard *self)
711 : : {
712 : 0 : g_autoptr (GTask) task = NULL;
713 : 0 : g_autoptr (GCancellable) destroy = NULL;
714 : :
715 [ # # ]: 0 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
716 : :
717 [ # # ]: 0 : if (!g_set_object (&self->connection, connection))
718 : : return;
719 : :
720 : 0 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
721 : 0 : task = g_task_new (self, destroy, create_session_error_handler, NULL);
722 [ # # ]: 0 : g_task_set_source_tag (task, on_name_appeared);
723 : :
724 [ # # ]: 0 : g_dbus_connection_call (self->connection,
725 : : REMOTE_DESKTOP_NAME,
726 : : REMOTE_DESKTOP_PATH,
727 : : REMOTE_DESKTOP_IFACE,
728 : : "CreateSession",
729 : : NULL,
730 : : G_VARIANT_TYPE ("(o)"),
731 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
732 : : -1,
733 : : destroy,
734 : : (GAsyncReadyCallback)create_session_cb,
735 : : g_steal_pointer (&task));
736 : : }
737 : :
738 : : static void
739 : 2 : on_name_vanished (GDBusConnection *connection,
740 : : const char *name,
741 : : ValentMutterClipboard *self)
742 : : {
743 [ - + ]: 2 : g_assert (VALENT_IS_MUTTER_CLIPBOARD (self));
744 : :
745 [ - + ]: 2 : if (self->connection != NULL)
746 : : {
747 [ # # ]: 0 : if (self->closed_id != 0)
748 : : {
749 : 0 : g_dbus_connection_signal_unsubscribe (self->connection,
750 : : self->closed_id);
751 : 0 : self->closed_id = 0;
752 : : }
753 : :
754 [ # # ]: 0 : if (self->selection_owner_changed_id != 0)
755 : : {
756 : 0 : g_dbus_connection_signal_unsubscribe (self->connection,
757 : : self->selection_owner_changed_id);
758 : 0 : self->selection_owner_changed_id = 0;
759 : : }
760 : :
761 [ # # ]: 0 : if (self->selection_transfer_id != 0)
762 : : {
763 : 0 : g_dbus_connection_signal_unsubscribe (self->connection,
764 : : self->selection_transfer_id);
765 : 0 : self->selection_transfer_id = 0;
766 : : }
767 : :
768 [ # # ]: 0 : if (self->session_path != NULL)
769 : : {
770 : 0 : g_dbus_connection_call (self->connection,
771 : : REMOTE_DESKTOP_NAME,
772 : : self->session_path,
773 : : REMOTE_DESKTOP_SESSION_IFACE,
774 : : "DisableClipboard",
775 : : NULL,
776 : : NULL,
777 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
778 : : -1,
779 : : NULL,
780 : : NULL,
781 : : NULL);
782 : :
783 : : // HACK: `Start()` must called before `Stop()` will close the session
784 : 0 : g_dbus_connection_call (self->connection,
785 : : REMOTE_DESKTOP_NAME,
786 : 0 : self->session_path,
787 : : REMOTE_DESKTOP_SESSION_IFACE,
788 : : "Start",
789 : : NULL,
790 : : NULL,
791 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
792 : : -1,
793 : : NULL,
794 : : NULL,
795 : : NULL);
796 : 0 : g_dbus_connection_call (self->connection,
797 : : REMOTE_DESKTOP_NAME,
798 : 0 : self->session_path,
799 : : REMOTE_DESKTOP_SESSION_IFACE,
800 : : "Stop",
801 : : NULL,
802 : : NULL,
803 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
804 : : -1,
805 : : NULL,
806 : : NULL,
807 : : NULL);
808 : : }
809 : : }
810 : :
811 [ - + ]: 2 : g_clear_object (&self->connection);
812 [ - + ]: 2 : g_clear_pointer (&self->session_path, g_free);
813 : 2 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
814 : : VALENT_PLUGIN_STATE_INACTIVE,
815 : : NULL);
816 : 2 : }
817 : :
818 : : /*
819 : : * ValentObject
820 : : */
821 : : static void
822 : 0 : valent_mutter_clipboard_destroy (ValentObject *object)
823 : : {
824 : 0 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (object);
825 : :
826 [ # # ]: 0 : g_clear_handle_id (&self->watcher_id, g_bus_unwatch_name);
827 : 0 : on_name_vanished (self->connection, REMOTE_DESKTOP_NAME, self);
828 : :
829 [ # # ]: 0 : g_clear_pointer (&self->content, g_bytes_unref);
830 [ # # ]: 0 : g_clear_pointer (&self->mimetypes, g_variant_unref);
831 : :
832 : 0 : VALENT_OBJECT_CLASS (valent_mutter_clipboard_parent_class)->destroy (object);
833 : 0 : }
834 : :
835 : : /*
836 : : * GObject
837 : : */
838 : : static void
839 : 2 : valent_mutter_clipboard_constructed (GObject *object)
840 : : {
841 : 2 : ValentMutterClipboard *self = VALENT_MUTTER_CLIPBOARD (object);
842 : :
843 : 2 : G_OBJECT_CLASS (valent_mutter_clipboard_parent_class)->constructed (object);
844 : :
845 : 2 : self->watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
846 : : REMOTE_DESKTOP_NAME,
847 : : G_BUS_NAME_WATCHER_FLAGS_NONE,
848 : : (GBusNameAppearedCallback)on_name_appeared,
849 : : (GBusNameVanishedCallback)on_name_vanished,
850 : : self, NULL);
851 : 2 : }
852 : :
853 : : static void
854 : 10 : valent_mutter_clipboard_class_init (ValentMutterClipboardClass *klass)
855 : : {
856 : 10 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
857 : 10 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
858 : 10 : ValentClipboardAdapterClass *clipboard_class = VALENT_CLIPBOARD_ADAPTER_CLASS (klass);
859 : :
860 : 10 : object_class->constructed = valent_mutter_clipboard_constructed;
861 : :
862 : 10 : vobject_class->destroy = valent_mutter_clipboard_destroy;
863 : :
864 : 10 : clipboard_class->get_mimetypes = valent_mutter_clipboard_get_mimetypes;
865 : 10 : clipboard_class->get_timestamp = valent_mutter_clipboard_get_timestamp;
866 : 10 : clipboard_class->read_bytes = valent_mutter_clipboard_read_bytes;
867 : 10 : clipboard_class->write_bytes = valent_mutter_clipboard_write_bytes;
868 : : }
869 : :
870 : : static void
871 : 2 : valent_mutter_clipboard_init (ValentMutterClipboard *self)
872 : : {
873 : 2 : }
874 : :
|