master
c 258 lines 6.8 KB
Raw
1 #include "qemu/osdep.h"
2 #include "system/runstate.h"
3 #include "ui/clipboard.h"
4 #include "trace.h"
5
6 static NotifierList clipboard_notifiers =
7 NOTIFIER_LIST_INITIALIZER(clipboard_notifiers);
8
9 static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT];
10
11 static VMChangeStateEntry *cb_change_state_entry = NULL;
12
13 static bool cb_reset_serial_on_resume = false;
14
15 static const VMStateDescription vmstate_cbcontent = {
16 .name = "clipboard/content",
17 .version_id = 0,
18 .minimum_version_id = 0,
19 .fields = (const VMStateField[]) {
20 VMSTATE_BOOL(available, QemuClipboardContent),
21 VMSTATE_BOOL(requested, QemuClipboardContent),
22 VMSTATE_UINT32(size, QemuClipboardContent),
23 VMSTATE_VBUFFER_ALLOC_UINT32(data, QemuClipboardContent, 0, 0, size),
24 VMSTATE_END_OF_LIST()
25 }
26 };
27
28 const VMStateDescription vmstate_cbinfo = {
29 .name = "clipboard",
30 .version_id = 0,
31 .minimum_version_id = 0,
32 .fields = (const VMStateField[]) {
33 VMSTATE_INT32(selection, QemuClipboardInfo),
34 VMSTATE_BOOL(has_serial, QemuClipboardInfo),
35 VMSTATE_UINT32(serial, QemuClipboardInfo),
36 VMSTATE_STRUCT_ARRAY(types, QemuClipboardInfo, QEMU_CLIPBOARD_TYPE__COUNT, 0, vmstate_cbcontent, QemuClipboardContent),
37 VMSTATE_END_OF_LIST()
38 }
39 };
40
41 static void qemu_clipboard_change_state(void *opaque, bool running, RunState state)
42 {
43 int i;
44
45 if (!running) {
46 return;
47 }
48
49 if (cb_reset_serial_on_resume) {
50 cb_reset_serial_on_resume = false;
51 qemu_clipboard_reset_serial();
52 }
53
54 for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) {
55 if (cbinfo[i]) {
56 qemu_clipboard_update(cbinfo[i]);
57 }
58 }
59
60 }
61
62 void qemu_clipboard_peer_register(QemuClipboardPeer *peer)
63 {
64 if (cb_change_state_entry == NULL) {
65 cb_change_state_entry = qemu_add_vm_change_state_handler(qemu_clipboard_change_state, NULL);
66 }
67
68 notifier_list_add(&clipboard_notifiers, &peer->notifier);
69 }
70
71 void qemu_clipboard_peer_unregister(QemuClipboardPeer *peer)
72 {
73 int i;
74
75 for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) {
76 qemu_clipboard_peer_release(peer, i);
77 }
78 notifier_remove(&peer->notifier);
79 }
80
81 bool qemu_clipboard_peer_owns(QemuClipboardPeer *peer,
82 QemuClipboardSelection selection)
83 {
84 QemuClipboardInfo *info = qemu_clipboard_info(selection);
85
86 return info && info->owner == peer;
87 }
88
89 void qemu_clipboard_peer_release(QemuClipboardPeer *peer,
90 QemuClipboardSelection selection)
91 {
92 g_autoptr(QemuClipboardInfo) info = NULL;
93
94 if (qemu_clipboard_peer_owns(peer, selection)) {
95 /* set empty clipboard info */
96 info = qemu_clipboard_info_new(NULL, selection);
97 qemu_clipboard_update(info);
98 }
99 }
100
101 bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client)
102 {
103 bool ok;
104
105 if (!info->has_serial ||
106 !cbinfo[info->selection] ||
107 !cbinfo[info->selection]->has_serial) {
108 trace_clipboard_check_serial(-1, -1, true);
109 return true;
110 }
111
112 if (client) {
113 ok = info->serial >= cbinfo[info->selection]->serial;
114 } else {
115 ok = info->serial > cbinfo[info->selection]->serial;
116 }
117
118 trace_clipboard_check_serial(cbinfo[info->selection]->serial, info->serial, ok);
119 return ok;
120 }
121
122 void qemu_clipboard_update(QemuClipboardInfo *info)
123 {
124 uint32_t type;
125 QemuClipboardNotify notify = {
126 .type = QEMU_CLIPBOARD_UPDATE_INFO,
127 .info = info,
128 };
129 assert(info->selection < QEMU_CLIPBOARD_SELECTION__COUNT);
130
131 for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) {
132 /*
133 * If data is missing, the clipboard owner's 'request' callback needs to
134 * be set. Otherwise, there is no way to get the clipboard data and
135 * qemu_clipboard_request() cannot be called.
136 */
137 if (info->types[type].available && !info->types[type].data) {
138 assert(info->owner && info->owner->request);
139 }
140 }
141
142 if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) {
143 notifier_list_notify(&clipboard_notifiers, &notify);
144 }
145
146 if (cbinfo[info->selection] != info) {
147 qemu_clipboard_info_unref(cbinfo[info->selection]);
148 cbinfo[info->selection] = qemu_clipboard_info_ref(info);
149 }
150 }
151
152 QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection)
153 {
154 assert(selection < QEMU_CLIPBOARD_SELECTION__COUNT);
155
156 return cbinfo[selection];
157 }
158
159 QemuClipboardInfo *qemu_clipboard_info_new(QemuClipboardPeer *owner,
160 QemuClipboardSelection selection)
161 {
162 QemuClipboardInfo *info = g_new0(QemuClipboardInfo, 1);
163
164 info->owner = owner;
165 info->selection = selection;
166 info->refcount = 1;
167
168 return info;
169 }
170
171 QemuClipboardInfo *qemu_clipboard_info_ref(QemuClipboardInfo *info)
172 {
173 info->refcount++;
174 return info;
175 }
176
177 void qemu_clipboard_info_unref(QemuClipboardInfo *info)
178 {
179 uint32_t type;
180
181 if (!info) {
182 return;
183 }
184
185 info->refcount--;
186 if (info->refcount > 0) {
187 return;
188 }
189
190 for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) {
191 g_free(info->types[type].data);
192 }
193 g_free(info);
194 }
195
196 void qemu_clipboard_request(QemuClipboardInfo *info,
197 QemuClipboardType type)
198 {
199 if (info->types[type].data ||
200 info->types[type].requested ||
201 !info->types[type].available ||
202 !info->owner)
203 return;
204
205 assert(info->owner->request);
206
207 info->types[type].requested = true;
208 info->owner->request(info, type);
209 }
210
211 void qemu_clipboard_reset_serial(void)
212 {
213 QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_RESET_SERIAL };
214 int i;
215
216 trace_clipboard_reset_serial();
217
218 for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) {
219 QemuClipboardInfo *info = qemu_clipboard_info(i);
220 if (info) {
221 info->serial = 0;
222 }
223 }
224
225 if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) {
226 notifier_list_notify(&clipboard_notifiers, &notify);
227 } else {
228 cb_reset_serial_on_resume = true;
229 }
230 }
231
232 void qemu_clipboard_set_data(QemuClipboardPeer *peer,
233 QemuClipboardInfo *info,
234 QemuClipboardType type,
235 uint32_t size,
236 const void *data,
237 bool update)
238 {
239 if (!info ||
240 info->owner != peer) {
241 return;
242 }
243
244 g_free(info->types[type].data);
245 if (size) {
246 info->types[type].data = g_memdup2(data, size);
247 info->types[type].size = size;
248 info->types[type].available = true;
249 } else {
250 info->types[type].data = NULL;
251 info->types[type].size = 0;
252 info->types[type].available = false;
253 }
254
255 if (update) {
256 qemu_clipboard_update(info);
257 }
258 }