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