1 #include "qemu/osdep.h" 2 #include "ui/clipboard.h" 3 #include "trace.h" 4 5 static NotifierList clipboard_notifiers = 6 NOTIFIER_LIST_INITIALIZER(clipboard_notifiers); 7 8 static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; 9 10 void qemu_clipboard_peer_register(QemuClipboardPeer *peer) 11 { 12 notifier_list_add(&clipboard_notifiers, &peer->notifier); 13 } 14 15 void qemu_clipboard_peer_unregister(QemuClipboardPeer *peer) 16 { 17 int i; 18 19 for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { 20 qemu_clipboard_peer_release(peer, i); 21 } 22 notifier_remove(&peer->notifier); 23 } 24 25 bool qemu_clipboard_peer_owns(QemuClipboardPeer *peer, 26 QemuClipboardSelection selection) 27 { 28 QemuClipboardInfo *info = qemu_clipboard_info(selection); 29 30 return info && info->owner == peer; 31 } 32 33 void qemu_clipboard_peer_release(QemuClipboardPeer *peer, 34 QemuClipboardSelection selection) 35 { 36 g_autoptr(QemuClipboardInfo) info = NULL; 37 38 if (qemu_clipboard_peer_owns(peer, selection)) { 39 /* set empty clipboard info */ 40 info = qemu_clipboard_info_new(NULL, selection); 41 qemu_clipboard_update(info); 42 } 43 } 44 45 bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client) 46 { 47 bool ok; 48 49 if (!info->has_serial || 50 !cbinfo[info->selection] || 51 !cbinfo[info->selection]->has_serial) { 52 trace_clipboard_check_serial(-1, -1, true); 53 return true; 54 } 55 56 if (client) { 57 ok = info->serial >= cbinfo[info->selection]->serial; 58 } else { 59 ok = info->serial > cbinfo[info->selection]->serial; 60 } 61 62 trace_clipboard_check_serial(cbinfo[info->selection]->serial, info->serial, ok); 63 return ok; 64 } 65 66 void qemu_clipboard_update(QemuClipboardInfo *info) 67 { 68 uint32_t type; 69 QemuClipboardNotify notify = { 70 .type = QEMU_CLIPBOARD_UPDATE_INFO, 71 .info = info, 72 }; 73 assert(info->selection < QEMU_CLIPBOARD_SELECTION__COUNT); 74 75 for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { 76 /* 77 * If data is missing, the clipboard owner's 'request' callback needs to 78 * be set. Otherwise, there is no way to get the clipboard data and 79 * qemu_clipboard_request() cannot be called. 80 */ 81 if (info->types[type].available && !info->types[type].data) { 82 assert(info->owner && info->owner->request); 83 } 84 } 85 86 notifier_list_notify(&clipboard_notifiers, ¬ify); 87 88 if (cbinfo[info->selection] != info) { 89 qemu_clipboard_info_unref(cbinfo[info->selection]); 90 cbinfo[info->selection] = qemu_clipboard_info_ref(info); 91 } 92 } 93 94 QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection) 95 { 96 assert(selection < QEMU_CLIPBOARD_SELECTION__COUNT); 97 98 return cbinfo[selection]; 99 } 100 101 QemuClipboardInfo *qemu_clipboard_info_new(QemuClipboardPeer *owner, 102 QemuClipboardSelection selection) 103 { 104 QemuClipboardInfo *info = g_new0(QemuClipboardInfo, 1); 105 106 info->owner = owner; 107 info->selection = selection; 108 info->refcount = 1; 109 110 return info; 111 } 112 113 QemuClipboardInfo *qemu_clipboard_info_ref(QemuClipboardInfo *info) 114 { 115 info->refcount++; 116 return info; 117 } 118 119 void qemu_clipboard_info_unref(QemuClipboardInfo *info) 120 { 121 uint32_t type; 122 123 if (!info) { 124 return; 125 } 126 127 info->refcount--; 128 if (info->refcount > 0) { 129 return; 130 } 131 132 for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { 133 g_free(info->types[type].data); 134 } 135 g_free(info); 136 } 137 138 void qemu_clipboard_request(QemuClipboardInfo *info, 139 QemuClipboardType type) 140 { 141 if (info->types[type].data || 142 info->types[type].requested || 143 !info->types[type].available || 144 !info->owner) 145 return; 146 147 assert(info->owner->request); 148 149 info->types[type].requested = true; 150 info->owner->request(info, type); 151 } 152 153 void qemu_clipboard_reset_serial(void) 154 { 155 QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_RESET_SERIAL }; 156 int i; 157 158 for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { 159 QemuClipboardInfo *info = qemu_clipboard_info(i); 160 if (info) { 161 info->serial = 0; 162 } 163 } 164 notifier_list_notify(&clipboard_notifiers, ¬ify); 165 } 166 167 void qemu_clipboard_set_data(QemuClipboardPeer *peer, 168 QemuClipboardInfo *info, 169 QemuClipboardType type, 170 uint32_t size, 171 const void *data, 172 bool update) 173 { 174 if (!info || 175 info->owner != peer) { 176 return; 177 } 178 179 g_free(info->types[type].data); 180 if (size) { 181 info->types[type].data = g_memdup2(data, size); 182 info->types[type].size = size; 183 info->types[type].available = true; 184 } else { 185 info->types[type].data = NULL; 186 info->types[type].size = 0; 187 info->types[type].available = false; 188 } 189 190 if (update) { 191 qemu_clipboard_update(info); 192 } 193 } 194