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 144ff1a5810SMarc-André Lureau if (type != QEMU_CLIPBOARD_TYPE_TEXT) { 145ff1a5810SMarc-André Lureau /* unsupported atm */ 146ff1a5810SMarc-André Lureau return; 147ff1a5810SMarc-André Lureau } 148ff1a5810SMarc-André Lureau 149ff1a5810SMarc-André Lureau if (dpy->clipboard_proxy) { 150ff1a5810SMarc-André Lureau if (!qemu_dbus_display1_clipboard_call_request_sync( 151ff1a5810SMarc-André Lureau dpy->clipboard_proxy, 152ff1a5810SMarc-André Lureau info->selection, 153ff1a5810SMarc-André Lureau mimes, 154ff1a5810SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, &mime, &v_data, NULL, &err)) { 155ff1a5810SMarc-André Lureau error_report("Failed to request clipboard: %s", err->message); 156ff1a5810SMarc-André Lureau return; 157ff1a5810SMarc-André Lureau } 158ff1a5810SMarc-André Lureau 159ff1a5810SMarc-André Lureau if (g_strcmp0(mime, MIME_TEXT_PLAIN_UTF8)) { 160ff1a5810SMarc-André Lureau error_report("Unsupported returned MIME: %s", mime); 161ff1a5810SMarc-André Lureau return; 162ff1a5810SMarc-André Lureau } 163ff1a5810SMarc-André Lureau 164ff1a5810SMarc-André Lureau data = g_variant_get_fixed_array(v_data, &n, 1); 165ff1a5810SMarc-André Lureau qemu_clipboard_set_data(&dpy->clipboard_peer, info, type, 166ff1a5810SMarc-André Lureau n, data, true); 167ff1a5810SMarc-André Lureau } 168ff1a5810SMarc-André Lureau } 169ff1a5810SMarc-André Lureau 170ff1a5810SMarc-André Lureau static void 171ff1a5810SMarc-André Lureau dbus_clipboard_request_cancelled(DBusClipboardRequest *req) 172ff1a5810SMarc-André Lureau { 173ff1a5810SMarc-André Lureau if (!req->invocation) { 174ff1a5810SMarc-André Lureau return; 175ff1a5810SMarc-André Lureau } 176ff1a5810SMarc-André Lureau 177ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 178ff1a5810SMarc-André Lureau req->invocation, 179ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 180ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 181ff1a5810SMarc-André Lureau "Cancelled clipboard request"); 182ff1a5810SMarc-André Lureau 183ff1a5810SMarc-André Lureau g_clear_object(&req->invocation); 184ff1a5810SMarc-André Lureau g_source_remove(req->timeout_id); 185ff1a5810SMarc-André Lureau req->timeout_id = 0; 186ff1a5810SMarc-André Lureau } 187ff1a5810SMarc-André Lureau 188ff1a5810SMarc-André Lureau static void 189ff1a5810SMarc-André Lureau dbus_clipboard_unregister_proxy(DBusDisplay *dpy) 190ff1a5810SMarc-André Lureau { 191ff1a5810SMarc-André Lureau const char *name = NULL; 192ff1a5810SMarc-André Lureau int i; 193ff1a5810SMarc-André Lureau 194ff1a5810SMarc-André Lureau for (i = 0; i < G_N_ELEMENTS(dpy->clipboard_request); ++i) { 195ff1a5810SMarc-André Lureau dbus_clipboard_request_cancelled(&dpy->clipboard_request[i]); 196ff1a5810SMarc-André Lureau } 197ff1a5810SMarc-André Lureau 198ff1a5810SMarc-André Lureau if (!dpy->clipboard_proxy) { 199ff1a5810SMarc-André Lureau return; 200ff1a5810SMarc-André Lureau } 201ff1a5810SMarc-André Lureau 202ff1a5810SMarc-André Lureau name = g_dbus_proxy_get_name(G_DBUS_PROXY(dpy->clipboard_proxy)); 203ff1a5810SMarc-André Lureau trace_dbus_clipboard_unregister(name); 204ff1a5810SMarc-André Lureau g_clear_object(&dpy->clipboard_proxy); 205ff1a5810SMarc-André Lureau } 206ff1a5810SMarc-André Lureau 207ff1a5810SMarc-André Lureau static gboolean 208ff1a5810SMarc-André Lureau dbus_clipboard_register( 209ff1a5810SMarc-André Lureau DBusDisplay *dpy, 210ff1a5810SMarc-André Lureau GDBusMethodInvocation *invocation) 211ff1a5810SMarc-André Lureau { 212ff1a5810SMarc-André Lureau g_autoptr(GError) err = NULL; 213ff1a5810SMarc-André Lureau const char *name = NULL; 214*b289bb30SMarc-André Lureau GDBusConnection *connection = g_dbus_method_invocation_get_connection(invocation); 215ff1a5810SMarc-André Lureau 216ff1a5810SMarc-André Lureau if (dpy->clipboard_proxy) { 217ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 218ff1a5810SMarc-André Lureau invocation, 219ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 220ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 221ff1a5810SMarc-André Lureau "Clipboard peer already registered!"); 222ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 223ff1a5810SMarc-André Lureau } 224ff1a5810SMarc-André Lureau 225ff1a5810SMarc-André Lureau dpy->clipboard_proxy = 226ff1a5810SMarc-André Lureau qemu_dbus_display1_clipboard_proxy_new_sync( 227*b289bb30SMarc-André Lureau connection, 228ff1a5810SMarc-André Lureau G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 229ff1a5810SMarc-André Lureau g_dbus_method_invocation_get_sender(invocation), 230ff1a5810SMarc-André Lureau "/org/qemu/Display1/Clipboard", 231ff1a5810SMarc-André Lureau NULL, 232ff1a5810SMarc-André Lureau &err); 233ff1a5810SMarc-André Lureau if (!dpy->clipboard_proxy) { 234ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 235ff1a5810SMarc-André Lureau invocation, 236ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 237ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 238ff1a5810SMarc-André Lureau "Failed to setup proxy: %s", err->message); 239ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 240ff1a5810SMarc-André Lureau } 241ff1a5810SMarc-André Lureau 242ff1a5810SMarc-André Lureau name = g_dbus_proxy_get_name(G_DBUS_PROXY(dpy->clipboard_proxy)); 243ff1a5810SMarc-André Lureau trace_dbus_clipboard_register(name); 244ff1a5810SMarc-André Lureau 245ff1a5810SMarc-André Lureau g_object_connect(dpy->clipboard_proxy, 246ff1a5810SMarc-André Lureau "swapped-signal::notify::g-name-owner", 247*b289bb30SMarc-André Lureau dbus_clipboard_unregister_proxy, dpy, 248*b289bb30SMarc-André Lureau NULL); 249*b289bb30SMarc-André Lureau g_object_connect(connection, 250*b289bb30SMarc-André Lureau "swapped-signal::closed", 251*b289bb30SMarc-André Lureau dbus_clipboard_unregister_proxy, dpy, 252ff1a5810SMarc-André Lureau NULL); 253ff1a5810SMarc-André Lureau qemu_clipboard_reset_serial(); 254ff1a5810SMarc-André Lureau 255ff1a5810SMarc-André Lureau qemu_dbus_display1_clipboard_complete_register(dpy->clipboard, invocation); 256ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 257ff1a5810SMarc-André Lureau } 258ff1a5810SMarc-André Lureau 259ff1a5810SMarc-André Lureau static gboolean 260ff1a5810SMarc-André Lureau dbus_clipboard_check_caller(DBusDisplay *dpy, GDBusMethodInvocation *invocation) 261ff1a5810SMarc-André Lureau { 262ff1a5810SMarc-André Lureau if (!dpy->clipboard_proxy || 263ff1a5810SMarc-André Lureau g_strcmp0(g_dbus_proxy_get_name(G_DBUS_PROXY(dpy->clipboard_proxy)), 264ff1a5810SMarc-André Lureau g_dbus_method_invocation_get_sender(invocation))) { 265ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 266ff1a5810SMarc-André Lureau invocation, 267ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 268ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 269ff1a5810SMarc-André Lureau "Unregistered caller"); 270ff1a5810SMarc-André Lureau return FALSE; 271ff1a5810SMarc-André Lureau } 272ff1a5810SMarc-André Lureau 273ff1a5810SMarc-André Lureau return TRUE; 274ff1a5810SMarc-André Lureau } 275ff1a5810SMarc-André Lureau 276ff1a5810SMarc-André Lureau static gboolean 277ff1a5810SMarc-André Lureau dbus_clipboard_unregister( 278ff1a5810SMarc-André Lureau DBusDisplay *dpy, 279ff1a5810SMarc-André Lureau GDBusMethodInvocation *invocation) 280ff1a5810SMarc-André Lureau { 281ff1a5810SMarc-André Lureau if (!dbus_clipboard_check_caller(dpy, invocation)) { 282ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 283ff1a5810SMarc-André Lureau } 284ff1a5810SMarc-André Lureau 285ff1a5810SMarc-André Lureau dbus_clipboard_unregister_proxy(dpy); 286ff1a5810SMarc-André Lureau 287ff1a5810SMarc-André Lureau qemu_dbus_display1_clipboard_complete_unregister( 288ff1a5810SMarc-André Lureau dpy->clipboard, invocation); 289ff1a5810SMarc-André Lureau 290ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 291ff1a5810SMarc-André Lureau } 292ff1a5810SMarc-André Lureau 293ff1a5810SMarc-André Lureau static gboolean 294ff1a5810SMarc-André Lureau dbus_clipboard_grab( 295ff1a5810SMarc-André Lureau DBusDisplay *dpy, 296ff1a5810SMarc-André Lureau GDBusMethodInvocation *invocation, 297ff1a5810SMarc-André Lureau gint arg_selection, 298ff1a5810SMarc-André Lureau guint arg_serial, 299ff1a5810SMarc-André Lureau const gchar *const *arg_mimes) 300ff1a5810SMarc-André Lureau { 301ff1a5810SMarc-André Lureau QemuClipboardSelection s = arg_selection; 302ff1a5810SMarc-André Lureau g_autoptr(QemuClipboardInfo) info = NULL; 303ff1a5810SMarc-André Lureau 304ff1a5810SMarc-André Lureau if (!dbus_clipboard_check_caller(dpy, invocation)) { 305ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 306ff1a5810SMarc-André Lureau } 307ff1a5810SMarc-André Lureau 308ff1a5810SMarc-André Lureau if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) { 309ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 310ff1a5810SMarc-André Lureau invocation, 311ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 312ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 313ff1a5810SMarc-André Lureau "Invalid clipboard selection: %d", arg_selection); 314ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 315ff1a5810SMarc-André Lureau } 316ff1a5810SMarc-André Lureau 317ff1a5810SMarc-André Lureau info = qemu_clipboard_info_new(&dpy->clipboard_peer, s); 318ff1a5810SMarc-André Lureau if (g_strv_contains(arg_mimes, MIME_TEXT_PLAIN_UTF8)) { 319ff1a5810SMarc-André Lureau info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; 320ff1a5810SMarc-André Lureau } 321ff1a5810SMarc-André Lureau info->serial = arg_serial; 322ff1a5810SMarc-André Lureau info->has_serial = true; 323ff1a5810SMarc-André Lureau if (qemu_clipboard_check_serial(info, true)) { 324ff1a5810SMarc-André Lureau qemu_clipboard_update(info); 325ff1a5810SMarc-André Lureau } else { 326ff1a5810SMarc-André Lureau trace_dbus_clipboard_grab_failed(); 327ff1a5810SMarc-André Lureau } 328ff1a5810SMarc-André Lureau 329ff1a5810SMarc-André Lureau qemu_dbus_display1_clipboard_complete_grab(dpy->clipboard, invocation); 330ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 331ff1a5810SMarc-André Lureau } 332ff1a5810SMarc-André Lureau 333ff1a5810SMarc-André Lureau static gboolean 334ff1a5810SMarc-André Lureau dbus_clipboard_release( 335ff1a5810SMarc-André Lureau DBusDisplay *dpy, 336ff1a5810SMarc-André Lureau GDBusMethodInvocation *invocation, 337ff1a5810SMarc-André Lureau gint arg_selection) 338ff1a5810SMarc-André Lureau { 339ff1a5810SMarc-André Lureau if (!dbus_clipboard_check_caller(dpy, invocation)) { 340ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 341ff1a5810SMarc-André Lureau } 342ff1a5810SMarc-André Lureau 343ff1a5810SMarc-André Lureau qemu_clipboard_peer_release(&dpy->clipboard_peer, arg_selection); 344ff1a5810SMarc-André Lureau 345ff1a5810SMarc-André Lureau qemu_dbus_display1_clipboard_complete_release(dpy->clipboard, invocation); 346ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 347ff1a5810SMarc-André Lureau } 348ff1a5810SMarc-André Lureau 349ff1a5810SMarc-André Lureau static gboolean 350ff1a5810SMarc-André Lureau dbus_clipboard_request_timeout(gpointer user_data) 351ff1a5810SMarc-André Lureau { 352ff1a5810SMarc-André Lureau dbus_clipboard_request_cancelled(user_data); 353ff1a5810SMarc-André Lureau return G_SOURCE_REMOVE; 354ff1a5810SMarc-André Lureau } 355ff1a5810SMarc-André Lureau 356ff1a5810SMarc-André Lureau static gboolean 357ff1a5810SMarc-André Lureau dbus_clipboard_request( 358ff1a5810SMarc-André Lureau DBusDisplay *dpy, 359ff1a5810SMarc-André Lureau GDBusMethodInvocation *invocation, 360ff1a5810SMarc-André Lureau gint arg_selection, 361ff1a5810SMarc-André Lureau const gchar *const *arg_mimes) 362ff1a5810SMarc-André Lureau { 363ff1a5810SMarc-André Lureau QemuClipboardSelection s = arg_selection; 364ff1a5810SMarc-André Lureau QemuClipboardType type = QEMU_CLIPBOARD_TYPE_TEXT; 365ff1a5810SMarc-André Lureau QemuClipboardInfo *info = NULL; 366ff1a5810SMarc-André Lureau 367ff1a5810SMarc-André Lureau if (!dbus_clipboard_check_caller(dpy, invocation)) { 368ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 369ff1a5810SMarc-André Lureau } 370ff1a5810SMarc-André Lureau 371ff1a5810SMarc-André Lureau if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) { 372ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 373ff1a5810SMarc-André Lureau invocation, 374ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 375ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 376ff1a5810SMarc-André Lureau "Invalid clipboard selection: %d", arg_selection); 377ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 378ff1a5810SMarc-André Lureau } 379ff1a5810SMarc-André Lureau 380ff1a5810SMarc-André Lureau if (dpy->clipboard_request[s].invocation) { 381ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 382ff1a5810SMarc-André Lureau invocation, 383ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 384ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 385ff1a5810SMarc-André Lureau "Pending request"); 386ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 387ff1a5810SMarc-André Lureau } 388ff1a5810SMarc-André Lureau 389ff1a5810SMarc-André Lureau info = qemu_clipboard_info(s); 390ff1a5810SMarc-André Lureau if (!info || !info->owner || info->owner == &dpy->clipboard_peer) { 391ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 392ff1a5810SMarc-André Lureau invocation, 393ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 394ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 395ff1a5810SMarc-André Lureau "Empty clipboard"); 396ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 397ff1a5810SMarc-André Lureau } 398ff1a5810SMarc-André Lureau 399ff1a5810SMarc-André Lureau if (!g_strv_contains(arg_mimes, MIME_TEXT_PLAIN_UTF8) || 400ff1a5810SMarc-André Lureau !info->types[type].available) { 401ff1a5810SMarc-André Lureau g_dbus_method_invocation_return_error( 402ff1a5810SMarc-André Lureau invocation, 403ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR, 404ff1a5810SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED, 405ff1a5810SMarc-André Lureau "Unhandled MIME types requested"); 406ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 407ff1a5810SMarc-André Lureau } 408ff1a5810SMarc-André Lureau 409ff1a5810SMarc-André Lureau if (info->types[type].data) { 410ff1a5810SMarc-André Lureau dbus_clipboard_complete_request(dpy, invocation, info, type); 411ff1a5810SMarc-André Lureau } else { 412ff1a5810SMarc-André Lureau qemu_clipboard_request(info, type); 413ff1a5810SMarc-André Lureau 414ff1a5810SMarc-André Lureau dpy->clipboard_request[s].invocation = g_object_ref(invocation); 415ff1a5810SMarc-André Lureau dpy->clipboard_request[s].type = type; 416ff1a5810SMarc-André Lureau dpy->clipboard_request[s].timeout_id = 417ff1a5810SMarc-André Lureau g_timeout_add_seconds(5, dbus_clipboard_request_timeout, 418ff1a5810SMarc-André Lureau &dpy->clipboard_request[s]); 419ff1a5810SMarc-André Lureau } 420ff1a5810SMarc-André Lureau 421ff1a5810SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED; 422ff1a5810SMarc-André Lureau } 423ff1a5810SMarc-André Lureau 424ff1a5810SMarc-André Lureau void 425ff1a5810SMarc-André Lureau dbus_clipboard_init(DBusDisplay *dpy) 426ff1a5810SMarc-André Lureau { 427ff1a5810SMarc-André Lureau g_autoptr(GDBusObjectSkeleton) clipboard = NULL; 428ff1a5810SMarc-André Lureau 429ff1a5810SMarc-André Lureau assert(!dpy->clipboard); 430ff1a5810SMarc-André Lureau 431ff1a5810SMarc-André Lureau clipboard = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/Clipboard"); 432ff1a5810SMarc-André Lureau dpy->clipboard = qemu_dbus_display1_clipboard_skeleton_new(); 433ff1a5810SMarc-André Lureau g_object_connect(dpy->clipboard, 434ff1a5810SMarc-André Lureau "swapped-signal::handle-register", 435ff1a5810SMarc-André Lureau dbus_clipboard_register, dpy, 436ff1a5810SMarc-André Lureau "swapped-signal::handle-unregister", 437ff1a5810SMarc-André Lureau dbus_clipboard_unregister, dpy, 438ff1a5810SMarc-André Lureau "swapped-signal::handle-grab", 439ff1a5810SMarc-André Lureau dbus_clipboard_grab, dpy, 440ff1a5810SMarc-André Lureau "swapped-signal::handle-release", 441ff1a5810SMarc-André Lureau dbus_clipboard_release, dpy, 442ff1a5810SMarc-André Lureau "swapped-signal::handle-request", 443ff1a5810SMarc-André Lureau dbus_clipboard_request, dpy, 444ff1a5810SMarc-André Lureau NULL); 445ff1a5810SMarc-André Lureau 446ff1a5810SMarc-André Lureau g_dbus_object_skeleton_add_interface( 447ff1a5810SMarc-André Lureau G_DBUS_OBJECT_SKELETON(clipboard), 448ff1a5810SMarc-André Lureau G_DBUS_INTERFACE_SKELETON(dpy->clipboard)); 449ff1a5810SMarc-André Lureau g_dbus_object_manager_server_export(dpy->server, clipboard); 450ff1a5810SMarc-André Lureau dpy->clipboard_peer.name = "dbus"; 451ff1a5810SMarc-André Lureau dpy->clipboard_peer.notifier.notify = dbus_clipboard_notify; 452ff1a5810SMarc-André Lureau dpy->clipboard_peer.request = dbus_clipboard_qemu_request; 453ff1a5810SMarc-André Lureau qemu_clipboard_peer_register(&dpy->clipboard_peer); 454ff1a5810SMarc-André Lureau } 455