xref: /openbmc/qemu/ui/dbus-clipboard.c (revision 3cce8bd4d737f2ca688bbdcb92cd5cc683245bbd)
1ff1a5810SMarc-André Lureau /*
2ff1a5810SMarc-André Lureau  * QEMU DBus display
3ff1a5810SMarc-André Lureau  *
4ff1a5810SMarc-André Lureau  * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
5ff1a5810SMarc-André Lureau  *
6ff1a5810SMarc-André Lureau  * Permission is hereby granted, free of charge, to any person obtaining a copy
7ff1a5810SMarc-André Lureau  * of this software and associated documentation files (the "Software"), to deal
8ff1a5810SMarc-André Lureau  * in the Software without restriction, including without limitation the rights
9ff1a5810SMarc-André Lureau  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10ff1a5810SMarc-André Lureau  * copies of the Software, and to permit persons to whom the Software is
11ff1a5810SMarc-André Lureau  * furnished to do so, subject to the following conditions:
12ff1a5810SMarc-André Lureau  *
13ff1a5810SMarc-André Lureau  * The above copyright notice and this permission notice shall be included in
14ff1a5810SMarc-André Lureau  * all copies or substantial portions of the Software.
15ff1a5810SMarc-André Lureau  *
16ff1a5810SMarc-André Lureau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17ff1a5810SMarc-André Lureau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18ff1a5810SMarc-André Lureau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19ff1a5810SMarc-André Lureau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20ff1a5810SMarc-André Lureau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21ff1a5810SMarc-André Lureau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22ff1a5810SMarc-André Lureau  * THE SOFTWARE.
23ff1a5810SMarc-André Lureau  */
24ff1a5810SMarc-André Lureau #include "qemu/osdep.h"
25ff1a5810SMarc-André Lureau #include "qemu/dbus.h"
265feed38cSThomas Huth #include "qemu/error-report.h"
27ff1a5810SMarc-André Lureau #include "qemu/main-loop.h"
28ff1a5810SMarc-André Lureau #include "qom/object_interfaces.h"
29ff1a5810SMarc-André Lureau #include "sysemu/sysemu.h"
30ff1a5810SMarc-André Lureau #include "qapi/error.h"
31ff1a5810SMarc-André Lureau #include "trace.h"
32ff1a5810SMarc-André Lureau 
33ff1a5810SMarc-André Lureau #include "dbus.h"
34ff1a5810SMarc-André Lureau 
35ff1a5810SMarc-André Lureau #define MIME_TEXT_PLAIN_UTF8 "text/plain;charset=utf-8"
36ff1a5810SMarc-André Lureau 
37ff1a5810SMarc-André Lureau static void
dbus_clipboard_complete_request(DBusDisplay * dpy,GDBusMethodInvocation * invocation,QemuClipboardInfo * info,QemuClipboardType type)38ff1a5810SMarc-André Lureau dbus_clipboard_complete_request(
39ff1a5810SMarc-André Lureau     DBusDisplay *dpy,
40ff1a5810SMarc-André Lureau     GDBusMethodInvocation *invocation,
41ff1a5810SMarc-André Lureau     QemuClipboardInfo *info,
42ff1a5810SMarc-André Lureau     QemuClipboardType type)
43ff1a5810SMarc-André Lureau {
44ff1a5810SMarc-André Lureau     GVariant *v_data = g_variant_new_from_data(
45ff1a5810SMarc-André Lureau         G_VARIANT_TYPE("ay"),
46ff1a5810SMarc-André Lureau         info->types[type].data,
47ff1a5810SMarc-André Lureau         info->types[type].size,
48ff1a5810SMarc-André Lureau         TRUE,
49ff1a5810SMarc-André Lureau         (GDestroyNotify)qemu_clipboard_info_unref,
50ff1a5810SMarc-André Lureau         qemu_clipboard_info_ref(info));
51ff1a5810SMarc-André Lureau 
52ff1a5810SMarc-André Lureau     qemu_dbus_display1_clipboard_complete_request(
53ff1a5810SMarc-André Lureau         dpy->clipboard, invocation,
54ff1a5810SMarc-André Lureau         MIME_TEXT_PLAIN_UTF8, v_data);
55ff1a5810SMarc-André Lureau }
56ff1a5810SMarc-André Lureau 
57ff1a5810SMarc-André Lureau static void
dbus_clipboard_update_info(DBusDisplay * dpy,QemuClipboardInfo * info)58ff1a5810SMarc-André Lureau dbus_clipboard_update_info(DBusDisplay *dpy, QemuClipboardInfo *info)
59ff1a5810SMarc-André Lureau {
60ff1a5810SMarc-André Lureau     bool self_update = info->owner == &dpy->clipboard_peer;
61ff1a5810SMarc-André Lureau     const char *mime[QEMU_CLIPBOARD_TYPE__COUNT + 1] = { 0, };
62ff1a5810SMarc-André Lureau     DBusClipboardRequest *req;
63ff1a5810SMarc-André Lureau     int i = 0;
64ff1a5810SMarc-André Lureau 
65ff1a5810SMarc-André Lureau     if (info->owner == NULL) {
66ff1a5810SMarc-André Lureau         if (dpy->clipboard_proxy) {
67ff1a5810SMarc-André Lureau             qemu_dbus_display1_clipboard_call_release(
68ff1a5810SMarc-André Lureau                 dpy->clipboard_proxy,
69ff1a5810SMarc-André Lureau                 info->selection,
70ff1a5810SMarc-André Lureau                 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
71ff1a5810SMarc-André Lureau         }
72ff1a5810SMarc-André Lureau         return;
73ff1a5810SMarc-André Lureau     }
74ff1a5810SMarc-André Lureau 
75ff1a5810SMarc-André Lureau     if (self_update || !info->has_serial) {
76ff1a5810SMarc-André Lureau         return;
77ff1a5810SMarc-André Lureau     }
78ff1a5810SMarc-André Lureau 
79ff1a5810SMarc-André Lureau     req = &dpy->clipboard_request[info->selection];
80ff1a5810SMarc-André Lureau     if (req->invocation && info->types[req->type].data) {
81ff1a5810SMarc-André Lureau         dbus_clipboard_complete_request(dpy, req->invocation, info, req->type);
82ff1a5810SMarc-André Lureau         g_clear_object(&req->invocation);
83ff1a5810SMarc-André Lureau         g_source_remove(req->timeout_id);
84ff1a5810SMarc-André Lureau         req->timeout_id = 0;
85ff1a5810SMarc-André Lureau         return;
86ff1a5810SMarc-André Lureau     }
87ff1a5810SMarc-André Lureau 
88ff1a5810SMarc-André Lureau     if (info->types[QEMU_CLIPBOARD_TYPE_TEXT].available) {
89ff1a5810SMarc-André Lureau         mime[i++] = MIME_TEXT_PLAIN_UTF8;
90ff1a5810SMarc-André Lureau     }
91ff1a5810SMarc-André Lureau 
92ff1a5810SMarc-André Lureau     if (i > 0) {
93ff1a5810SMarc-André Lureau         if (dpy->clipboard_proxy) {
94ff1a5810SMarc-André Lureau             qemu_dbus_display1_clipboard_call_grab(
95ff1a5810SMarc-André Lureau                 dpy->clipboard_proxy,
96ff1a5810SMarc-André Lureau                 info->selection,
97ff1a5810SMarc-André Lureau                 info->serial,
98ff1a5810SMarc-André Lureau                 mime,
99ff1a5810SMarc-André Lureau                 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
100ff1a5810SMarc-André Lureau         }
101ff1a5810SMarc-André Lureau     }
102ff1a5810SMarc-André Lureau }
103ff1a5810SMarc-André Lureau 
104ff1a5810SMarc-André Lureau static void
dbus_clipboard_reset_serial(DBusDisplay * dpy)105ff1a5810SMarc-André Lureau dbus_clipboard_reset_serial(DBusDisplay *dpy)
106ff1a5810SMarc-André Lureau {
107ff1a5810SMarc-André Lureau     if (dpy->clipboard_proxy) {
108ff1a5810SMarc-André Lureau         qemu_dbus_display1_clipboard_call_register(
109ff1a5810SMarc-André Lureau             dpy->clipboard_proxy,
110ff1a5810SMarc-André Lureau             G_DBUS_CALL_FLAGS_NONE,
111ff1a5810SMarc-André Lureau             -1, NULL, NULL, NULL);
112ff1a5810SMarc-André Lureau     }
113ff1a5810SMarc-André Lureau }
114ff1a5810SMarc-André Lureau 
115ff1a5810SMarc-André Lureau static void
dbus_clipboard_notify(Notifier * notifier,void * data)116ff1a5810SMarc-André Lureau dbus_clipboard_notify(Notifier *notifier, void *data)
117ff1a5810SMarc-André Lureau {
118ff1a5810SMarc-André Lureau     DBusDisplay *dpy =
119ff1a5810SMarc-André Lureau         container_of(notifier, DBusDisplay, clipboard_peer.notifier);
120ff1a5810SMarc-André Lureau     QemuClipboardNotify *notify = data;
121ff1a5810SMarc-André Lureau 
122ff1a5810SMarc-André Lureau     switch (notify->type) {
123ff1a5810SMarc-André Lureau     case QEMU_CLIPBOARD_UPDATE_INFO:
124ff1a5810SMarc-André Lureau         dbus_clipboard_update_info(dpy, notify->info);
125ff1a5810SMarc-André Lureau         return;
126ff1a5810SMarc-André Lureau     case QEMU_CLIPBOARD_RESET_SERIAL:
127ff1a5810SMarc-André Lureau         dbus_clipboard_reset_serial(dpy);
128ff1a5810SMarc-André Lureau         return;
129ff1a5810SMarc-André Lureau     }
130ff1a5810SMarc-André Lureau }
131ff1a5810SMarc-André Lureau 
132ff1a5810SMarc-André Lureau static void
dbus_clipboard_qemu_request(QemuClipboardInfo * info,QemuClipboardType type)133ff1a5810SMarc-André Lureau dbus_clipboard_qemu_request(QemuClipboardInfo *info,
134ff1a5810SMarc-André Lureau                             QemuClipboardType type)
135ff1a5810SMarc-André Lureau {
136ff1a5810SMarc-André Lureau     DBusDisplay *dpy = container_of(info->owner, DBusDisplay, clipboard_peer);
137ff1a5810SMarc-André Lureau     g_autofree char *mime = NULL;
138ff1a5810SMarc-André Lureau     g_autoptr(GVariant) v_data = NULL;
139ff1a5810SMarc-André Lureau     g_autoptr(GError) err = NULL;
140ff1a5810SMarc-André Lureau     const char *data = NULL;
141ff1a5810SMarc-André Lureau     const char *mimes[] = { MIME_TEXT_PLAIN_UTF8, NULL };
142ff1a5810SMarc-André Lureau     size_t n;
143ff1a5810SMarc-André Lureau 
144*2e35439fSMarc-André Lureau     trace_dbus_clipboard_qemu_request(type);
145*2e35439fSMarc-André Lureau 
146ff1a5810SMarc-André Lureau     if (type != QEMU_CLIPBOARD_TYPE_TEXT) {
147ff1a5810SMarc-André Lureau         /* unsupported atm */
148ff1a5810SMarc-André Lureau         return;
149ff1a5810SMarc-André Lureau     }
150ff1a5810SMarc-André Lureau 
151ff1a5810SMarc-André Lureau     if (dpy->clipboard_proxy) {
152ff1a5810SMarc-André Lureau         if (!qemu_dbus_display1_clipboard_call_request_sync(
153ff1a5810SMarc-André Lureau                 dpy->clipboard_proxy,
154ff1a5810SMarc-André Lureau                 info->selection,
155ff1a5810SMarc-André Lureau                 mimes,
156ff1a5810SMarc-André Lureau                 G_DBUS_CALL_FLAGS_NONE, -1, &mime, &v_data, NULL, &err)) {
157ff1a5810SMarc-André Lureau             error_report("Failed to request clipboard: %s", err->message);
158ff1a5810SMarc-André Lureau             return;
159ff1a5810SMarc-André Lureau         }
160ff1a5810SMarc-André Lureau 
161ff1a5810SMarc-André Lureau         if (g_strcmp0(mime, MIME_TEXT_PLAIN_UTF8)) {
162ff1a5810SMarc-André Lureau             error_report("Unsupported returned MIME: %s", mime);
163ff1a5810SMarc-André Lureau             return;
164ff1a5810SMarc-André Lureau         }
165ff1a5810SMarc-André Lureau 
166ff1a5810SMarc-André Lureau         data = g_variant_get_fixed_array(v_data, &n, 1);
167ff1a5810SMarc-André Lureau         qemu_clipboard_set_data(&dpy->clipboard_peer, info, type,
168ff1a5810SMarc-André Lureau                                 n, data, true);
169ff1a5810SMarc-André Lureau     }
170ff1a5810SMarc-André Lureau }
171ff1a5810SMarc-André Lureau 
172ff1a5810SMarc-André Lureau static void
dbus_clipboard_request_cancelled(DBusClipboardRequest * req)173ff1a5810SMarc-André Lureau dbus_clipboard_request_cancelled(DBusClipboardRequest *req)
174ff1a5810SMarc-André Lureau {
175ff1a5810SMarc-André Lureau     if (!req->invocation) {
176ff1a5810SMarc-André Lureau         return;
177ff1a5810SMarc-André Lureau     }
178ff1a5810SMarc-André Lureau 
179ff1a5810SMarc-André Lureau     g_dbus_method_invocation_return_error(
180ff1a5810SMarc-André Lureau         req->invocation,
181ff1a5810SMarc-André Lureau         DBUS_DISPLAY_ERROR,
182ff1a5810SMarc-André Lureau         DBUS_DISPLAY_ERROR_FAILED,
183ff1a5810SMarc-André Lureau         "Cancelled clipboard request");
184ff1a5810SMarc-André Lureau 
185ff1a5810SMarc-André Lureau     g_clear_object(&req->invocation);
186ff1a5810SMarc-André Lureau     g_source_remove(req->timeout_id);
187ff1a5810SMarc-André Lureau     req->timeout_id = 0;
188ff1a5810SMarc-André Lureau }
189ff1a5810SMarc-André Lureau 
190ff1a5810SMarc-André Lureau static void
dbus_clipboard_unregister_proxy(DBusDisplay * dpy)191ff1a5810SMarc-André Lureau dbus_clipboard_unregister_proxy(DBusDisplay *dpy)
192ff1a5810SMarc-André Lureau {
193ff1a5810SMarc-André Lureau     const char *name = NULL;
194ff1a5810SMarc-André Lureau     int i;
195ff1a5810SMarc-André Lureau 
196ff1a5810SMarc-André Lureau     for (i = 0; i < G_N_ELEMENTS(dpy->clipboard_request); ++i) {
197ff1a5810SMarc-André Lureau         dbus_clipboard_request_cancelled(&dpy->clipboard_request[i]);
198ff1a5810SMarc-André Lureau     }
199ff1a5810SMarc-André Lureau 
200ff1a5810SMarc-André Lureau     if (!dpy->clipboard_proxy) {
201ff1a5810SMarc-André Lureau         return;
202ff1a5810SMarc-André Lureau     }
203ff1a5810SMarc-André Lureau 
204ff1a5810SMarc-André Lureau     name = g_dbus_proxy_get_name(G_DBUS_PROXY(dpy->clipboard_proxy));
205ff1a5810SMarc-André Lureau     trace_dbus_clipboard_unregister(name);
206ff1a5810SMarc-André Lureau     g_clear_object(&dpy->clipboard_proxy);
207ff1a5810SMarc-André Lureau }
208ff1a5810SMarc-André Lureau 
209ff1a5810SMarc-André Lureau static gboolean
dbus_clipboard_register(DBusDisplay * dpy,GDBusMethodInvocation * invocation)210ff1a5810SMarc-André Lureau dbus_clipboard_register(
211ff1a5810SMarc-André Lureau     DBusDisplay *dpy,
212ff1a5810SMarc-André Lureau     GDBusMethodInvocation *invocation)
213ff1a5810SMarc-André Lureau {
214ff1a5810SMarc-André Lureau     g_autoptr(GError) err = NULL;
215ff1a5810SMarc-André Lureau     const char *name = NULL;
216b289bb30SMarc-André Lureau     GDBusConnection *connection = g_dbus_method_invocation_get_connection(invocation);
217ff1a5810SMarc-André Lureau 
218ff1a5810SMarc-André Lureau     if (dpy->clipboard_proxy) {
219ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
220ff1a5810SMarc-André Lureau             invocation,
221ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
222ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
223ff1a5810SMarc-André Lureau             "Clipboard peer already registered!");
224ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
225ff1a5810SMarc-André Lureau     }
226ff1a5810SMarc-André Lureau 
227ff1a5810SMarc-André Lureau     dpy->clipboard_proxy =
228ff1a5810SMarc-André Lureau         qemu_dbus_display1_clipboard_proxy_new_sync(
229b289bb30SMarc-André Lureau             connection,
230ff1a5810SMarc-André Lureau             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
231ff1a5810SMarc-André Lureau             g_dbus_method_invocation_get_sender(invocation),
232ff1a5810SMarc-André Lureau             "/org/qemu/Display1/Clipboard",
233ff1a5810SMarc-André Lureau             NULL,
234ff1a5810SMarc-André Lureau             &err);
235ff1a5810SMarc-André Lureau     if (!dpy->clipboard_proxy) {
236ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
237ff1a5810SMarc-André Lureau             invocation,
238ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
239ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
240ff1a5810SMarc-André Lureau             "Failed to setup proxy: %s", err->message);
241ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
242ff1a5810SMarc-André Lureau     }
243ff1a5810SMarc-André Lureau 
244ff1a5810SMarc-André Lureau     name = g_dbus_proxy_get_name(G_DBUS_PROXY(dpy->clipboard_proxy));
245ff1a5810SMarc-André Lureau     trace_dbus_clipboard_register(name);
246ff1a5810SMarc-André Lureau 
247ff1a5810SMarc-André Lureau     g_object_connect(dpy->clipboard_proxy,
248ff1a5810SMarc-André Lureau                      "swapped-signal::notify::g-name-owner",
249b289bb30SMarc-André Lureau                      dbus_clipboard_unregister_proxy, dpy,
250b289bb30SMarc-André Lureau                      NULL);
251b289bb30SMarc-André Lureau     g_object_connect(connection,
252b289bb30SMarc-André Lureau                      "swapped-signal::closed",
253b289bb30SMarc-André Lureau                      dbus_clipboard_unregister_proxy, dpy,
254ff1a5810SMarc-André Lureau                      NULL);
255ff1a5810SMarc-André Lureau     qemu_clipboard_reset_serial();
256ff1a5810SMarc-André Lureau 
257ff1a5810SMarc-André Lureau     qemu_dbus_display1_clipboard_complete_register(dpy->clipboard, invocation);
258ff1a5810SMarc-André Lureau     return DBUS_METHOD_INVOCATION_HANDLED;
259ff1a5810SMarc-André Lureau }
260ff1a5810SMarc-André Lureau 
261ff1a5810SMarc-André Lureau static gboolean
dbus_clipboard_check_caller(DBusDisplay * dpy,GDBusMethodInvocation * invocation)262ff1a5810SMarc-André Lureau dbus_clipboard_check_caller(DBusDisplay *dpy, GDBusMethodInvocation *invocation)
263ff1a5810SMarc-André Lureau {
264ff1a5810SMarc-André Lureau     if (!dpy->clipboard_proxy ||
265ff1a5810SMarc-André Lureau         g_strcmp0(g_dbus_proxy_get_name(G_DBUS_PROXY(dpy->clipboard_proxy)),
266ff1a5810SMarc-André Lureau                   g_dbus_method_invocation_get_sender(invocation))) {
267ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
268ff1a5810SMarc-André Lureau             invocation,
269ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
270ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
271ff1a5810SMarc-André Lureau             "Unregistered caller");
272ff1a5810SMarc-André Lureau         return FALSE;
273ff1a5810SMarc-André Lureau     }
274ff1a5810SMarc-André Lureau 
275ff1a5810SMarc-André Lureau     return TRUE;
276ff1a5810SMarc-André Lureau }
277ff1a5810SMarc-André Lureau 
278ff1a5810SMarc-André Lureau static gboolean
dbus_clipboard_unregister(DBusDisplay * dpy,GDBusMethodInvocation * invocation)279ff1a5810SMarc-André Lureau dbus_clipboard_unregister(
280ff1a5810SMarc-André Lureau     DBusDisplay *dpy,
281ff1a5810SMarc-André Lureau     GDBusMethodInvocation *invocation)
282ff1a5810SMarc-André Lureau {
283ff1a5810SMarc-André Lureau     if (!dbus_clipboard_check_caller(dpy, invocation)) {
284ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
285ff1a5810SMarc-André Lureau     }
286ff1a5810SMarc-André Lureau 
287ff1a5810SMarc-André Lureau     dbus_clipboard_unregister_proxy(dpy);
288ff1a5810SMarc-André Lureau 
289ff1a5810SMarc-André Lureau     qemu_dbus_display1_clipboard_complete_unregister(
290ff1a5810SMarc-André Lureau         dpy->clipboard, invocation);
291ff1a5810SMarc-André Lureau 
292ff1a5810SMarc-André Lureau     return DBUS_METHOD_INVOCATION_HANDLED;
293ff1a5810SMarc-André Lureau }
294ff1a5810SMarc-André Lureau 
295ff1a5810SMarc-André Lureau static gboolean
dbus_clipboard_grab(DBusDisplay * dpy,GDBusMethodInvocation * invocation,gint arg_selection,guint arg_serial,const gchar * const * arg_mimes)296ff1a5810SMarc-André Lureau dbus_clipboard_grab(
297ff1a5810SMarc-André Lureau     DBusDisplay *dpy,
298ff1a5810SMarc-André Lureau     GDBusMethodInvocation *invocation,
299ff1a5810SMarc-André Lureau     gint arg_selection,
300ff1a5810SMarc-André Lureau     guint arg_serial,
301ff1a5810SMarc-André Lureau     const gchar *const *arg_mimes)
302ff1a5810SMarc-André Lureau {
303ff1a5810SMarc-André Lureau     QemuClipboardSelection s = arg_selection;
304ff1a5810SMarc-André Lureau     g_autoptr(QemuClipboardInfo) info = NULL;
305ff1a5810SMarc-André Lureau 
306ff1a5810SMarc-André Lureau     if (!dbus_clipboard_check_caller(dpy, invocation)) {
307ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
308ff1a5810SMarc-André Lureau     }
309ff1a5810SMarc-André Lureau 
310*2e35439fSMarc-André Lureau     trace_dbus_clipboard_grab(arg_selection, arg_serial);
311*2e35439fSMarc-André Lureau 
312ff1a5810SMarc-André Lureau     if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) {
313ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
314ff1a5810SMarc-André Lureau             invocation,
315ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
316ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
317ff1a5810SMarc-André Lureau             "Invalid clipboard selection: %d", arg_selection);
318ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
319ff1a5810SMarc-André Lureau     }
320ff1a5810SMarc-André Lureau 
321ff1a5810SMarc-André Lureau     info = qemu_clipboard_info_new(&dpy->clipboard_peer, s);
322ff1a5810SMarc-André Lureau     if (g_strv_contains(arg_mimes, MIME_TEXT_PLAIN_UTF8)) {
323ff1a5810SMarc-André Lureau         info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
324ff1a5810SMarc-André Lureau     }
325ff1a5810SMarc-André Lureau     info->serial = arg_serial;
326ff1a5810SMarc-André Lureau     info->has_serial = true;
327ff1a5810SMarc-André Lureau     if (qemu_clipboard_check_serial(info, true)) {
328ff1a5810SMarc-André Lureau         qemu_clipboard_update(info);
329ff1a5810SMarc-André Lureau     } else {
330ff1a5810SMarc-André Lureau         trace_dbus_clipboard_grab_failed();
331ff1a5810SMarc-André Lureau     }
332ff1a5810SMarc-André Lureau 
333ff1a5810SMarc-André Lureau     qemu_dbus_display1_clipboard_complete_grab(dpy->clipboard, invocation);
334ff1a5810SMarc-André Lureau     return DBUS_METHOD_INVOCATION_HANDLED;
335ff1a5810SMarc-André Lureau }
336ff1a5810SMarc-André Lureau 
337ff1a5810SMarc-André Lureau static gboolean
dbus_clipboard_release(DBusDisplay * dpy,GDBusMethodInvocation * invocation,gint arg_selection)338ff1a5810SMarc-André Lureau dbus_clipboard_release(
339ff1a5810SMarc-André Lureau     DBusDisplay *dpy,
340ff1a5810SMarc-André Lureau     GDBusMethodInvocation *invocation,
341ff1a5810SMarc-André Lureau     gint arg_selection)
342ff1a5810SMarc-André Lureau {
343ff1a5810SMarc-André Lureau     if (!dbus_clipboard_check_caller(dpy, invocation)) {
344ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
345ff1a5810SMarc-André Lureau     }
346ff1a5810SMarc-André Lureau 
347ff1a5810SMarc-André Lureau     qemu_clipboard_peer_release(&dpy->clipboard_peer, arg_selection);
348ff1a5810SMarc-André Lureau 
349ff1a5810SMarc-André Lureau     qemu_dbus_display1_clipboard_complete_release(dpy->clipboard, invocation);
350ff1a5810SMarc-André Lureau     return DBUS_METHOD_INVOCATION_HANDLED;
351ff1a5810SMarc-André Lureau }
352ff1a5810SMarc-André Lureau 
353ff1a5810SMarc-André Lureau static gboolean
dbus_clipboard_request_timeout(gpointer user_data)354ff1a5810SMarc-André Lureau dbus_clipboard_request_timeout(gpointer user_data)
355ff1a5810SMarc-André Lureau {
356ff1a5810SMarc-André Lureau     dbus_clipboard_request_cancelled(user_data);
357ff1a5810SMarc-André Lureau     return G_SOURCE_REMOVE;
358ff1a5810SMarc-André Lureau }
359ff1a5810SMarc-André Lureau 
360ff1a5810SMarc-André Lureau static gboolean
dbus_clipboard_request(DBusDisplay * dpy,GDBusMethodInvocation * invocation,gint arg_selection,const gchar * const * arg_mimes)361ff1a5810SMarc-André Lureau dbus_clipboard_request(
362ff1a5810SMarc-André Lureau     DBusDisplay *dpy,
363ff1a5810SMarc-André Lureau     GDBusMethodInvocation *invocation,
364ff1a5810SMarc-André Lureau     gint arg_selection,
365ff1a5810SMarc-André Lureau     const gchar *const *arg_mimes)
366ff1a5810SMarc-André Lureau {
367ff1a5810SMarc-André Lureau     QemuClipboardSelection s = arg_selection;
368ff1a5810SMarc-André Lureau     QemuClipboardType type = QEMU_CLIPBOARD_TYPE_TEXT;
369ff1a5810SMarc-André Lureau     QemuClipboardInfo *info = NULL;
370ff1a5810SMarc-André Lureau 
371ff1a5810SMarc-André Lureau     if (!dbus_clipboard_check_caller(dpy, invocation)) {
372ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
373ff1a5810SMarc-André Lureau     }
374ff1a5810SMarc-André Lureau 
375ff1a5810SMarc-André Lureau     if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) {
376ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
377ff1a5810SMarc-André Lureau             invocation,
378ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
379ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
380ff1a5810SMarc-André Lureau             "Invalid clipboard selection: %d", arg_selection);
381ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
382ff1a5810SMarc-André Lureau     }
383ff1a5810SMarc-André Lureau 
384ff1a5810SMarc-André Lureau     if (dpy->clipboard_request[s].invocation) {
385ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
386ff1a5810SMarc-André Lureau             invocation,
387ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
388ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
389ff1a5810SMarc-André Lureau             "Pending request");
390ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
391ff1a5810SMarc-André Lureau     }
392ff1a5810SMarc-André Lureau 
393ff1a5810SMarc-André Lureau     info = qemu_clipboard_info(s);
394ff1a5810SMarc-André Lureau     if (!info || !info->owner || info->owner == &dpy->clipboard_peer) {
395ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
396ff1a5810SMarc-André Lureau             invocation,
397ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
398ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
399ff1a5810SMarc-André Lureau             "Empty clipboard");
400ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
401ff1a5810SMarc-André Lureau     }
402ff1a5810SMarc-André Lureau 
403ff1a5810SMarc-André Lureau     if (!g_strv_contains(arg_mimes, MIME_TEXT_PLAIN_UTF8) ||
404ff1a5810SMarc-André Lureau         !info->types[type].available) {
405ff1a5810SMarc-André Lureau         g_dbus_method_invocation_return_error(
406ff1a5810SMarc-André Lureau             invocation,
407ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR,
408ff1a5810SMarc-André Lureau             DBUS_DISPLAY_ERROR_FAILED,
409ff1a5810SMarc-André Lureau             "Unhandled MIME types requested");
410ff1a5810SMarc-André Lureau         return DBUS_METHOD_INVOCATION_HANDLED;
411ff1a5810SMarc-André Lureau     }
412ff1a5810SMarc-André Lureau 
413ff1a5810SMarc-André Lureau     if (info->types[type].data) {
414ff1a5810SMarc-André Lureau         dbus_clipboard_complete_request(dpy, invocation, info, type);
415ff1a5810SMarc-André Lureau     } else {
416ff1a5810SMarc-André Lureau         qemu_clipboard_request(info, type);
417ff1a5810SMarc-André Lureau 
418ff1a5810SMarc-André Lureau         dpy->clipboard_request[s].invocation = g_object_ref(invocation);
419ff1a5810SMarc-André Lureau         dpy->clipboard_request[s].type = type;
420ff1a5810SMarc-André Lureau         dpy->clipboard_request[s].timeout_id =
421ff1a5810SMarc-André Lureau             g_timeout_add_seconds(5, dbus_clipboard_request_timeout,
422ff1a5810SMarc-André Lureau                                   &dpy->clipboard_request[s]);
423ff1a5810SMarc-André Lureau     }
424ff1a5810SMarc-André Lureau 
425ff1a5810SMarc-André Lureau     return DBUS_METHOD_INVOCATION_HANDLED;
426ff1a5810SMarc-André Lureau }
427ff1a5810SMarc-André Lureau 
428ff1a5810SMarc-André Lureau void
dbus_clipboard_init(DBusDisplay * dpy)429ff1a5810SMarc-André Lureau dbus_clipboard_init(DBusDisplay *dpy)
430ff1a5810SMarc-André Lureau {
431ff1a5810SMarc-André Lureau     g_autoptr(GDBusObjectSkeleton) clipboard = NULL;
432ff1a5810SMarc-André Lureau 
433ff1a5810SMarc-André Lureau     assert(!dpy->clipboard);
434ff1a5810SMarc-André Lureau 
435ff1a5810SMarc-André Lureau     clipboard = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/Clipboard");
436ff1a5810SMarc-André Lureau     dpy->clipboard = qemu_dbus_display1_clipboard_skeleton_new();
437ff1a5810SMarc-André Lureau     g_object_connect(dpy->clipboard,
438ff1a5810SMarc-André Lureau                      "swapped-signal::handle-register",
439ff1a5810SMarc-André Lureau                      dbus_clipboard_register, dpy,
440ff1a5810SMarc-André Lureau                      "swapped-signal::handle-unregister",
441ff1a5810SMarc-André Lureau                      dbus_clipboard_unregister, dpy,
442ff1a5810SMarc-André Lureau                      "swapped-signal::handle-grab",
443ff1a5810SMarc-André Lureau                      dbus_clipboard_grab, dpy,
444ff1a5810SMarc-André Lureau                      "swapped-signal::handle-release",
445ff1a5810SMarc-André Lureau                      dbus_clipboard_release, dpy,
446ff1a5810SMarc-André Lureau                      "swapped-signal::handle-request",
447ff1a5810SMarc-André Lureau                      dbus_clipboard_request, dpy,
448ff1a5810SMarc-André Lureau                      NULL);
449ff1a5810SMarc-André Lureau 
450ff1a5810SMarc-André Lureau     g_dbus_object_skeleton_add_interface(
451ff1a5810SMarc-André Lureau         G_DBUS_OBJECT_SKELETON(clipboard),
452ff1a5810SMarc-André Lureau         G_DBUS_INTERFACE_SKELETON(dpy->clipboard));
453ff1a5810SMarc-André Lureau     g_dbus_object_manager_server_export(dpy->server, clipboard);
454ff1a5810SMarc-André Lureau     dpy->clipboard_peer.name = "dbus";
455ff1a5810SMarc-André Lureau     dpy->clipboard_peer.notifier.notify = dbus_clipboard_notify;
456ff1a5810SMarc-André Lureau     dpy->clipboard_peer.request = dbus_clipboard_qemu_request;
457ff1a5810SMarc-André Lureau     qemu_clipboard_peer_register(&dpy->clipboard_peer);
458ff1a5810SMarc-André Lureau }
459