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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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