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-gdk-clipboard"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gdk/gdk.h>
9 : : #include <valent.h>
10 : :
11 : : #include "valent-gdk-clipboard.h"
12 : :
13 : : #define CLIPBOARD_MAXSIZE (16 * 1024)
14 : :
15 : :
16 : : struct _ValentGdkClipboard
17 : : {
18 : : ValentClipboardAdapter parent_instance;
19 : :
20 : : GdkClipboard *clipboard;
21 : : int64_t timestamp;
22 : : };
23 : :
24 [ + + + - ]: 20 : G_DEFINE_FINAL_TYPE (ValentGdkClipboard, valent_gdk_clipboard, VALENT_TYPE_CLIPBOARD_ADAPTER)
25 : :
26 : : static const char * const text_mimetypes[] = {
27 : : "text/plain;charset=utf-8",
28 : : "text/plain",
29 : : "UTF8_STRING",
30 : : "STRING",
31 : : "TEXT",
32 : : "COMPOUND_TEXT",
33 : : NULL,
34 : : };
35 : :
36 : :
37 : : /*
38 : : * GdkClipboard Callbacks
39 : : */
40 : : static void
41 : 3 : on_changed (GdkClipboard *clipboard,
42 : : ValentGdkClipboard *self)
43 : : {
44 : 3 : ValentClipboardAdapter *adapter = VALENT_CLIPBOARD_ADAPTER (self);
45 : :
46 [ - + ]: 3 : g_assert (VALENT_IS_GDK_CLIPBOARD (self));
47 [ + - ]: 3 : g_assert (VALENT_IS_CLIPBOARD_ADAPTER (adapter));
48 : :
49 : : // TODO: get the actual TIMESTAMP value
50 : 3 : self->timestamp = valent_timestamp_ms ();
51 : 3 : valent_clipboard_adapter_changed (adapter);
52 : 3 : }
53 : :
54 : : static void
55 : 0 : g_input_stream_read_bytes_cb (GInputStream *stream,
56 : : GAsyncResult *result,
57 : : gpointer user_data)
58 : : {
59 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
60 [ # # # # ]: 0 : g_autoptr (GBytes) bytes = NULL;
61 [ # # ]: 0 : g_autoptr (GError) error = NULL;
62 : :
63 : 0 : bytes = g_input_stream_read_bytes_finish (stream, result, &error);
64 [ # # ]: 0 : if (bytes == NULL)
65 : : {
66 : 0 : g_task_return_error (task, g_steal_pointer (&error));
67 [ # # ]: 0 : return;
68 : : }
69 : :
70 [ # # ]: 0 : g_task_return_pointer (task,
71 : 0 : g_bytes_ref (bytes),
72 : : (GDestroyNotify)g_bytes_unref);
73 : : }
74 : :
75 : : static void
76 : 0 : gdk_clipboard_read_cb (GdkClipboard *clipboard,
77 : : GAsyncResult *result,
78 : : gpointer user_data)
79 : : {
80 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
81 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
82 [ # # ]: 0 : g_autoptr (GInputStream) input = NULL;
83 : 0 : const char *mimetype = NULL;
84 [ # # ]: 0 : g_autoptr (GError) error = NULL;
85 : :
86 [ # # # # : 0 : g_assert (GDK_IS_CLIPBOARD (clipboard));
# # # # ]
87 [ # # ]: 0 : g_assert (g_task_is_valid (result, clipboard));
88 : :
89 : 0 : input = gdk_clipboard_read_finish (clipboard, result, &mimetype, &error);
90 [ # # ]: 0 : if (input == NULL)
91 : : {
92 : 0 : g_task_return_error (task, g_steal_pointer (&error));
93 [ # # ]: 0 : return;
94 : : }
95 : :
96 [ # # ]: 0 : g_input_stream_read_bytes_async (input,
97 : : G_PRIORITY_DEFAULT,
98 : : CLIPBOARD_MAXSIZE,
99 : : cancellable,
100 : : (GAsyncReadyCallback)g_input_stream_read_bytes_cb,
101 : : g_steal_pointer (&task));
102 : : }
103 : :
104 : : static void
105 : 3 : gdk_clipboard_read_text_cb (GdkClipboard *clipboard,
106 : : GAsyncResult *result,
107 : : gpointer user_data)
108 : : {
109 : 3 : g_autoptr (GTask) task = G_TASK (user_data);
110 : 3 : char *text = NULL;
111 : 3 : GError *error = NULL;
112 : :
113 [ + - + - : 3 : g_assert (GDK_IS_CLIPBOARD (clipboard));
+ - - + ]
114 [ + - ]: 3 : g_assert (g_task_is_valid (result, clipboard));
115 : :
116 : 3 : text = gdk_clipboard_read_text_finish (clipboard, result, &error);
117 [ - + ]: 3 : if (text == NULL)
118 : : {
119 : 0 : g_task_return_error (task, g_steal_pointer (&error));
120 [ # # ]: 0 : return;
121 : : }
122 : :
123 [ + - ]: 3 : g_task_return_pointer (task,
124 : 3 : g_bytes_new_take (text, strlen (text) + 1),
125 : : (GDestroyNotify)g_bytes_unref);
126 : : }
127 : :
128 : : /*
129 : : * ValentClipboardAdapter
130 : : */
131 : : static GStrv
132 : 4 : valent_gdk_clipboard_get_mimetypes (ValentClipboardAdapter *adapter)
133 : : {
134 : 4 : ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
135 : 4 : GdkContentFormats *formats = NULL;
136 : 4 : const char * const *mimetypes = NULL;
137 : :
138 [ - + ]: 4 : g_assert (VALENT_IS_GDK_CLIPBOARD (self));
139 [ + - + - : 4 : g_return_val_if_fail (GDK_IS_CLIPBOARD (self->clipboard), NULL);
+ - + - ]
140 : :
141 [ - + ]: 4 : if ((formats = gdk_clipboard_get_formats (self->clipboard)) == NULL)
142 : : return NULL;
143 : :
144 : 4 : mimetypes = gdk_content_formats_get_mime_types (formats, NULL);
145 : :
146 : 4 : return g_strdupv ((char **)mimetypes);
147 : : }
148 : :
149 : : static int64_t
150 : 2 : valent_gdk_clipboard_get_timestamp (ValentClipboardAdapter *adapter)
151 : : {
152 : 2 : ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
153 : :
154 [ - + ]: 2 : g_assert (VALENT_IS_GDK_CLIPBOARD (self));
155 [ + - + - : 2 : g_return_val_if_fail (GDK_IS_CLIPBOARD (self->clipboard), 0);
+ - + - ]
156 : :
157 : 2 : return self->timestamp;
158 : : }
159 : :
160 : : static void
161 : 3 : valent_gdk_clipboard_read_bytes (ValentClipboardAdapter *adapter,
162 : : const char *mimetype,
163 : : GCancellable *cancellable,
164 : : GAsyncReadyCallback callback,
165 : : gpointer user_data)
166 : : {
167 : 3 : ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
168 : 3 : g_autoptr (GTask) task = NULL;
169 : 3 : GdkContentFormats *formats = NULL;
170 : :
171 [ - + ]: 3 : g_assert (VALENT_IS_GDK_CLIPBOARD (self));
172 [ + - + - ]: 3 : g_assert (mimetype != NULL && *mimetype != '\0');
173 : :
174 [ + - + - : 3 : if G_UNLIKELY (!GDK_IS_CLIPBOARD (self->clipboard))
+ - - + ]
175 : : {
176 : 0 : g_task_report_new_error (adapter, callback, user_data,
177 : : valent_gdk_clipboard_read_bytes,
178 : : G_IO_ERROR,
179 : : G_IO_ERROR_NOT_SUPPORTED,
180 : : "Clipboard not available");
181 : 0 : return;
182 : : }
183 : :
184 : : /* Special case for text */
185 [ + - ]: 3 : if (g_strv_contains (text_mimetypes, mimetype))
186 : : {
187 : 3 : task = g_task_new (adapter, cancellable, callback, user_data);
188 [ + - ]: 3 : g_task_set_source_tag (task, valent_gdk_clipboard_read_bytes);
189 : :
190 : 3 : gdk_clipboard_read_text_async (self->clipboard,
191 : : cancellable,
192 : : (GAsyncReadyCallback)gdk_clipboard_read_text_cb,
193 : : g_steal_pointer (&task));
194 : 3 : return;
195 : : }
196 : :
197 [ # # ]: 0 : if ((formats = gdk_clipboard_get_formats (self->clipboard)) == NULL)
198 : : {
199 : 0 : g_task_report_new_error (adapter, callback, user_data,
200 : : valent_gdk_clipboard_read_bytes,
201 : : G_IO_ERROR,
202 : : G_IO_ERROR_NOT_SUPPORTED,
203 : : "Clipboard empty");
204 : 0 : return;
205 : : }
206 : :
207 [ # # ]: 0 : if (!gdk_content_formats_contain_mime_type (formats, mimetype))
208 : : {
209 : 0 : g_task_report_new_error (adapter, callback, user_data,
210 : : valent_gdk_clipboard_read_bytes,
211 : : G_IO_ERROR,
212 : : G_IO_ERROR_NOT_SUPPORTED,
213 : : "%s format not available.",
214 : : mimetype);
215 : 0 : return;
216 : : }
217 : :
218 : 0 : task = g_task_new (adapter, cancellable, callback, user_data);
219 [ # # ]: 0 : g_task_set_source_tag (task, valent_gdk_clipboard_read_bytes);
220 [ # # ]: 0 : g_task_set_task_data (task, g_strdup (mimetype), g_free);
221 : :
222 : 0 : gdk_clipboard_read_async (self->clipboard,
223 : 0 : (const char *[]){ mimetype, NULL },
224 : : G_PRIORITY_DEFAULT,
225 : : cancellable,
226 : : (GAsyncReadyCallback)gdk_clipboard_read_cb,
227 : : g_steal_pointer (&task));
228 : : }
229 : :
230 : : static void
231 : 2 : valent_gdk_clipboard_write_bytes (ValentClipboardAdapter *adapter,
232 : : const char *mimetype,
233 : : GBytes *bytes,
234 : : GCancellable *cancellable,
235 : : GAsyncReadyCallback callback,
236 : : gpointer user_data)
237 : : {
238 : 2 : ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
239 : 4 : g_autoptr (GdkContentProvider) content = NULL;
240 [ - - + - ]: 2 : g_autoptr (GTask) task = NULL;
241 : :
242 [ - + ]: 2 : g_assert (VALENT_IS_GDK_CLIPBOARD (self));
243 [ - + - - : 2 : g_assert (bytes != NULL || (mimetype != NULL && *mimetype != '\0'));
- - ]
244 [ + - + - : 2 : g_return_if_fail (GDK_IS_CLIPBOARD (self->clipboard));
+ - + - ]
245 : :
246 [ + - ]: 2 : if (bytes != NULL)
247 : 2 : content = gdk_content_provider_new_for_bytes (mimetype, bytes);
248 : :
249 [ - + ]: 2 : if (!gdk_clipboard_set_content (self->clipboard, content))
250 : : {
251 : 0 : g_task_report_new_error (adapter, callback, user_data,
252 : : valent_gdk_clipboard_write_bytes,
253 : : G_IO_ERROR,
254 : : G_IO_ERROR_FAILED,
255 : : "Failed to set clipboard content");
256 [ # # ]: 0 : return;
257 : : }
258 : :
259 : 2 : task = g_task_new (adapter, cancellable, callback, user_data);
260 [ + - ]: 2 : g_task_set_source_tag (task, valent_gdk_clipboard_write_bytes);
261 [ + - ]: 2 : g_task_return_boolean (task, TRUE);
262 : : }
263 : :
264 : : /*
265 : : * ValentObject
266 : : */
267 : : static void
268 : 2 : valent_gdk_clipboard_destroy (ValentObject *object)
269 : : {
270 : 2 : ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (object);
271 : :
272 [ + + ]: 2 : if (self->clipboard != NULL)
273 : : {
274 : 1 : g_signal_handlers_disconnect_by_data (self->clipboard, self);
275 : 1 : self->clipboard = NULL;
276 : : }
277 : :
278 : 2 : VALENT_OBJECT_CLASS (valent_gdk_clipboard_parent_class)->destroy (object);
279 : 2 : }
280 : :
281 : : /*
282 : : * GObject
283 : : */
284 : : static void
285 : 1 : valent_gdk_clipboard_constructed (GObject *object)
286 : : {
287 : 1 : ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (object);
288 : 1 : GdkDisplay *display;
289 : :
290 : 1 : G_OBJECT_CLASS (valent_gdk_clipboard_parent_class)->constructed (object);
291 : :
292 [ + - ]: 1 : if ((display = gdk_display_get_default ()) != NULL)
293 : : {
294 : 1 : self->clipboard = gdk_display_get_clipboard (display);
295 : 1 : g_signal_connect_object (self->clipboard,
296 : : "changed",
297 : : G_CALLBACK (on_changed),
298 : : self,
299 : : G_CONNECT_DEFAULT);
300 : : }
301 : : else
302 : : {
303 : 0 : valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
304 : : VALENT_PLUGIN_STATE_INACTIVE,
305 : : NULL);
306 : : }
307 : 1 : }
308 : :
309 : : static void
310 : 2 : valent_gdk_clipboard_class_init (ValentGdkClipboardClass *klass)
311 : : {
312 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
313 : 2 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
314 : 2 : ValentClipboardAdapterClass *clipboard_class = VALENT_CLIPBOARD_ADAPTER_CLASS (klass);
315 : :
316 : 2 : object_class->constructed = valent_gdk_clipboard_constructed;
317 : :
318 : 2 : vobject_class->destroy = valent_gdk_clipboard_destroy;
319 : :
320 : 2 : clipboard_class->get_mimetypes = valent_gdk_clipboard_get_mimetypes;
321 : 2 : clipboard_class->get_timestamp = valent_gdk_clipboard_get_timestamp;
322 : 2 : clipboard_class->read_bytes = valent_gdk_clipboard_read_bytes;
323 : 2 : clipboard_class->write_bytes = valent_gdk_clipboard_write_bytes;
324 : : }
325 : :
326 : : static void
327 : 1 : valent_gdk_clipboard_init (ValentGdkClipboard *self)
328 : : {
329 : 1 : }
330 : :
|