| #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); |
| } |
| } |