|  | #include "qemu/osdep.h" | 
|  | #include "ui/clipboard.h" | 
|  | #include "trace.h" | 
|  |  | 
|  | static NotifierList clipboard_notifiers = | 
|  | NOTIFIER_LIST_INITIALIZER(clipboard_notifiers); | 
|  |  | 
|  | static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; | 
|  |  | 
|  | void qemu_clipboard_peer_register(QemuClipboardPeer *peer) | 
|  | { | 
|  | notifier_list_add(&clipboard_notifiers, &peer->notifier); | 
|  | } | 
|  |  | 
|  | void qemu_clipboard_peer_unregister(QemuClipboardPeer *peer) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { | 
|  | qemu_clipboard_peer_release(peer, i); | 
|  | } | 
|  | notifier_remove(&peer->notifier); | 
|  | } | 
|  |  | 
|  | bool qemu_clipboard_peer_owns(QemuClipboardPeer *peer, | 
|  | QemuClipboardSelection selection) | 
|  | { | 
|  | QemuClipboardInfo *info = qemu_clipboard_info(selection); | 
|  |  | 
|  | return info && info->owner == peer; | 
|  | } | 
|  |  | 
|  | void qemu_clipboard_peer_release(QemuClipboardPeer *peer, | 
|  | QemuClipboardSelection selection) | 
|  | { | 
|  | g_autoptr(QemuClipboardInfo) info = NULL; | 
|  |  | 
|  | if (qemu_clipboard_peer_owns(peer, selection)) { | 
|  | /* set empty clipboard info */ | 
|  | info = qemu_clipboard_info_new(NULL, selection); | 
|  | qemu_clipboard_update(info); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client) | 
|  | { | 
|  | bool ok; | 
|  |  | 
|  | if (!info->has_serial || | 
|  | !cbinfo[info->selection] || | 
|  | !cbinfo[info->selection]->has_serial) { | 
|  | trace_clipboard_check_serial(-1, -1, true); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (client) { | 
|  | ok = info->serial >= cbinfo[info->selection]->serial; | 
|  | } else { | 
|  | ok = info->serial > cbinfo[info->selection]->serial; | 
|  | } | 
|  |  | 
|  | trace_clipboard_check_serial(cbinfo[info->selection]->serial, info->serial, ok); | 
|  | return ok; | 
|  | } | 
|  |  | 
|  | void qemu_clipboard_update(QemuClipboardInfo *info) | 
|  | { | 
|  | uint32_t type; | 
|  | QemuClipboardNotify notify = { | 
|  | .type = QEMU_CLIPBOARD_UPDATE_INFO, | 
|  | .info = info, | 
|  | }; | 
|  | assert(info->selection < QEMU_CLIPBOARD_SELECTION__COUNT); | 
|  |  | 
|  | for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { | 
|  | /* | 
|  | * If data is missing, the clipboard owner's 'request' callback needs to | 
|  | * be set. Otherwise, there is no way to get the clipboard data and | 
|  | * qemu_clipboard_request() cannot be called. | 
|  | */ | 
|  | if (info->types[type].available && !info->types[type].data) { | 
|  | assert(info->owner && info->owner->request); | 
|  | } | 
|  | } | 
|  |  | 
|  | notifier_list_notify(&clipboard_notifiers, ¬ify); | 
|  |  | 
|  | if (cbinfo[info->selection] != info) { | 
|  | qemu_clipboard_info_unref(cbinfo[info->selection]); | 
|  | cbinfo[info->selection] = qemu_clipboard_info_ref(info); | 
|  | } | 
|  | } | 
|  |  | 
|  | QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection) | 
|  | { | 
|  | assert(selection < QEMU_CLIPBOARD_SELECTION__COUNT); | 
|  |  | 
|  | return cbinfo[selection]; | 
|  | } | 
|  |  | 
|  | QemuClipboardInfo *qemu_clipboard_info_new(QemuClipboardPeer *owner, | 
|  | QemuClipboardSelection selection) | 
|  | { | 
|  | QemuClipboardInfo *info = g_new0(QemuClipboardInfo, 1); | 
|  |  | 
|  | info->owner = owner; | 
|  | info->selection = selection; | 
|  | info->refcount = 1; | 
|  |  | 
|  | return info; | 
|  | } | 
|  |  | 
|  | QemuClipboardInfo *qemu_clipboard_info_ref(QemuClipboardInfo *info) | 
|  | { | 
|  | info->refcount++; | 
|  | return info; | 
|  | } | 
|  |  | 
|  | void qemu_clipboard_info_unref(QemuClipboardInfo *info) | 
|  | { | 
|  | uint32_t type; | 
|  |  | 
|  | if (!info) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | info->refcount--; | 
|  | if (info->refcount > 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { | 
|  | g_free(info->types[type].data); | 
|  | } | 
|  | g_free(info); | 
|  | } | 
|  |  | 
|  | void qemu_clipboard_request(QemuClipboardInfo *info, | 
|  | QemuClipboardType type) | 
|  | { | 
|  | if (info->types[type].data || | 
|  | info->types[type].requested || | 
|  | !info->types[type].available || | 
|  | !info->owner) | 
|  | return; | 
|  |  | 
|  | assert(info->owner->request); | 
|  |  | 
|  | info->types[type].requested = true; | 
|  | info->owner->request(info, type); | 
|  | } | 
|  |  | 
|  | void qemu_clipboard_reset_serial(void) | 
|  | { | 
|  | QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_RESET_SERIAL }; | 
|  | int i; | 
|  |  | 
|  | trace_clipboard_reset_serial(); | 
|  |  | 
|  | for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { | 
|  | QemuClipboardInfo *info = qemu_clipboard_info(i); | 
|  | if (info) { | 
|  | info->serial = 0; | 
|  | } | 
|  | } | 
|  | notifier_list_notify(&clipboard_notifiers, ¬ify); | 
|  | } | 
|  |  | 
|  | void qemu_clipboard_set_data(QemuClipboardPeer *peer, | 
|  | QemuClipboardInfo *info, | 
|  | QemuClipboardType type, | 
|  | uint32_t size, | 
|  | const void *data, | 
|  | bool update) | 
|  | { | 
|  | if (!info || | 
|  | info->owner != peer) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_free(info->types[type].data); | 
|  | if (size) { | 
|  | info->types[type].data = g_memdup2(data, size); | 
|  | info->types[type].size = size; | 
|  | info->types[type].available = true; | 
|  | } else { | 
|  | info->types[type].data = NULL; | 
|  | info->types[type].size = 0; | 
|  | info->types[type].available = false; | 
|  | } | 
|  |  | 
|  | if (update) { | 
|  | qemu_clipboard_update(info); | 
|  | } | 
|  | } |