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